linux-arch.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 00/19] arch: Add basic LoongArch support
@ 2021-07-06  4:18 Huacai Chen
  2021-07-06  4:18 ` [PATCH 01/19] LoongArch: Add elf-related definitions Huacai Chen
                   ` (20 more replies)
  0 siblings, 21 replies; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen, Chen Zhu

LoongArch is a new RISC ISA, which is a bit like MIPS or RISC-V.
LoongArch includes a reduced 32-bit version (LA32R), a standard 32-bit
version (LA32S) and a 64-bit version (LA64). LoongArch use ACPI as its
boot protocol LoongArch-specific interrupt controllers (similar to APIC)
are already added in the next revision of ACPI Specification (current
revision is 6.4).

This patchset is adding basic LoongArch support in mainline kernel, we
can see a complete snapshot here:
https://github.com/loongson/linux/tree/loongarch-next

Cross-compile tool chain to build kernel:
https://github.com/loongson/build-tools/releases

Loongson and LoongArch documentations:
https://github.com/loongson/LoongArch-Documentation

LoongArch-specific interrupt controllers:
https://mantis.uefi.org/mantis/view.php?id=2203

Huacai Chen(19):
 LoongArch: Add elf-related definitions.
 LoongArch: Add writecombine support for drm.
 LoongArch: Add build infrastructure.
 LoongArch: Add common headers.
 LoongArch: Add boot and setup routines.
 LoongArch: Add exception/interrupt handling. 
 LoongArch: Add process management.
 LoongArch: Add memory management.
 LoongArch: Add system call support.
 LoongArch: Add signal handling support.
 LoongArch: Add elf and module support.
 LoongArch: Add misc common routines.
 LoongArch: Add some library functions.
 LoongArch: Add 64-bit Loongson platform.
 LoongArch: Add PCI controller support.
 LoongArch: Add VDSO and VSYSCALL support.
 LoongArch: Add multi-processor (SMP) support.
 LoongArch: Add Non-Uniform Memory Access (NUMA) support.
 LoongArch: Add Loongson-3 default config file.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
Signed-off-by: Chen Zhu <zhuchen@loongson.cn> 
---
 drivers/irqchip/Kconfig                |  37 +++-
 drivers/irqchip/Makefile               |   3 +
 drivers/irqchip/irq-loongarch-cpu.c    |  87 ++++++++++
 drivers/irqchip/irq-loongson-eiointc.c | 308 +++++++++++++++++++++++++++++++++
 drivers/irqchip/irq-loongson-htvec.c   | 102 ++++++++++-
 drivers/irqchip/irq-loongson-liointc.c | 140 ++++++++++++++-
 drivers/irqchip/irq-loongson-pch-lpc.c | 204 ++++++++++++++++++++++
 drivers/irqchip/irq-loongson-pch-msi.c |  69 +++++++-
 drivers/irqchip/irq-loongson-pch-pic.c | 139 ++++++++++++++-
 include/linux/cpuhotplug.h             |   1 +
 10 files changed, 1069 insertions(+), 21 deletions(-)
 create mode 100644 drivers/irqchip/irq-loongarch-cpu.c
 create mode 100644 drivers/irqchip/irq-loongson-eiointc.c
 create mode 100644 drivers/irqchip/irq-loongson-pch-lpc.c
--
2.27.0


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

* [PATCH 01/19] LoongArch: Add elf-related definitions
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06  4:18 ` [PATCH 02/19] LoongArch: Add writecombine support for drm Huacai Chen
                   ` (19 subsequent siblings)
  20 siblings, 0 replies; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

Add elf-related definitions for LoongArch, including: EM_LOONGARCH,
KEXEC_ARCH_LOONGARCH, AUDIT_ARCH_LOONGARCH32, AUDIT_ARCH_LOONGARCH64
and NT_LOONGARCH_*.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 include/uapi/linux/audit.h  | 2 ++
 include/uapi/linux/elf-em.h | 1 +
 include/uapi/linux/elf.h    | 4 ++++
 include/uapi/linux/kexec.h  | 1 +
 scripts/sorttable.c         | 5 +++++
 5 files changed, 13 insertions(+)

diff --git a/include/uapi/linux/audit.h b/include/uapi/linux/audit.h
index daa481729e9b..e6023f00eaf9 100644
--- a/include/uapi/linux/audit.h
+++ b/include/uapi/linux/audit.h
@@ -434,6 +434,8 @@ enum {
 #define AUDIT_ARCH_UNICORE	(EM_UNICORE|__AUDIT_ARCH_LE)
 #define AUDIT_ARCH_X86_64	(EM_X86_64|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
 #define AUDIT_ARCH_XTENSA	(EM_XTENSA)
+#define AUDIT_ARCH_LOONGARCH32	(EM_LOONGARCH|__AUDIT_ARCH_LE)
+#define AUDIT_ARCH_LOONGARCH64	(EM_LOONGARCH|__AUDIT_ARCH_64BIT|__AUDIT_ARCH_LE)
 
 #define AUDIT_PERM_EXEC		1
 #define AUDIT_PERM_WRITE	2
diff --git a/include/uapi/linux/elf-em.h b/include/uapi/linux/elf-em.h
index f47e853546fa..ef38c2bc5ab7 100644
--- a/include/uapi/linux/elf-em.h
+++ b/include/uapi/linux/elf-em.h
@@ -51,6 +51,7 @@
 #define EM_RISCV	243	/* RISC-V */
 #define EM_BPF		247	/* Linux BPF - in-kernel virtual machine */
 #define EM_CSKY		252	/* C-SKY */
+#define EM_LOONGARCH	258	/* LoongArch */
 #define EM_FRV		0x5441	/* Fujitsu FR-V */
 
 /*
diff --git a/include/uapi/linux/elf.h b/include/uapi/linux/elf.h
index 61bf4774b8f2..592385c55af3 100644
--- a/include/uapi/linux/elf.h
+++ b/include/uapi/linux/elf.h
@@ -432,6 +432,10 @@ typedef struct elf64_shdr {
 #define NT_MIPS_DSP	0x800		/* MIPS DSP ASE registers */
 #define NT_MIPS_FP_MODE	0x801		/* MIPS floating-point mode */
 #define NT_MIPS_MSA	0x802		/* MIPS SIMD registers */
+#define NT_LOONGARCH_CPUCFG	0x900	/* LoongArch CPU config registers */
+#define NT_LOONGARCH_LBT	0x901	/* LoongArch Loongson Binary Translation registers */
+#define NT_LOONGARCH_LSX	0x902	/* LoongArch Loongson SIMD Extension registers */
+#define NT_LOONGARCH_LASX	0x903	/* LoongArch Loongson Advanced SIMD Extension registers */
 
 /* Note types with note name "GNU" */
 #define NT_GNU_PROPERTY_TYPE_0	5
diff --git a/include/uapi/linux/kexec.h b/include/uapi/linux/kexec.h
index 778dc191c265..5ef2df004565 100644
--- a/include/uapi/linux/kexec.h
+++ b/include/uapi/linux/kexec.h
@@ -43,6 +43,7 @@
 #define KEXEC_ARCH_MIPS    ( 8 << 16)
 #define KEXEC_ARCH_AARCH64 (183 << 16)
 #define KEXEC_ARCH_RISCV   (243 << 16)
+#define KEXEC_ARCH_LOONGARCH	(258 << 16)
 
 /* The artificial cap on the number of segments passed to kexec_load. */
 #define KEXEC_SEGMENT_MAX 16
diff --git a/scripts/sorttable.c b/scripts/sorttable.c
index 0ef3abfc4a51..3823079cf692 100644
--- a/scripts/sorttable.c
+++ b/scripts/sorttable.c
@@ -54,6 +54,10 @@
 #define EM_ARCV2	195
 #endif
 
+#ifndef EM_LOONGARCH
+#define EM_LOONGARCH	258
+#endif
+
 static uint32_t (*r)(const uint32_t *);
 static uint16_t (*r2)(const uint16_t *);
 static uint64_t (*r8)(const uint64_t *);
@@ -347,6 +351,7 @@ static int do_file(char const *const fname, void *addr)
 	case EM_ARCOMPACT:
 	case EM_ARCV2:
 	case EM_ARM:
+	case EM_LOONGARCH:
 	case EM_MICROBLAZE:
 	case EM_MIPS:
 	case EM_XTENSA:
-- 
2.27.0


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

* [PATCH 02/19] LoongArch: Add writecombine support for drm
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
  2021-07-06  4:18 ` [PATCH 01/19] LoongArch: Add elf-related definitions Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06  4:18 ` [PATCH 03/19] LoongArch: Add build infrastructure Huacai Chen
                   ` (18 subsequent siblings)
  20 siblings, 0 replies; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

LoongArch maintains cache coherency in hardware, but its WUC attribute
(Weak-ordered UnCached, which is similar to WC) is out of the scope of
cache coherency machanism. This means WUC can only used for write-only
memory regions.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 drivers/gpu/drm/drm_vm.c         | 2 +-
 drivers/gpu/drm/ttm/ttm_module.c | 2 +-
 include/drm/drm_cache.h          | 8 ++++++++
 3 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/drm_vm.c b/drivers/gpu/drm/drm_vm.c
index e957d4851dc0..f024dc93939e 100644
--- a/drivers/gpu/drm/drm_vm.c
+++ b/drivers/gpu/drm/drm_vm.c
@@ -69,7 +69,7 @@ static pgprot_t drm_io_prot(struct drm_local_map *map,
 	pgprot_t tmp = vm_get_page_prot(vma->vm_flags);
 
 #if defined(__i386__) || defined(__x86_64__) || defined(__powerpc__) || \
-    defined(__mips__)
+    defined(__mips__) || defined(__loongarch__)
 	if (map->type == _DRM_REGISTERS && !(map->flags & _DRM_WRITE_COMBINING))
 		tmp = pgprot_noncached(tmp);
 	else
diff --git a/drivers/gpu/drm/ttm/ttm_module.c b/drivers/gpu/drm/ttm/ttm_module.c
index 997c458f68a9..8fbff28c85f3 100644
--- a/drivers/gpu/drm/ttm/ttm_module.c
+++ b/drivers/gpu/drm/ttm/ttm_module.c
@@ -60,7 +60,7 @@ pgprot_t ttm_prot_from_caching(enum ttm_caching caching, pgprot_t tmp)
 		tmp = pgprot_noncached(tmp);
 #endif
 #if defined(__ia64__) || defined(__arm__) || defined(__aarch64__) || \
-	defined(__powerpc__) || defined(__mips__)
+	defined(__powerpc__) || defined(__mips__) || defined(__loongarch__)
 	if (caching == ttm_write_combined)
 		tmp = pgprot_writecombine(tmp);
 	else
diff --git a/include/drm/drm_cache.h b/include/drm/drm_cache.h
index cc9de1632dd3..5c1059435369 100644
--- a/include/drm/drm_cache.h
+++ b/include/drm/drm_cache.h
@@ -67,6 +67,14 @@ static inline bool drm_arch_can_wc_memory(void)
 	 * optimization entirely for ARM and arm64.
 	 */
 	return false;
+#elif defined(CONFIG_LOONGARCH)
+	/*
+	 * LoongArch maintains cache coherency in hardware, but its WUC attribute
+	 * (Weak-ordered UnCached, which is similar to WC) is out of the scope of
+	 * cache coherency machanism. This means WUC can only used for write-only
+	 * memory regions.
+	 */
+	return false;
 #else
 	return true;
 #endif
-- 
2.27.0


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

* [PATCH 03/19] LoongArch: Add build infrastructure
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
  2021-07-06  4:18 ` [PATCH 01/19] LoongArch: Add elf-related definitions Huacai Chen
  2021-07-06  4:18 ` [PATCH 02/19] LoongArch: Add writecombine support for drm Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06 10:12   ` Arnd Bergmann
                     ` (2 more replies)
  2021-07-06  4:18 ` [PATCH 05/19] LoongArch: Add boot and setup routines Huacai Chen
                   ` (17 subsequent siblings)
  20 siblings, 3 replies; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

This patch adds Kbuild, Makefile, Kconfig and link script for LoongArch
build infrastructure.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 arch/loongarch/.gitignore              |  11 +
 arch/loongarch/Kbuild                  |  21 ++
 arch/loongarch/Kbuild.platforms        |   6 +
 arch/loongarch/Kconfig                 | 403 +++++++++++++++++++++++++
 arch/loongarch/Kconfig.debug           |   0
 arch/loongarch/Makefile                | 121 ++++++++
 arch/loongarch/include/asm/Kbuild      |  30 ++
 arch/loongarch/include/uapi/asm/Kbuild |   2 +
 arch/loongarch/kernel/Makefile         |  24 ++
 arch/loongarch/kernel/vmlinux.lds.S    | 112 +++++++
 arch/loongarch/lib/Makefile            |   7 +
 arch/loongarch/loongson64/Makefile     |   8 +
 arch/loongarch/loongson64/Platform     |  14 +
 arch/loongarch/mm/Makefile             |  10 +
 arch/loongarch/pci/Makefile            |   9 +
 scripts/subarch.include                |   2 +-
 16 files changed, 779 insertions(+), 1 deletion(-)
 create mode 100644 arch/loongarch/.gitignore
 create mode 100644 arch/loongarch/Kbuild
 create mode 100644 arch/loongarch/Kbuild.platforms
 create mode 100644 arch/loongarch/Kconfig
 create mode 100644 arch/loongarch/Kconfig.debug
 create mode 100644 arch/loongarch/Makefile
 create mode 100644 arch/loongarch/include/asm/Kbuild
 create mode 100644 arch/loongarch/include/uapi/asm/Kbuild
 create mode 100644 arch/loongarch/kernel/Makefile
 create mode 100644 arch/loongarch/kernel/vmlinux.lds.S
 create mode 100644 arch/loongarch/lib/Makefile
 create mode 100644 arch/loongarch/loongson64/Makefile
 create mode 100644 arch/loongarch/loongson64/Platform
 create mode 100644 arch/loongarch/mm/Makefile
 create mode 100644 arch/loongarch/pci/Makefile

diff --git a/arch/loongarch/.gitignore b/arch/loongarch/.gitignore
new file mode 100644
index 000000000000..7e6b000a7f3e
--- /dev/null
+++ b/arch/loongarch/.gitignore
@@ -0,0 +1,11 @@
+*.lds
+*.raw
+calc_vmlinuz_load_addr
+elf-entry
+relocs
+vmlinux.*
+vmlinuz.*
+vdso/genvdso
+vdso/vdso-image.c
+
+!kernel/vmlinux.lds.S
diff --git a/arch/loongarch/Kbuild b/arch/loongarch/Kbuild
new file mode 100644
index 000000000000..e997ba117bd9
--- /dev/null
+++ b/arch/loongarch/Kbuild
@@ -0,0 +1,21 @@
+# Fail on warnings - also for files referenced in subdirs
+# -Werror can be disabled for specific files using:
+# CFLAGS_<file.o> := -Wno-error
+ifeq ($(W),)
+subdir-ccflags-y := -Werror
+endif
+
+# platform specific definitions
+include arch/loongarch/Kbuild.platforms
+obj-y := $(platform-y)
+
+# make clean traverses $(obj-) without having included .config, so
+# everything ends up here
+obj- := $(platform-)
+
+# LoongArch object files
+# The object files are linked as core-y files would be linked
+
+obj-y += kernel/
+obj-y += mm/
+obj-y += vdso/
diff --git a/arch/loongarch/Kbuild.platforms b/arch/loongarch/Kbuild.platforms
new file mode 100644
index 000000000000..ad390d5c00f2
--- /dev/null
+++ b/arch/loongarch/Kbuild.platforms
@@ -0,0 +1,6 @@
+# All platforms listed in alphabetic order
+
+platforms += loongson64
+
+# include the platform specific files
+include $(patsubst %, $(srctree)/arch/loongarch/%/Platform, $(platforms))
diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
new file mode 100644
index 000000000000..7d5889a264c6
--- /dev/null
+++ b/arch/loongarch/Kconfig
@@ -0,0 +1,403 @@
+# SPDX-License-Identifier: GPL-2.0
+config LOONGARCH
+	bool
+	default y
+	select ACPI_SYSTEM_POWER_STATES_SUPPORT	if ACPI
+	select ARCH_BINFMT_ELF_STATE
+	select ARCH_DISCARD_MEMBLOCK
+	select ARCH_HAS_ACPI_TABLE_UPGRADE	if ACPI
+	select ARCH_HAS_ELF_RANDOMIZE
+	select ARCH_HAS_PTE_SPECIAL if !32BIT
+	select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
+	select ARCH_INLINE_READ_LOCK if !PREEMPTION
+	select ARCH_INLINE_READ_LOCK_BH if !PREEMPTION
+	select ARCH_INLINE_READ_LOCK_IRQ if !PREEMPTION
+	select ARCH_INLINE_READ_LOCK_IRQSAVE if !PREEMPTION
+	select ARCH_INLINE_READ_UNLOCK if !PREEMPTION
+	select ARCH_INLINE_READ_UNLOCK_BH if !PREEMPTION
+	select ARCH_INLINE_READ_UNLOCK_IRQ if !PREEMPTION
+	select ARCH_INLINE_READ_UNLOCK_IRQRESTORE if !PREEMPTION
+	select ARCH_INLINE_WRITE_LOCK if !PREEMPTION
+	select ARCH_INLINE_WRITE_LOCK_BH if !PREEMPTION
+	select ARCH_INLINE_WRITE_LOCK_IRQ if !PREEMPTION
+	select ARCH_INLINE_WRITE_LOCK_IRQSAVE if !PREEMPTION
+	select ARCH_INLINE_WRITE_UNLOCK if !PREEMPTION
+	select ARCH_INLINE_WRITE_UNLOCK_BH if !PREEMPTION
+	select ARCH_INLINE_WRITE_UNLOCK_IRQ if !PREEMPTION
+	select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE if !PREEMPTION
+	select ARCH_INLINE_SPIN_TRYLOCK if !PREEMPTION
+	select ARCH_INLINE_SPIN_TRYLOCK_BH if !PREEMPTION
+	select ARCH_INLINE_SPIN_LOCK if !PREEMPTION
+	select ARCH_INLINE_SPIN_LOCK_BH if !PREEMPTION
+	select ARCH_INLINE_SPIN_LOCK_IRQ if !PREEMPTION
+	select ARCH_INLINE_SPIN_LOCK_IRQSAVE if !PREEMPTION
+	select ARCH_INLINE_SPIN_UNLOCK if !PREEMPTION
+	select ARCH_INLINE_SPIN_UNLOCK_BH if !PREEMPTION
+	select ARCH_INLINE_SPIN_UNLOCK_IRQ if !PREEMPTION
+	select ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE if !PREEMPTION
+	select ARCH_SUPPORTS_ACPI
+	select ARCH_SUPPORTS_HUGETLBFS
+	select ARCH_USE_BUILTIN_BSWAP
+	select ARCH_USE_CMPXCHG_LOCKREF if 64BIT
+	select ARCH_USE_QUEUED_RWLOCKS
+	select ARCH_USE_QUEUED_SPINLOCKS
+	select BUILDTIME_TABLE_SORT
+	select GENERIC_ATOMIC64 if !64BIT
+	select GENERIC_CLOCKEVENTS
+	select GENERIC_CMOS_UPDATE
+	select GENERIC_CPU_AUTOPROBE
+	select GENERIC_GETTIMEOFDAY
+	select GENERIC_IOMAP
+	select GENERIC_IRQ_PROBE
+	select GENERIC_IRQ_SHOW
+	select GENERIC_LIB_ASHLDI3
+	select GENERIC_LIB_ASHRDI3
+	select GENERIC_LIB_CMPDI2
+	select GENERIC_LIB_LSHRDI3
+	select GENERIC_LIB_UCMPDI2
+	select GENERIC_TIME_VSYSCALL
+	select HANDLE_DOMAIN_IRQ
+	select HAVE_ARCH_AUDITSYSCALL
+	select HAVE_ARCH_COMPILER_H
+	select HAVE_ARCH_MMAP_RND_BITS if MMU
+	select HAVE_ARCH_SECCOMP_FILTER
+	select HAVE_ARCH_TRACEHOOK
+	select HAVE_ARCH_TRANSPARENT_HUGEPAGE
+	select HAVE_ASM_MODVERSIONS
+	select HAVE_CBPF_JIT if !64BIT
+	select HAVE_EBPF_JIT if 64BIT
+	select HAVE_CONTEXT_TRACKING
+	select HAVE_COPY_THREAD_TLS
+	select HAVE_DEBUG_KMEMLEAK
+	select HAVE_DEBUG_STACKOVERFLOW
+	select HAVE_DMA_CONTIGUOUS
+	select HAVE_EXIT_THREAD
+	select HAVE_FAST_GUP
+	select HAVE_FUTEX_CMPXCHG if FUTEX
+	select HAVE_GENERIC_VDSO
+	select HAVE_IDE
+	select HAVE_IOREMAP_PROT
+	select HAVE_IRQ_EXIT_ON_IRQ_STACK
+	select HAVE_IRQ_TIME_ACCOUNTING
+	select HAVE_MEMBLOCK
+	select HAVE_MEMBLOCK_NODE_MAP
+	select HAVE_MOD_ARCH_SPECIFIC
+	select HAVE_NMI
+	select HAVE_PERF_EVENTS
+	select HAVE_REGS_AND_STACK_ACCESS_API
+	select HAVE_RSEQ
+	select HAVE_SYSCALL_TRACEPOINTS
+	select HAVE_VIRT_CPU_ACCOUNTING_GEN if 64BIT
+	select IRQ_FORCED_THREADING
+	select MODULES_USE_ELF_RELA if MODULES && 64BIT
+	select MODULES_USE_ELF_REL if MODULES
+	select PCI_DOMAINS if PCI
+	select PCI_MSI_ARCH_FALLBACKS
+	select PERF_USE_VMALLOC
+	select RTC_LIB
+	select SYSCTL_EXCEPTION_TRACE
+	select VIRT_TO_BUS
+
+menu "Machine selection"
+
+choice
+	prompt "System type"
+	default MACH_LOONGSON64
+
+config MACH_LOONGSON64
+	bool "Loongson 64-bit family of machines"
+	select ARCH_SPARSEMEM_ENABLE
+	select ARCH_MIGHT_HAVE_PC_PARPORT
+	select ARCH_MIGHT_HAVE_PC_SERIO
+	select GENERIC_ISA_DMA
+	select HAVE_PCI
+	select ISA
+	select PCI
+	select IRQ_LOONGARCH_CPU
+	select NR_CPUS_DEFAULT_4
+	select SPARSE_IRQ
+	select SYS_HAS_CPU_LOONGSON64
+	select SYS_SUPPORTS_64BIT_KERNEL
+	select ZONE_DMA32
+	help
+	  This enables the support of Loongson 64-bit family of machines. These
+	  machines are based on new Loongson-3 processors (Old Loongson is MIPS
+	  compatible, while new Loongson is based on LoongArch ISA).
+
+endchoice
+
+endmenu
+
+config GENERIC_HWEIGHT
+	bool
+	default y
+
+config GENERIC_CALIBRATE_DELAY
+	bool
+	default y
+
+config SCHED_OMIT_FRAME_POINTER
+	bool
+	default y
+
+config GENERIC_CSUM
+	def_bool y
+
+config GENERIC_ISA_DMA
+	bool
+
+config L1_CACHE_SHIFT
+	int
+	default "6"
+
+menu "CPU selection"
+
+choice
+	prompt "CPU type"
+	default CPU_LOONGSON64
+
+config CPU_LOONGSON64
+	bool "Loongson 64-bit CPU"
+	depends on SYS_HAS_CPU_LOONGSON64
+	select CPU_SUPPORTS_64BIT_KERNEL
+	select GPIOLIB
+	select SWIOTLB
+	select ARCH_SUPPORTS_ATOMIC_RMW
+	help
+	  The Loongson 64-bit processor implements the LoongArch64 (the 64-bit
+	  version of LoongArch) instruction set.
+
+endchoice
+
+config SYS_HAS_CPU_LOONGSON64
+	bool
+
+endmenu
+
+config SYS_SUPPORTS_32BIT_KERNEL
+	bool
+config SYS_SUPPORTS_64BIT_KERNEL
+	bool
+config CPU_SUPPORTS_32BIT_KERNEL
+	bool
+config CPU_SUPPORTS_64BIT_KERNEL
+	bool
+
+menu "Kernel type"
+
+choice
+	prompt "Kernel code model"
+	help
+	  You should only select this option if you have a workload that
+	  actually benefits from 64-bit processing or if your machine has
+	  large memory.  You will only be presented a single option in this
+	  menu if your system does not support both 32-bit and 64-bit kernels.
+
+config 32BIT
+	bool "32-bit kernel"
+	depends on CPU_SUPPORTS_32BIT_KERNEL && SYS_SUPPORTS_32BIT_KERNEL
+	help
+	  Select this option if you want to build a 32-bit kernel.
+
+config 64BIT
+	bool "64-bit kernel"
+	depends on CPU_SUPPORTS_64BIT_KERNEL && SYS_SUPPORTS_64BIT_KERNEL
+	help
+	  Select this option if you want to build a 64-bit kernel.
+
+endchoice
+
+choice
+	prompt "Kernel page size"
+	default PAGE_SIZE_16KB
+
+config PAGE_SIZE_4KB
+	bool "4kB"
+	help
+	  This option select the standard 4kB Linux page size.
+
+config PAGE_SIZE_16KB
+	bool "16kB"
+	help
+	  This option select the standard 16kB Linux page size.
+
+config PAGE_SIZE_64KB
+	bool "64kB"
+	help
+	  This option select the standard 64kB Linux page size.
+
+endchoice
+
+choice
+	prompt "Virtual memory address space bits"
+	default VA_BITS_40
+	help
+	  Allows choosing one of multiple possible virtual memory
+	  address space bits for applications. The level of page
+	  translation table is determined by a combination of page
+	  size and virtual memory address space bits.
+
+config VA_BITS_40
+	bool "40-bits"
+	depends on 64BIT
+	help
+	  Support a maximum at least 40 bits of application virtual memory.
+
+config VA_BITS_48
+	bool "48-bits"
+	depends on 64BIT
+	help
+	  Support a maximum at least 48 bits of application virtual memory.
+
+endchoice
+
+config FORCE_MAX_ZONEORDER
+	int "Maximum zone order"
+	range 14 64 if PAGE_SIZE_64KB
+	default "14" if PAGE_SIZE_64KB
+	range 12 64 if PAGE_SIZE_16KB
+	default "12" if PAGE_SIZE_16KB
+	range 11 64
+	default "11"
+	help
+	  The kernel memory allocator divides physically contiguous memory
+	  blocks into "zones", where each zone is a power of two number of
+	  pages.  This option selects the largest power of two that the kernel
+	  keeps in the memory allocator.  If you need to allocate very large
+	  blocks of physically contiguous memory, then you may need to
+	  increase this value.
+
+	  This config option is actually maximum order plus one. For example,
+	  a value of 11 means that the largest free memory block is 2^10 pages.
+
+	  The page size is not necessarily 4KB.  Keep this in mind
+	  when choosing a value for this option.
+
+config CPU_HAS_PREFETCH
+	bool
+	default y
+
+config CPU_HAS_FPU
+	bool
+	default y
+
+config ARCH_SELECT_MEMORY_MODEL
+	def_bool y
+
+config ARCH_FLATMEM_ENABLE
+	def_bool y
+
+config ARCH_SPARSEMEM_ENABLE
+	def_bool y
+	select SPARSEMEM_STATIC
+	help
+	  Say Y to support efficient handling of sparse physical memory,
+	  for architectures which are either NUMA (Non-Uniform Memory Access)
+	  or have huge holes in the physical address space for other reasons.
+	  See <file:Documentation/vm/numa.rst> for more.
+
+config DMI
+	bool "Enable DMI scanning"
+	select DMI_SCAN_MACHINE_NON_EFI_FALLBACK
+	default y
+	help
+	  Enabled scanning of DMI to identify machine quirks. Say Y
+	  here unless you have verified that your setup is not
+	  affected by entries in the DMI blacklist. Required by PNP
+	  BIOS code.
+
+config EFI
+	bool "EFI runtime service support"
+	select UCS2_STRING
+	select EFI_RUNTIME_WRAPPERS
+	help
+	  This enables the kernel to use EFI runtime services that are
+	  available (such as the EFI variable services).
+
+	  This option is only useful on systems that have EFI firmware.
+	  In addition, you should use the latest ELILO loader available
+	  at <http://elilo.sourceforge.net> in order to take advantage
+	  of EFI runtime services. However, even with this option, the
+	  resultant kernel should continue to boot on existing non-EFI
+	  platforms.
+
+source "kernel/Kconfig.hz"
+
+config SECCOMP
+	bool "Enable seccomp to safely compute untrusted bytecode"
+	depends on PROC_FS
+	default y
+	help
+	  This kernel feature is useful for number crunching applications
+	  that may need to compute untrusted bytecode during their
+	  execution. By using pipes or other transports made available to
+	  the process as file descriptors supporting the read/write
+	  syscalls, it's possible to isolate those applications in
+	  their own address space using seccomp. Once seccomp is
+	  enabled via /proc/<pid>/seccomp, it cannot be disabled
+	  and the task is only allowed to execute a few safe syscalls
+	  defined by each seccomp mode.
+
+	  If unsure, say Y. Only embedded should say N here.
+
+endmenu
+
+config ARCH_ENABLE_MEMORY_HOTPLUG
+	def_bool y
+	depends on LOONGARCH
+
+config ARCH_ENABLE_MEMORY_HOTREMOVE
+	def_bool y
+	depends on MEMORY_HOTPLUG
+
+config ARCH_MEMORY_PROBE
+	def_bool y
+	depends on MEMORY_HOTPLUG
+
+config LOCKDEP_SUPPORT
+	bool
+	default y
+
+config PGTABLE_LEVELS
+	int
+	default 4 if PAGE_SIZE_4KB && VA_BITS_48
+	default 2 if PAGE_SIZE_64KB && VA_BITS_40
+	default 3
+
+config MMU
+	bool
+	default y
+
+config ARCH_MMAP_RND_BITS_MIN
+	default 12 if 64BIT
+	default 8
+
+config ARCH_MMAP_RND_BITS_MAX
+	default 18 if 64BIT
+	default 15
+
+config ZONE_DMA
+	bool
+
+config ZONE_DMA32
+	bool
+
+menu "Bus options"
+
+config ISA
+	bool
+
+config PCI_MMCONFIG
+	bool
+	default y
+	depends on PCI && ACPI
+
+endmenu
+
+menu "Power management options"
+
+source "drivers/acpi/Kconfig"
+
+endmenu
+
+source "drivers/firmware/Kconfig"
diff --git a/arch/loongarch/Kconfig.debug b/arch/loongarch/Kconfig.debug
new file mode 100644
index 000000000000..e69de29bb2d1
diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile
new file mode 100644
index 000000000000..d339049e9022
--- /dev/null
+++ b/arch/loongarch/Makefile
@@ -0,0 +1,121 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Author: Huacai Chen <chenhuacai@loongson.cn>
+# Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+
+#
+# Select the object file format to substitute into the linker script.
+#
+64bit-tool-archpref	= loongarch64
+32bit-bfd		= elf32-loongarch
+64bit-bfd		= elf64-loongarch
+32bit-emul		= elf32loongarch
+64bit-emul		= elf64loongarch
+
+ifdef CONFIG_64BIT
+tool-archpref		= $(64bit-tool-archpref)
+UTS_MACHINE		:= loongarch64
+endif
+
+ifneq ($(SUBARCH),$(ARCH))
+  ifeq ($(CROSS_COMPILE),)
+    CROSS_COMPILE := $(call cc-cross-prefix, $(tool-archpref)-linux-  $(tool-archpref)-linux-gnu-  $(tool-archpref)-unknown-linux-gnu-)
+  endif
+endif
+
+cflags-y += $(call cc-option, -mno-check-zero-division)
+
+ifdef CONFIG_64BIT
+ld-emul			= $(64bit-emul)
+cflags-y		+= -mabi=lp64
+endif
+
+all-y			:= vmlinux
+
+#
+# GCC uses -G 0 -mabicalls -fpic as default.  We don't want PIC in the kernel
+# code since it only slows down the whole thing.  At some point we might make
+# use of global pointer optimizations but their use of $r2 conflicts with
+# the current pointer optimization.
+#
+cflags-y			+= -G 0 -pipe
+cflags-y			+= -msoft-float
+LDFLAGS_vmlinux			+= -G 0 -static -n -nostdlib
+KBUILD_AFLAGS_KERNEL		+= -Wa,-mla-global-with-pcrel
+KBUILD_CFLAGS_KERNEL		+= -Wa,-mla-global-with-pcrel
+KBUILD_AFLAGS_MODULE		+= -Wa,-mla-global-with-abs
+KBUILD_CFLAGS_MODULE		+= -fno-plt -Wa,-mla-global-with-abs,-mla-local-with-abs
+
+cflags-y += -ffreestanding
+
+# Some distribution-specific toolchains might pass the -fstack-check
+# option during the build, which adds a simple stack-probe at the beginning
+# of every function.  This stack probe is to ensure that there is enough
+# stack space, else a SEGV is generated.  This is not desirable for LoongArch
+# as kernel stacks are small, placed in unmapped virtual memory, and do not
+# grow when overflowed.
+#
+cflags-y += -fno-stack-check
+
+#
+# Board-dependent options and extra files
+#
+include arch/loongarch/Kbuild.platforms
+
+ifdef CONFIG_PHYSICAL_START
+load-y				= $(CONFIG_PHYSICAL_START)
+endif
+
+drivers-$(CONFIG_PCI)		+= arch/loongarch/pci/
+
+KBUILD_AFLAGS	+= $(cflags-y)
+KBUILD_CFLAGS	+= $(cflags-y)
+KBUILD_CPPFLAGS += -DVMLINUX_LOAD_ADDRESS=$(load-y)
+
+bootvars-y	= VMLINUX_LOAD_ADDRESS=$(load-y) PLATFORM="$(platform-y)"
+
+ifdef CONFIG_64BIT
+bootvars-y	+= ADDR_BITS=64
+endif
+
+# This is required to get dwarf unwinding tables into .debug_frame
+# instead of .eh_frame so we don't discard them.
+KBUILD_CFLAGS += -fno-asynchronous-unwind-tables
+
+KBUILD_LDFLAGS	+= -m $(ld-emul)
+
+ifdef CONFIG_LOONGARCH
+CHECKFLAGS += $(shell $(CC) $(KBUILD_CFLAGS) -dM -E -x c /dev/null | \
+	egrep -vw '__GNUC_(|MINOR_|PATCHLEVEL_)_' | \
+	sed -e "s/^\#define /-D'/" -e "s/ /'='/" -e "s/$$/'/" -e 's/\$$/&&/g')
+endif
+
+head-y := arch/loongarch/kernel/head.o
+
+libs-y += arch/loongarch/lib/
+
+# See arch/loongarch/Kbuild for content of core part of the kernel
+core-y += arch/loongarch/
+
+# boot image targets (arch/loongarch/boot/)
+boot-y			:= vmlinux.bin
+
+all:	$(all-y)
+
+# boot
+$(boot-y): vmlinux FORCE
+	$(Q)$(MAKE) $(build)=arch/loongarch/boot VMLINUX=vmlinux \
+		$(bootvars-y) arch/loongarch/boot/$@
+
+CLEAN_FILES += vmlinux
+
+install:
+	$(Q)install -D -m 755 vmlinux $(INSTALL_PATH)/vmlinux-$(KERNELRELEASE)
+	$(Q)install -D -m 644 .config $(INSTALL_PATH)/config-$(KERNELRELEASE)
+	$(Q)install -D -m 644 System.map $(INSTALL_PATH)/System.map-$(KERNELRELEASE)
+
+define archhelp
+	echo '  install              - install kernel into $(INSTALL_PATH)'
+	echo '  vmlinux.bin          - Raw binary boot image'
+	echo
+endef
diff --git a/arch/loongarch/include/asm/Kbuild b/arch/loongarch/include/asm/Kbuild
new file mode 100644
index 000000000000..10d6c39eb8b1
--- /dev/null
+++ b/arch/loongarch/include/asm/Kbuild
@@ -0,0 +1,30 @@
+# SPDX-License-Identifier: GPL-2.0
+generic-y += dma-contiguous.h
+generic-y += export.h
+generic-y += mcs_spinlock.h
+generic-y += parport.h
+generic-y += early_ioremap.h
+generic-y += qrwlock.h
+generic-y += qspinlock.h
+generic-y += rwsem.h
+generic-y += segment.h
+generic-y += user.h
+generic-y += rwsem.h
+generic-y += stat.h
+generic-y += fcntl.h
+generic-y += ioctl.h
+generic-y += ioctls.h
+generic-y += mman.h
+generic-y += msgbuf.h
+generic-y += sembuf.h
+generic-y += shmbuf.h
+generic-y += statfs.h
+generic-y += socket.h
+generic-y += sockios.h
+generic-y += termios.h
+generic-y += termbits.h
+generic-y += poll.h
+generic-y += param.h
+generic-y += posix_types.h
+generic-y += resource.h
+generic-y += kvm_para.h
diff --git a/arch/loongarch/include/uapi/asm/Kbuild b/arch/loongarch/include/uapi/asm/Kbuild
new file mode 100644
index 000000000000..4aa680ca2e5f
--- /dev/null
+++ b/arch/loongarch/include/uapi/asm/Kbuild
@@ -0,0 +1,2 @@
+# SPDX-License-Identifier: GPL-2.0
+generic-y += kvm_para.h
diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile
new file mode 100644
index 000000000000..e37f8016d93c
--- /dev/null
+++ b/arch/loongarch/kernel/Makefile
@@ -0,0 +1,24 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Linux/LoongArch kernel.
+#
+
+extra-y		:= head.o vmlinux.lds
+
+obj-y		+= cmpxchg.o cpu-probe.o elf.o entry.o genex.o idle.o irq.o \
+		   process.o ptrace.o reset.o setup.o signal.o io.o \
+		   syscall.o time.o topology.o traps.o unaligned.o \
+		   cmdline.o switch.o cacheinfo.o vdso.o
+
+obj-$(CONFIG_ACPI)		+= acpi.o
+obj-$(CONFIG_EFI) 		+= efi.o
+
+obj-$(CONFIG_MODULES)		+= module.o
+
+obj-$(CONFIG_CPU_HAS_FPU)	+= fpu.o
+
+obj-$(CONFIG_64BIT)		+= scall64.o
+
+obj-$(CONFIG_PROC_FS)		+= proc.o
+
+CPPFLAGS_vmlinux.lds		:= $(KBUILD_CFLAGS)
diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S
new file mode 100644
index 000000000000..3ae212100de5
--- /dev/null
+++ b/arch/loongarch/kernel/vmlinux.lds.S
@@ -0,0 +1,112 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#include <asm/asm-offsets.h>
+#include <asm/thread_info.h>
+
+#define PAGE_SIZE _PAGE_SIZE
+
+/*
+ * Put .bss..swapper_pg_dir as the first thing in .bss. This will
+ * ensure that it has .bss alignment (64K).
+ */
+#define BSS_FIRST_SECTIONS *(.bss..swapper_pg_dir)
+
+#include <asm-generic/vmlinux.lds.h>
+
+OUTPUT_ARCH(loongarch)
+ENTRY(kernel_entry)
+PHDRS {
+	text PT_LOAD FLAGS(7);	/* RWX */
+	note PT_NOTE FLAGS(4);	/* R__ */
+}
+
+jiffies	 = jiffies_64;
+
+SECTIONS
+{
+	. = VMLINUX_LOAD_ADDRESS;
+	/* Read-only */
+	_text = .;	/* Text and read-only data */
+	.text : {
+		TEXT_TEXT
+		SCHED_TEXT
+		CPUIDLE_TEXT
+		LOCK_TEXT
+		KPROBES_TEXT
+		IRQENTRY_TEXT
+		SOFTIRQENTRY_TEXT
+		*(.fixup)
+		*(.gnu.warning)
+	} :text = 0
+	_etext = .;	/* End of text section */
+
+	EXCEPTION_TABLE(16)
+
+	_sdata = .;			/* Start of data section */
+	RO_DATA(4096)
+	RW_DATA(1 << CONFIG_L1_CACHE_SHIFT, PAGE_SIZE, THREAD_SIZE)
+
+	/* We want the small data sections together, so single-instruction offsets
+	   can access them all, and initialized data all before uninitialized, so
+	   we can shorten the on-disk segment size.  */
+	.sdata : {
+		*(.sdata)
+	}
+	_edata =  .;			/* End of data section */
+
+	/* Will be freed after init */
+	. = ALIGN(PAGE_SIZE);		/* Init code and data */
+	__init_begin = .;
+	INIT_TEXT_SECTION(PAGE_SIZE)
+	INIT_DATA_SECTION(16)
+
+	. = ALIGN(4);
+
+	/* .exit.text is discarded at runtime, not link time, to deal with
+	 * references from .rodata
+	 */
+	.exit.text : {
+		EXIT_TEXT
+	}
+	.exit.data : {
+		EXIT_DATA
+	}
+
+	/*
+	 * Align to 64K in attempt to eliminate holes before the
+	 * .bss..swapper_pg_dir section at the start of .bss.  This
+	 * also satisfies PAGE_SIZE alignment as the largest page size
+	 * allowed is 64K.
+	 */
+	. = ALIGN(0x10000);
+	__init_end = .;
+	/* freed after init ends here */
+
+	/*
+	 * Force .bss to 64K alignment so that .bss..swapper_pg_dir
+	 * gets that alignment.	 .sbss should be empty, so there will be
+	 * no holes after __init_end. */
+	BSS_SECTION(0, 0x10000, 8)
+
+	_end = . ;
+
+	STABS_DEBUG
+	DWARF_DEBUG
+
+	/* These must appear regardless of  .  */
+	.gptab.sdata : {
+		*(.gptab.data)
+		*(.gptab.sdata)
+	}
+	.gptab.sbss : {
+		*(.gptab.bss)
+		*(.gptab.sbss)
+	}
+
+	/* Sections to be discarded */
+	DISCARDS
+	/DISCARD/ : {
+		*(.gnu.attributes)
+		*(.options)
+		*(.eh_frame)
+	}
+}
diff --git a/arch/loongarch/lib/Makefile b/arch/loongarch/lib/Makefile
new file mode 100644
index 000000000000..04916bcde679
--- /dev/null
+++ b/arch/loongarch/lib/Makefile
@@ -0,0 +1,7 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for LoongArch-specific library files..
+#
+
+lib-y	+= delay.o memcpy.o memset.o \
+	   strncpy_user.o strnlen_user.o dump_tlb.o
diff --git a/arch/loongarch/loongson64/Makefile b/arch/loongarch/loongson64/Makefile
new file mode 100644
index 000000000000..42e92e5e12be
--- /dev/null
+++ b/arch/loongarch/loongson64/Makefile
@@ -0,0 +1,8 @@
+#
+# All Loongson based systems
+#
+
+obj-y += setup.o init.o env.o reset.o irq.o mem.o \
+	 rtc.o boardinfo.o
+
+obj-$(CONFIG_PCI_MSI)	+= msi.o
diff --git a/arch/loongarch/loongson64/Platform b/arch/loongarch/loongson64/Platform
new file mode 100644
index 000000000000..387e9ae38a51
--- /dev/null
+++ b/arch/loongarch/loongson64/Platform
@@ -0,0 +1,14 @@
+#
+# Loongson Processors' Support
+#
+
+cflags-$(CONFIG_CPU_LOONGSON64)	+= $(call as-option,-Wa$(comma)-mno-fix-loongson3-llsc,)
+cflags-$(CONFIG_CPU_LOONGSON64) += -U_LOONGARCH_ISA -D_LOONGARCH_ISA=_LOONGARCH_ISA_LOONGARCH64
+
+#
+# Loongson Machines Support
+#
+
+platform-$(CONFIG_MACH_LOONGSON64) += loongson64/
+cflags-$(CONFIG_MACH_LOONGSON64)   += -I$(srctree)/arch/loongarch/include/asm/mach-loongson64
+load-$(CONFIG_MACH_LOONGSON64)     += 0x9000000000200000
diff --git a/arch/loongarch/mm/Makefile b/arch/loongarch/mm/Makefile
new file mode 100644
index 000000000000..a3a137709650
--- /dev/null
+++ b/arch/loongarch/mm/Makefile
@@ -0,0 +1,10 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the Linux/LoongArch-specific parts of the memory manager.
+#
+
+obj-y				+= init.o cache.o tlb.o tlbex.o extable.o \
+				   fault.o ioremap.o maccess.o mmap.o pgtable.o page.o
+
+obj-$(CONFIG_64BIT)		+= pgtable-64.o
+obj-$(CONFIG_HUGETLB_PAGE)	+= hugetlbpage.o
diff --git a/arch/loongarch/pci/Makefile b/arch/loongarch/pci/Makefile
new file mode 100644
index 000000000000..113a395ad4a3
--- /dev/null
+++ b/arch/loongarch/pci/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for the PCI specific kernel interface routines under Linux.
+#
+
+obj-y				+= pci.o
+obj-$(CONFIG_ACPI)		+= acpi.o
+obj-$(CONFIG_PCI_MMCONFIG)	+= mmconfig.o
+obj-$(CONFIG_MACH_LOONGSON64)	+= pci-loongson.o
diff --git a/scripts/subarch.include b/scripts/subarch.include
index 650682821126..c79e0d0b1a19 100644
--- a/scripts/subarch.include
+++ b/scripts/subarch.include
@@ -10,4 +10,4 @@ SUBARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \
 				  -e s/s390x/s390/ -e s/parisc64/parisc/ \
 				  -e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
 				  -e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ \
-				  -e s/riscv.*/riscv/)
+				  -e s/riscv.*/riscv/ -e s/loongarch.*/loongarch/)
-- 
2.27.0


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

* [PATCH 05/19] LoongArch: Add boot and setup routines
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (2 preceding siblings ...)
  2021-07-06  4:18 ` [PATCH 03/19] LoongArch: Add build infrastructure Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06 10:16   ` Arnd Bergmann
  2021-07-06 10:55   ` Arnd Bergmann
  2021-07-06  4:18 ` [PATCH 06/19] LoongArch: Add exception/interrupt handling Huacai Chen
                   ` (16 subsequent siblings)
  20 siblings, 2 replies; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

This patch adds basic boot, setup and reset routines for LoongArch.
LoongArch uses UEFI-based firmware and uses ACPI as the boot protocol.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 arch/loongarch/kernel/acpi.c      | 325 +++++++++++++++++++++++
 arch/loongarch/kernel/cacheinfo.c | 126 +++++++++
 arch/loongarch/kernel/cmdline.c   |  28 ++
 arch/loongarch/kernel/cpu-probe.c | 302 +++++++++++++++++++++
 arch/loongarch/kernel/efi.c       |  68 +++++
 arch/loongarch/kernel/head.S      |  67 +++++
 arch/loongarch/kernel/reset.c     |  51 ++++
 arch/loongarch/kernel/setup.c     | 428 ++++++++++++++++++++++++++++++
 arch/loongarch/kernel/time.c      | 227 ++++++++++++++++
 arch/loongarch/kernel/topology.c  |  13 +
 10 files changed, 1635 insertions(+)
 create mode 100644 arch/loongarch/kernel/acpi.c
 create mode 100644 arch/loongarch/kernel/cacheinfo.c
 create mode 100644 arch/loongarch/kernel/cmdline.c
 create mode 100644 arch/loongarch/kernel/cpu-probe.c
 create mode 100644 arch/loongarch/kernel/efi.c
 create mode 100644 arch/loongarch/kernel/head.S
 create mode 100644 arch/loongarch/kernel/reset.c
 create mode 100644 arch/loongarch/kernel/setup.c
 create mode 100644 arch/loongarch/kernel/time.c
 create mode 100644 arch/loongarch/kernel/topology.c

diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c
new file mode 100644
index 000000000000..632a2da2130c
--- /dev/null
+++ b/arch/loongarch/kernel/acpi.c
@@ -0,0 +1,325 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * acpi.c - Architecture-Specific Low-Level ACPI Boot Support
+ *
+ * Author: Jianmin Lv <lvjianmin@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/irq.h>
+#include <linux/irqdomain.h>
+#include <linux/memblock.h>
+#include <asm/io.h>
+#include <loongson.h>
+
+int acpi_disabled;
+EXPORT_SYMBOL(acpi_disabled);
+int acpi_noirq;
+int acpi_pci_disabled;
+EXPORT_SYMBOL(acpi_pci_disabled);
+int acpi_strict = 1; /* We have no workarounds on LoongArch */
+int num_processors;
+int disabled_cpus;
+enum acpi_irq_model_id acpi_irq_model = ACPI_IRQ_MODEL_PLATFORM;
+
+u64 acpi_saved_sp;
+
+#define MAX_CORE_PIC 256
+
+#define PREFIX			"ACPI: "
+
+/*
+ * Following __acpi_xx functions should be implemented for sepecific cpu.
+ */
+int acpi_gsi_to_irq(u32 gsi, unsigned int *irqp)
+{
+	if (irqp != NULL)
+		*irqp = acpi_register_gsi(NULL, gsi, -1, -1);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(acpi_gsi_to_irq);
+
+int acpi_isa_irq_to_gsi(unsigned int isa_irq, u32 *gsi)
+{
+	if (gsi)
+		*gsi = isa_irq;
+	return 0;
+}
+
+/*
+ * success: return IRQ number (>=0)
+ * failure: return < 0
+ */
+int acpi_register_gsi(struct device *dev, u32 gsi, int trigger, int polarity)
+{
+	int id;
+	struct irq_fwspec fwspec;
+	struct fwnode_handle *handle;
+
+	switch (gsi) {
+	case GSI_MIN_CPU_IRQ ... GSI_MAX_CPU_IRQ:
+		fwspec.fwnode = acpi_liointc_handle;
+		fwspec.param[0] = gsi - GSI_MIN_CPU_IRQ;
+		fwspec.param_count = 1;
+
+		return irq_create_fwspec_mapping(&fwspec);
+
+	case GSI_MIN_PCH_IRQ ... GSI_MAX_PCH_IRQ:
+		id = find_pch_pic(gsi);
+		if (id < 0)
+			return -1;
+
+		handle = acpi_picdomain_handle[id];
+		if (handle) {
+			fwspec.fwnode = handle;
+			fwspec.param[0] = gsi - GSI_MIN_PCH_IRQ;
+			fwspec.param[1] = IRQ_TYPE_LEVEL_HIGH;
+			fwspec.param_count = 2;
+
+			return irq_create_fwspec_mapping(&fwspec);
+		}
+
+		return gsi;
+	}
+
+	return -1;
+}
+EXPORT_SYMBOL_GPL(acpi_register_gsi);
+
+void acpi_unregister_gsi(u32 gsi)
+{
+
+}
+EXPORT_SYMBOL_GPL(acpi_unregister_gsi);
+
+void __iomem *__init __acpi_map_table(unsigned long phys, unsigned long size)
+{
+
+	if (!phys || !size)
+		return NULL;
+
+	return early_memremap(phys, size);
+}
+void __init __acpi_unmap_table(void __iomem *map, unsigned long size)
+{
+	if (!map || !size)
+		return;
+
+	early_memunmap(map, size);
+}
+
+void __init acpi_boot_table_init(void)
+{
+	/*
+	 * If acpi_disabled, bail out
+	 */
+	if (acpi_disabled)
+		return;
+
+	/*
+	 * Initialize the ACPI boot-time table parser.
+	 */
+	if (acpi_table_init()) {
+		disable_acpi();
+		return;
+	}
+}
+
+static int __init
+acpi_parse_cpuintc(union acpi_subtable_headers *header, const unsigned long end)
+{
+	struct acpi_madt_core_pic *processor = NULL;
+
+	processor = (struct acpi_madt_core_pic *)header;
+	if (BAD_MADT_ENTRY(processor, end))
+		return -EINVAL;
+
+	acpi_table_print_madt_entry(&header->common);
+
+	return 0;
+}
+
+static int __init
+acpi_parse_liointc(union acpi_subtable_headers *header, const unsigned long end)
+{
+	struct acpi_madt_lio_pic *liointc = NULL;
+
+	liointc = (struct acpi_madt_lio_pic *)header;
+
+	if (BAD_MADT_ENTRY(liointc, end))
+		return -EINVAL;
+
+	acpi_liointc = liointc;
+
+	return 0;
+}
+
+static int __init
+acpi_parse_eiointc(union acpi_subtable_headers *header, const unsigned long end)
+{
+	struct acpi_madt_eio_pic *eiointc = NULL;
+
+	eiointc = (struct acpi_madt_eio_pic *)header;
+
+	if (BAD_MADT_ENTRY(eiointc, end))
+		return -EINVAL;
+
+	acpi_eiointc = eiointc;
+
+	return 0;
+}
+
+static int __init
+acpi_parse_htintc(union acpi_subtable_headers *header, const unsigned long end)
+{
+	struct acpi_madt_ht_pic *htintc = NULL;
+
+	htintc = (struct acpi_madt_ht_pic *)header;
+
+	if (BAD_MADT_ENTRY(htintc, end))
+		return -EINVAL;
+
+	acpi_htintc = htintc;
+
+	return 0;
+}
+
+static int __init
+acpi_parse_pch_lpc(union acpi_subtable_headers *header, const unsigned long end)
+{
+	struct acpi_madt_lpc_pic *pchlpc = NULL;
+
+	pchlpc = (struct acpi_madt_lpc_pic *)header;
+
+	if (BAD_MADT_ENTRY(pchlpc, end))
+		return -EINVAL;
+
+	acpi_pchlpc = pchlpc;
+
+	return 0;
+}
+
+static int __init
+acpi_parse_pch_msi(union acpi_subtable_headers *header, const unsigned long end)
+{
+	struct acpi_madt_msi_pic *pchmsi = NULL;
+
+	pchmsi = (struct acpi_madt_msi_pic *)header;
+
+	if (BAD_MADT_ENTRY(pchmsi, end))
+		return -EINVAL;
+
+	acpi_pchmsi = pchmsi;
+
+	return 0;
+}
+
+static int __init
+acpi_parse_pch_pic(union acpi_subtable_headers *header, const unsigned long end)
+{
+	struct acpi_madt_bio_pic *pchpic = NULL;
+
+	pchpic = (struct acpi_madt_bio_pic *)header;
+
+	if (BAD_MADT_ENTRY(pchpic, end))
+		return -EINVAL;
+
+	acpi_pchpic[loongson_sysconf.nr_pch_pics] = pchpic;
+	loongson_sysconf.nr_pch_pics++;
+
+	return 0;
+}
+
+static void __init acpi_process_madt(void)
+{
+	int error;
+
+	/* Parse MADT CPUINTC entries */
+	error = acpi_table_parse_madt(ACPI_MADT_TYPE_CORE_PIC, acpi_parse_cpuintc, MAX_CORE_PIC);
+	if (error < 0) {
+		disable_acpi();
+		pr_err(PREFIX "Invalid BIOS MADT (CPUINTC entries), ACPI disabled\n");
+		return;
+	}
+
+	loongson_sysconf.nr_cpus = num_processors;
+
+	/* Parse MADT LIOINTC entries */
+	error = acpi_table_parse_madt(ACPI_MADT_TYPE_LIO_PIC, acpi_parse_liointc, 1);
+	if (error < 0) {
+		disable_acpi();
+		pr_err(PREFIX "Invalid BIOS MADT (LIOINTC entries), ACPI disabled\n");
+		return;
+	}
+
+	/* Parse MADT EIOINTC entries */
+	error = acpi_table_parse_madt(ACPI_MADT_TYPE_EIO_PIC, acpi_parse_eiointc, 1);
+	if (error < 0) {
+		disable_acpi();
+		pr_err(PREFIX "Invalid BIOS MADT (EIOINTC entries), ACPI disabled\n");
+		return;
+	}
+
+	/* Parse MADT HTVEC entries */
+	error = acpi_table_parse_madt(ACPI_MADT_TYPE_HT_PIC, acpi_parse_htintc, 1);
+	if (error < 0) {
+		disable_acpi();
+		pr_err(PREFIX "Invalid BIOS MADT (HTVEC entries), ACPI disabled\n");
+		return;
+	}
+
+	/* Parse MADT PCHLPC entries */
+	error = acpi_table_parse_madt(ACPI_MADT_TYPE_LPC_PIC, acpi_parse_pch_lpc, 1);
+	if (error < 0) {
+		disable_acpi();
+		pr_err(PREFIX "Invalid BIOS MADT (PCHLPC entries), ACPI disabled\n");
+		return;
+	}
+
+	/* Parse MADT PCHMSI entries */
+	error = acpi_table_parse_madt(ACPI_MADT_TYPE_MSI_PIC, acpi_parse_pch_msi, 1);
+	if (error < 0) {
+		disable_acpi();
+		pr_err(PREFIX "Invalid BIOS MADT (PCHMSI entries), ACPI disabled\n");
+		return;
+	}
+
+	/* Parse MADT PCHPIC entries */
+	error = acpi_table_parse_madt(ACPI_MADT_TYPE_BIO_PIC, acpi_parse_pch_pic, MAX_PCH_PICS);
+	if (error < 0) {
+		disable_acpi();
+		pr_err(PREFIX "Invalid BIOS MADT (PCHPIC entries), ACPI disabled\n");
+		return;
+	}
+}
+
+int __init acpi_boot_init(void)
+{
+	/*
+	 * If acpi_disabled, bail out
+	 */
+	if (acpi_disabled)
+		return -1;
+
+	loongson_sysconf.boot_cpu_id = read_csr_cpuid();
+
+	/*
+	 * Process the Multiple APIC Description Table (MADT), if present
+	 */
+	acpi_process_madt();
+
+	return 0;
+}
+
+void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
+{
+	u8 map_count = loongson_mem_map->map_count;
+
+	loongson_mem_map->map[map_count].mem_start = addr;
+	loongson_mem_map->map[map_count].mem_size = size;
+	loongson_mem_map->map[map_count].mem_type = ADDRESS_TYPE_ACPI;
+	loongson_mem_map->map_count++;
+}
diff --git a/arch/loongarch/kernel/cacheinfo.c b/arch/loongarch/kernel/cacheinfo.c
new file mode 100644
index 000000000000..399e2f5fa6fc
--- /dev/null
+++ b/arch/loongarch/kernel/cacheinfo.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * LoongArch cacheinfo support
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/cacheinfo.h>
+#include <linux/of.h>
+
+/* Populates leaf and increments to next leaf */
+#define populate_cache(cache, leaf, c_level, c_type)		\
+do {								\
+	leaf->type = c_type;					\
+	leaf->level = c_level;					\
+	leaf->coherency_line_size = c->cache.linesz;		\
+	leaf->number_of_sets = c->cache.sets;			\
+	leaf->ways_of_associativity = c->cache.ways;		\
+	leaf->size = c->cache.linesz * c->cache.sets *		\
+		c->cache.ways;					\
+	leaf++;							\
+} while (0)
+
+static int __init_cache_level(unsigned int cpu)
+{
+	struct cpuinfo_loongarch *c = &current_cpu_data;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	int levels = 0, leaves = 0;
+
+	/*
+	 * If Dcache is not set, we assume the cache structures
+	 * are not properly initialized.
+	 */
+	if (c->dcache.waysize)
+		levels += 1;
+	else
+		return -ENOENT;
+
+
+	leaves += (c->icache.waysize) ? 2 : 1;
+
+	if (c->vcache.waysize) {
+		levels++;
+		leaves++;
+	}
+
+	if (c->scache.waysize) {
+		levels++;
+		leaves++;
+	}
+
+	if (c->tcache.waysize) {
+		levels++;
+		leaves++;
+	}
+
+	this_cpu_ci->num_levels = levels;
+	this_cpu_ci->num_leaves = leaves;
+	return 0;
+}
+
+static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf,
+					   struct cacheinfo *sib_leaf)
+{
+	return !((this_leaf->level == 1) || (this_leaf->level == 2));
+}
+
+static void __cache_cpumap_setup(unsigned int cpu)
+{
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cacheinfo *this_leaf, *sib_leaf;
+	unsigned int index;
+
+	for (index = 0; index < this_cpu_ci->num_leaves; index++) {
+		unsigned int i;
+
+		this_leaf = this_cpu_ci->info_list + index;
+		/* skip if shared_cpu_map is already populated */
+		if (!cpumask_empty(&this_leaf->shared_cpu_map))
+			continue;
+
+		cpumask_set_cpu(cpu, &this_leaf->shared_cpu_map);
+		for_each_online_cpu(i) {
+			struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i);
+
+			if (i == cpu || !sib_cpu_ci->info_list)
+				continue;/* skip if itself or no cacheinfo */
+			sib_leaf = sib_cpu_ci->info_list + index;
+			if (cache_leaves_are_shared(this_leaf, sib_leaf)) {
+				cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map);
+				cpumask_set_cpu(i, &this_leaf->shared_cpu_map);
+			}
+		}
+	}
+}
+
+static int __populate_cache_leaves(unsigned int cpu)
+{
+	int level = 1;
+	struct cpuinfo_loongarch *c = &current_cpu_data;
+	struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu);
+	struct cacheinfo *this_leaf = this_cpu_ci->info_list;
+
+	if (c->icache.waysize) {
+		populate_cache(dcache, this_leaf, level, CACHE_TYPE_DATA);
+		populate_cache(icache, this_leaf, level++, CACHE_TYPE_INST);
+	} else {
+		populate_cache(dcache, this_leaf, level++, CACHE_TYPE_UNIFIED);
+	}
+
+	if (c->vcache.waysize)
+		populate_cache(vcache, this_leaf, level++, CACHE_TYPE_UNIFIED);
+
+	if (c->scache.waysize)
+		populate_cache(scache, this_leaf, level++, CACHE_TYPE_UNIFIED);
+
+	if (c->tcache.waysize)
+		populate_cache(tcache, this_leaf, level++, CACHE_TYPE_UNIFIED);
+
+	__cache_cpumap_setup(cpu);
+	this_cpu_ci->cpu_map_populated = true;
+
+	return 0;
+}
+
+DEFINE_SMP_CALL_CACHE_FUNCTION(init_cache_level)
+DEFINE_SMP_CALL_CACHE_FUNCTION(populate_cache_leaves)
diff --git a/arch/loongarch/kernel/cmdline.c b/arch/loongarch/kernel/cmdline.c
new file mode 100644
index 000000000000..46bf4486b54c
--- /dev/null
+++ b/arch/loongarch/kernel/cmdline.c
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+#include <asm/fw.h>
+
+int fw_argc;
+long *_fw_argv, *_fw_envp;
+
+void __init fw_init_cmdline(void)
+{
+	int i;
+
+	fw_argc = fw_arg0;
+	_fw_argv = (long *)fw_arg1;
+	_fw_envp = (long *)fw_arg2;
+
+	arcs_cmdline[0] = '\0';
+	for (i = 1; i < fw_argc; i++) {
+		strlcat(arcs_cmdline, fw_argv(i), COMMAND_LINE_SIZE);
+		if (i < (fw_argc - 1))
+			strlcat(arcs_cmdline, " ", COMMAND_LINE_SIZE);
+	}
+}
diff --git a/arch/loongarch/kernel/cpu-probe.c b/arch/loongarch/kernel/cpu-probe.c
new file mode 100644
index 000000000000..400c7d1ba67e
--- /dev/null
+++ b/arch/loongarch/kernel/cpu-probe.c
@@ -0,0 +1,302 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Processor capabilities determination functions.
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/ptrace.h>
+#include <linux/smp.h>
+#include <linux/stddef.h>
+#include <linux/export.h>
+#include <linux/printk.h>
+#include <linux/uaccess.h>
+
+#include <asm/cpu.h>
+#include <asm/cpu-features.h>
+#include <asm/fpu.h>
+#include <asm/loongarchregs.h>
+#include <asm/elf.h>
+#include <asm/pgtable-bits.h>
+
+/* Hardware capabilities */
+unsigned int elf_hwcap __read_mostly;
+EXPORT_SYMBOL_GPL(elf_hwcap);
+
+/*
+ * Determine the FCSR mask for FPU hardware.
+ */
+static inline void cpu_set_fpu_fcsr_mask(struct cpuinfo_loongarch *c)
+{
+	unsigned long sr, mask, fcsr, fcsr0, fcsr1;
+
+	fcsr = c->fpu_csr0;
+	mask = FPU_CSR_ALL_X | FPU_CSR_ALL_E | FPU_CSR_ALL_S | FPU_CSR_RM;
+
+	sr = read_csr_euen();
+	enable_fpu();
+
+	fcsr0 = fcsr & mask;
+	write_fcsr(LOONGARCH_FCSR0, fcsr0);
+	fcsr0 = read_fcsr(LOONGARCH_FCSR0);
+
+	fcsr1 = fcsr | ~mask;
+	write_fcsr(LOONGARCH_FCSR0, fcsr1);
+	fcsr1 = read_fcsr(LOONGARCH_FCSR0);
+
+	write_fcsr(LOONGARCH_FCSR0, fcsr);
+
+	write_csr_euen(sr);
+
+	c->fpu_mask = ~(fcsr0 ^ fcsr1) & ~mask;
+}
+
+static inline void set_elf_platform(int cpu, const char *plat)
+{
+	if (cpu == 0)
+		__elf_platform = plat;
+}
+
+/* MAP BASE */
+unsigned long vm_map_base;
+EXPORT_SYMBOL_GPL(vm_map_base);
+
+void cpu_probe_addrbits(struct cpuinfo_loongarch *c)
+{
+#ifdef __NEED_ADDRBITS_PROBE
+	c->pabits = (read_cpucfg(LOONGARCH_CPUCFG1) & CPUCFG1_PABITS) >> 4;
+	c->vabits = (read_cpucfg(LOONGARCH_CPUCFG1) & CPUCFG1_VABITS) >> 12;
+	vm_map_base = 0UL - (1UL << c->vabits);
+#endif
+}
+
+static void set_isa(struct cpuinfo_loongarch *c, unsigned int isa)
+{
+	switch (isa) {
+	case LOONGARCH_CPU_ISA_LA64:
+		c->isa_level |= LOONGARCH_CPU_ISA_LA64;
+		fallthrough;
+	case LOONGARCH_CPU_ISA_LA32S:
+		c->isa_level |= LOONGARCH_CPU_ISA_LA32S;
+		fallthrough;
+	case LOONGARCH_CPU_ISA_LA32R:
+		c->isa_level |= LOONGARCH_CPU_ISA_LA32R;
+		break;
+	}
+}
+
+static void decode_configs(struct cpuinfo_loongarch *c)
+{
+	unsigned int config;
+	unsigned long asid_mask;
+
+	config = read_cpucfg(LOONGARCH_CPUCFG1);
+	if (config & CPUCFG1_UAL) {
+		c->options |= LOONGARCH_CPU_UAL;
+		elf_hwcap |= HWCAP_LOONGARCH_UAL;
+	}
+
+	config = read_cpucfg(LOONGARCH_CPUCFG2);
+	if (config & CPUCFG2_LAM) {
+		c->options |= LOONGARCH_CPU_LAM;
+		elf_hwcap |= HWCAP_LOONGARCH_LAM;
+	}
+	if (config & CPUCFG2_FP) {
+		c->options |= LOONGARCH_CPU_FPU;
+		elf_hwcap |= HWCAP_LOONGARCH_FPU;
+	}
+	if (config & CPUCFG2_COMPLEX) {
+		c->options |= LOONGARCH_CPU_COMPLEX;
+		elf_hwcap |= HWCAP_LOONGARCH_COMPLEX;
+	}
+	if (config & CPUCFG2_CRYPTO) {
+		c->options |= LOONGARCH_CPU_CRYPTO;
+		elf_hwcap |= HWCAP_LOONGARCH_CRYPTO;
+	}
+	if (config & CPUCFG2_LVZP) {
+		c->options |= LOONGARCH_CPU_LVZ;
+		elf_hwcap |= HWCAP_LOONGARCH_LVZ;
+	}
+
+	config = read_cpucfg(LOONGARCH_CPUCFG6);
+	if (config & CPUCFG6_PMP)
+		c->options |= LOONGARCH_CPU_PMP;
+
+	config = iocsr_readl(LOONGARCH_IOCSR_FEATURES);
+	if (config & IOCSRF_CSRIPI)
+		c->options |= LOONGARCH_CPU_CSRIPI;
+	if (config & IOCSRF_EXTIOI)
+		c->options |= LOONGARCH_CPU_EXTIOI;
+	if (config & IOCSRF_FREQSCALE)
+		c->options |= LOONGARCH_CPU_SCALEFREQ;
+	if (config & IOCSRF_VM)
+		c->options |= LOONGARCH_CPU_HYPERVISOR;
+
+	config = csr_readl(LOONGARCH_CSR_ASID);
+	config = (config & CSR_ASID_BIT) >> CSR_ASID_BIT_SHIFT;
+	asid_mask = GENMASK(config - 1, 0);
+	set_cpu_asid_mask(c, asid_mask);
+
+	config = read_csr_prcfg1();
+	c->kscratch_mask = GENMASK((config & CSR_CONF1_KSNUM) - 1, 0);
+	c->kscratch_mask &= ~(EXC_KSCRATCH_MASK | PERCPU_KSCRATCH_MASK | KVM_KSCRATCH_MASK);
+
+	config = read_csr_prcfg3();
+	switch (config & CSR_CONF3_TLBTYPE) {
+	case 0:
+		c->tlbsizemtlb = 0;
+		c->tlbsizestlbsets = 0;
+		c->tlbsizestlbways = 0;
+		c->tlbsize = 0;
+		break;
+	case 1:
+		c->tlbsizemtlb = ((config & CSR_CONF3_MTLBSIZE) >> CSR_CONF3_MTLBSIZE_SHIFT) + 1;
+		c->tlbsizestlbsets = 0;
+		c->tlbsizestlbways = 0;
+		c->tlbsize = c->tlbsizemtlb + c->tlbsizestlbsets * c->tlbsizestlbways;
+		break;
+	case 2:
+		c->tlbsizemtlb = ((config & CSR_CONF3_MTLBSIZE) >> CSR_CONF3_MTLBSIZE_SHIFT) + 1;
+		c->tlbsizestlbsets = 1 << ((config & CSR_CONF3_STLBIDX) >> CSR_CONF3_STLBIDX_SHIFT);
+		c->tlbsizestlbways = ((config & CSR_CONF3_STLBWAYS) >> CSR_CONF3_STLBWAYS_SHIFT) + 1;
+		c->tlbsize = c->tlbsizemtlb + c->tlbsizestlbsets * c->tlbsizestlbways;
+		break;
+	default:
+		pr_warn("Warning: unimplemented tlb type\n");
+	}
+}
+
+#define MAX_NAME_LEN	32
+#define VENDOR_OFFSET	0
+#define CPUNAME_OFFSET	9
+
+static char cpu_full_name[MAX_NAME_LEN] = "        -        ";
+
+static inline void cpu_probe_loongson(struct cpuinfo_loongarch *c, unsigned int cpu)
+{
+	uint64_t *vendor = (void *)(&cpu_full_name[VENDOR_OFFSET]);
+	uint64_t *cpuname = (void *)(&cpu_full_name[CPUNAME_OFFSET]);
+
+	c->options = LOONGARCH_CPU_CPUCFG | LOONGARCH_CPU_CSR |
+		     LOONGARCH_CPU_TLB | LOONGARCH_CPU_VINT | LOONGARCH_CPU_WATCH;
+
+	decode_configs(c);
+	elf_hwcap |= HWCAP_LOONGARCH_CRC32;
+
+	__cpu_full_name[cpu] = cpu_full_name;
+	*vendor = iocsr_readq(LOONGARCH_IOCSR_VENDOR);
+	*cpuname = iocsr_readq(LOONGARCH_IOCSR_CPUNAME);
+
+	switch (c->processor_id & PRID_IMP_MASK) {
+	case PRID_IMP_LOONGSON_32:
+		c->cputype = CPU_LOONGSON32;
+		set_isa(c, LOONGARCH_CPU_ISA_LA32S);
+		c->writecombine = _CACHE_WUC;
+		__cpu_family[cpu] = "Loongson-32bit";
+		pr_info("Standard 32-bit Loongson Processor probed\n");
+		break;
+	case PRID_IMP_LOONGSON_64R:
+		c->cputype = CPU_LOONGSON64;
+		set_isa(c, LOONGARCH_CPU_ISA_LA64);
+		c->writecombine = _CACHE_WUC;
+		__cpu_family[cpu] = "Loongson-64bit";
+		pr_info("Reduced 64-bit Loongson Processor probed\n");
+		break;
+	case PRID_IMP_LOONGSON_64C:
+		c->cputype = CPU_LOONGSON64;
+		set_isa(c, LOONGARCH_CPU_ISA_LA64);
+		c->writecombine = _CACHE_WUC;
+		__cpu_family[cpu] = "Loongson-64bit";
+		pr_info("Classic 64-bit Loongson Processor probed\n");
+		break;
+	case PRID_IMP_LOONGSON_64G:
+		c->cputype = CPU_LOONGSON64;
+		set_isa(c, LOONGARCH_CPU_ISA_LA64);
+		c->writecombine = _CACHE_WUC;
+		__cpu_family[cpu] = "Loongson-64bit";
+		pr_info("Generic 64-bit Loongson Processor probed\n");
+		break;
+	default: /* Default to 64 bit */
+		c->cputype = CPU_LOONGSON64;
+		set_isa(c, LOONGARCH_CPU_ISA_LA64);
+		c->writecombine = _CACHE_SUC;
+		__cpu_family[cpu] = "Loongson-64bit";
+		pr_info("Unknown 64-bit Loongson Processor probed\n");
+	}
+}
+
+#ifdef CONFIG_64BIT
+/* For use by uaccess.h */
+u64 __ua_limit;
+EXPORT_SYMBOL(__ua_limit);
+#endif
+
+const char *__cpu_family[NR_CPUS];
+const char *__cpu_full_name[NR_CPUS];
+const char *__elf_platform;
+
+void cpu_probe(void)
+{
+	struct cpuinfo_loongarch *c = &current_cpu_data;
+	unsigned int cpu = smp_processor_id();
+
+	/*
+	 * Set a default elf platform, cpu probe may later
+	 * overwrite it with a more precise value
+	 */
+	set_elf_platform(cpu, "loongarch");
+
+	c->cputype	= CPU_UNKNOWN;
+	c->processor_id = read_cpucfg(LOONGARCH_CPUCFG0);
+	c->fpu_vers	= (read_cpucfg(LOONGARCH_CPUCFG2) >> 3) & 0x3;
+	c->writecombine = _CACHE_SUC;
+
+	c->fpu_csr0	= FPU_CSR_RN;
+	c->fpu_mask	= FPU_CSR_RSVD;
+
+	switch (c->processor_id & PRID_COMP_MASK) {
+	case PRID_COMP_LOONGSON:
+		cpu_probe_loongson(c, cpu);
+		break;
+	}
+
+	BUG_ON(!__cpu_family[cpu]);
+	BUG_ON(c->cputype == CPU_UNKNOWN);
+
+	cpu_probe_addrbits(c);
+
+#ifdef CONFIG_64BIT
+	if (cpu == 0)
+		__ua_limit = ~((1ull << cpu_vabits) - 1);
+#endif
+}
+
+void cpu_report(void)
+{
+	struct cpuinfo_loongarch *c = &current_cpu_data;
+
+	pr_info("CPU%d revision is: %08x (%s)\n",
+		smp_processor_id(), c->processor_id, cpu_family_string());
+	if (c->options & LOONGARCH_CPU_FPU)
+		pr_info("FPU%d revision is: %08x\n", smp_processor_id(), c->fpu_vers);
+}
+
+void cpu_set_cluster(struct cpuinfo_loongarch *cpuinfo, unsigned int cluster)
+{
+	/* Ensure the core number fits in the field */
+	WARN_ON(cluster > (LOONGARCH_GLOBALNUMBER_CLUSTER >>
+			   LOONGARCH_GLOBALNUMBER_CLUSTER_SHF));
+
+	cpuinfo->globalnumber &= ~LOONGARCH_GLOBALNUMBER_CLUSTER;
+	cpuinfo->globalnumber |= cluster << LOONGARCH_GLOBALNUMBER_CLUSTER_SHF;
+}
+
+void cpu_set_core(struct cpuinfo_loongarch *cpuinfo, unsigned int core)
+{
+	/* Ensure the core number fits in the field */
+	WARN_ON(core > (LOONGARCH_GLOBALNUMBER_CORE >> LOONGARCH_GLOBALNUMBER_CORE_SHF));
+
+	cpuinfo->globalnumber &= ~LOONGARCH_GLOBALNUMBER_CORE;
+	cpuinfo->globalnumber |= core << LOONGARCH_GLOBALNUMBER_CORE_SHF;
+}
diff --git a/arch/loongarch/kernel/efi.c b/arch/loongarch/kernel/efi.c
new file mode 100644
index 000000000000..ed359c1d6976
--- /dev/null
+++ b/arch/loongarch/kernel/efi.c
@@ -0,0 +1,68 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * EFI initialization
+ *
+ * Author: Jianmin Lv <lvjianmin@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/efi.h>
+#include <linux/acpi.h>
+#include <linux/efi-bgrt.h>
+#include <linux/export.h>
+#include <linux/slab.h>
+#include <linux/memblock.h>
+#include <linux/spinlock.h>
+#include <linux/uaccess.h>
+#include <linux/time.h>
+#include <linux/io.h>
+#include <linux/reboot.h>
+#include <linux/bcd.h>
+
+#include <asm/efi.h>
+#include <boot_param.h>
+
+static efi_config_table_type_t arch_tables[] __initdata = {{},};
+
+void __init efi_runtime_init(void)
+{
+	if (!efi_enabled(EFI_BOOT))
+		return;
+
+	if (!efi.runtime)
+		return;
+
+	if (efi_runtime_disabled()) {
+		pr_info("EFI runtime services will be disabled.\n");
+		return;
+	}
+
+	efi_native_runtime_setup();
+	set_bit(EFI_RUNTIME_SERVICES, &efi.flags);
+}
+
+void __init efi_init(void)
+{
+	unsigned long efi_config_table;
+	efi_system_table_t *efi_systab;
+
+	if (!efi_bp)
+		return;
+
+	efi_systab = (efi_system_table_t *)efi_bp->systemtable;
+	if (!efi_systab) {
+		pr_err("Can't find EFI system table.\n");
+		return;
+	}
+
+	set_bit(EFI_64BIT, &efi.flags);
+	efi_config_table = (unsigned long)efi_systab->tables;
+	efi.runtime	 = (efi_runtime_services_t *)efi_systab->runtime;
+	efi.runtime_version = (unsigned int)efi_systab->runtime->hdr.revision;
+
+	efi_config_parse_tables((void *)efi_systab->tables, efi_systab->nr_tables, arch_tables);
+}
diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
new file mode 100644
index 000000000000..0658db1ecb56
--- /dev/null
+++ b/arch/loongarch/kernel/head.S
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/init.h>
+#include <linux/threads.h>
+
+#include <asm/addrspace.h>
+#include <asm/asm.h>
+#include <asm/asmmacro.h>
+#include <asm/irqflags.h>
+#include <asm/regdef.h>
+#include <asm/loongarchregs.h>
+#include <asm/stackframe.h>
+
+SYM_ENTRY(_stext, SYM_L_GLOBAL, SYM_A_NONE)
+
+	__REF
+
+SYM_CODE_START(kernel_entry)			# kernel entry point
+
+	/* We might not get launched at the address the kernel is linked to,
+	   so we jump there.  */
+	la.abs		t0, 0f
+	jirl		zero, t0, 0
+0:
+	la		t0, __bss_start		# clear .bss
+	st.d		zero, t0, 0
+	la		t1, __bss_stop - LONGSIZE
+1:
+	addi.d		t0, t0, LONGSIZE
+	st.d		zero, t0, 0
+	bne		t0, t1, 1b
+
+	la		t0, fw_arg0
+	st.d		a0, t0, 0		# firmware arguments
+	la		t0, fw_arg1
+	st.d		a1, t0, 0
+	la		t0, fw_arg2
+	st.d		a2, t0, 0
+	la		t0, fw_arg3
+	st.d		a3, t0, 0
+
+	/* Config direct window and set PG */
+	li.d		t0, CSR_DMW0_INIT	# UC, PLV0, 0x8000 xxxx xxxx xxxx
+	csrwr		t0, LOONGARCH_CSR_DMWIN0
+	li.d		t0, CSR_DMW1_INIT	# CA, PLV0, 0x9000 xxxx xxxx xxxx
+	csrwr		t0, LOONGARCH_CSR_DMWIN1
+	/* Enable PG */
+	li.w		t0, 0xb0		# PLV=0, IE=0, PG=1
+	csrwr		t0, LOONGARCH_CSR_CRMD
+
+	/* KScratch3 used for percpu base, initialized as 0 */
+	csrwr		zero, PERCPU_BASE_KS
+	/* GPR21 used for percpu base (runtime), initialized as 0 */
+	or		x0, zero, zero
+
+	la		tp, init_thread_union
+	/* Set the SP after an empty pt_regs.  */
+	PTR_LI		sp, (_THREAD_SIZE - 32 - PT_SIZE)
+	PTR_ADDU	sp, sp, tp
+	set_saved_sp	sp, t0, t1
+	PTR_ADDIU	sp, sp, -4 * SZREG	# init stack pointer
+
+	b		start_kernel
+
+SYM_CODE_END(kernel_entry)
diff --git a/arch/loongarch/kernel/reset.c b/arch/loongarch/kernel/reset.c
new file mode 100644
index 000000000000..cdbeeaa052b3
--- /dev/null
+++ b/arch/loongarch/kernel/reset.c
@@ -0,0 +1,51 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/kernel.h>
+#include <linux/export.h>
+#include <linux/pm.h>
+#include <linux/types.h>
+#include <linux/reboot.h>
+#include <linux/delay.h>
+#include <linux/console.h>
+
+#include <asm/compiler.h>
+#include <asm/idle.h>
+#include <asm/loongarchregs.h>
+#include <asm/reboot.h>
+
+static void machine_hang(void)
+{
+	local_irq_disable();
+	clear_csr_ecfg(ECFG0_IM);
+
+	pr_notice("\n\n** You can safely turn off the power now **\n\n");
+	console_flush_on_panic(CONSOLE_FLUSH_PENDING);
+
+	while (true) {
+		__arch_cpu_idle();
+		local_irq_disable();
+	}
+}
+
+void (*pm_restart)(void) = machine_hang;
+void (*pm_power_off)(void) = machine_hang;
+
+EXPORT_SYMBOL(pm_power_off);
+
+void machine_halt(void)
+{
+	machine_hang();
+}
+
+void machine_power_off(void)
+{
+	pm_power_off();
+}
+
+void machine_restart(char *command)
+{
+	do_kernel_restart(command);
+	pm_restart();
+}
diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
new file mode 100644
index 000000000000..ba63b45ed82f
--- /dev/null
+++ b/arch/loongarch/kernel/setup.c
@@ -0,0 +1,428 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/export.h>
+#include <linux/screen_info.h>
+#include <linux/memblock.h>
+#include <linux/initrd.h>
+#include <linux/root_dev.h>
+#include <linux/highmem.h>
+#include <linux/console.h>
+#include <linux/pfn.h>
+#include <linux/debugfs.h>
+#include <linux/sizes.h>
+#include <linux/device.h>
+#include <linux/dma-map-ops.h>
+#include <linux/swiotlb.h>
+
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+#include <asm/cache.h>
+#include <asm/cpu.h>
+#include <asm/debug.h>
+#include <asm/dma.h>
+#include <asm/sections.h>
+#include <asm/setup.h>
+
+struct cpuinfo_loongarch cpu_data[NR_CPUS] __read_mostly;
+
+EXPORT_SYMBOL(cpu_data);
+
+#ifdef CONFIG_VT
+struct screen_info screen_info;
+#endif
+
+/*
+ * Setup information
+ *
+ * These are initialized so they are in the .data section
+ */
+
+char __initdata arcs_cmdline[COMMAND_LINE_SIZE];
+static char __initdata command_line[COMMAND_LINE_SIZE];
+
+/*
+ * loongarch_io_port_base is the begin of the address space to which x86 style
+ * I/O ports are mapped.
+ */
+unsigned long loongarch_io_port_base = -1;
+EXPORT_SYMBOL(loongarch_io_port_base);
+
+static struct resource code_resource = { .name = "Kernel code", };
+static struct resource data_resource = { .name = "Kernel data", };
+static struct resource bss_resource = { .name = "Kernel bss", };
+
+unsigned long __kaslr_offset __ro_after_init;
+EXPORT_SYMBOL(__kaslr_offset);
+
+static void *detect_magic __initdata = detect_memory_region;
+
+void __init detect_memory_region(phys_addr_t start, phys_addr_t sz_min, phys_addr_t sz_max)
+{
+	void *dm = &detect_magic;
+	phys_addr_t size;
+
+	for (size = sz_min; size < sz_max; size <<= 1) {
+		if (!memcmp(dm, dm + size, sizeof(detect_magic)))
+			break;
+	}
+
+	pr_debug("Memory: %lluMB of RAM detected at 0x%llx (min: %lluMB, max: %lluMB)\n",
+		((unsigned long long) size) / SZ_1M,
+		(unsigned long long) start,
+		((unsigned long long) sz_min) / SZ_1M,
+		((unsigned long long) sz_max) / SZ_1M);
+
+	memblock_add(start, size);
+}
+
+/*
+ * Manage initrd
+ */
+#ifdef CONFIG_BLK_DEV_INITRD
+
+static int __init rd_start_early(char *p)
+{
+	unsigned long start = memparse(p, &p);
+
+#ifdef CONFIG_64BIT
+	/* Guess if the sign extension was forgotten by bootloader */
+	if (start < CAC_BASE)
+		start = (int)start;
+#endif
+	initrd_start = start;
+	initrd_end += start;
+	return 0;
+}
+early_param("rd_start", rd_start_early);
+
+static int __init rd_size_early(char *p)
+{
+	initrd_end += memparse(p, &p);
+	return 0;
+}
+early_param("rd_size", rd_size_early);
+
+static unsigned long __init init_initrd(void)
+{
+	/*
+	 * Board specific code or command line parser should have
+	 * already set up initrd_start and initrd_end. In these cases
+	 * perfom sanity checks and use them if all looks good.
+	 */
+	if (!initrd_start || initrd_end <= initrd_start)
+		goto disable;
+
+	if (initrd_start & ~PAGE_MASK) {
+		pr_err("initrd start must be page aligned\n");
+		goto disable;
+	}
+	if (initrd_start < PAGE_OFFSET) {
+		pr_err("initrd start < PAGE_OFFSET\n");
+		goto disable;
+	}
+
+	ROOT_DEV = Root_RAM0;
+
+	return 0;
+disable:
+	initrd_start = 0;
+	initrd_end = 0;
+	return 0;
+}
+
+static void __init finalize_initrd(void)
+{
+	unsigned long size = initrd_end - initrd_start;
+
+	if (size == 0) {
+		pr_info("Initrd not found or empty");
+		goto disable;
+	}
+	if (__pa(initrd_end) > PFN_PHYS(max_low_pfn)) {
+		pr_err("Initrd extends beyond end of memory");
+		goto disable;
+	}
+
+
+	memblock_reserve(__pa(initrd_start), size);
+	initrd_below_start_ok = 1;
+
+	pr_info("Initial ramdisk at: 0x%lx (%lu bytes)\n",
+		initrd_start, size);
+	return;
+disable:
+	pr_cont(" - disabling initrd\n");
+	initrd_start = 0;
+	initrd_end = 0;
+}
+
+#else  /* !CONFIG_BLK_DEV_INITRD */
+
+static unsigned long __init init_initrd(void)
+{
+	return 0;
+}
+
+#define finalize_initrd()	do {} while (0)
+
+#endif
+
+/*
+ * Initialize the bootmem allocator. It also setup initrd related data
+ * if needed.
+ */
+static void __init bootmem_init(void)
+{
+	init_initrd();
+	finalize_initrd();
+}
+
+static int usermem __initdata;
+
+static int __init early_parse_mem(char *p)
+{
+	phys_addr_t start, size;
+
+	/*
+	 * If a user specifies memory size, we
+	 * blow away any automatically generated
+	 * size.
+	 */
+	if (usermem == 0) {
+		usermem = 1;
+		memblock_remove(memblock_start_of_DRAM(),
+			memblock_end_of_DRAM() - memblock_start_of_DRAM());
+	}
+	start = 0;
+	size = memparse(p, &p);
+	if (*p == '@')
+		start = memparse(p + 1, &p);
+
+	memblock_add(start, size);
+
+	return 0;
+}
+early_param("mem", early_parse_mem);
+
+static int __init early_parse_memmap(char *p)
+{
+	char *oldp;
+	u64 start_at, mem_size;
+
+	if (!p)
+		return -EINVAL;
+
+	if (!strncmp(p, "exactmap", 8)) {
+		pr_err("\"memmap=exactmap\" invalid on LoongArch\n");
+		return 0;
+	}
+
+	oldp = p;
+	mem_size = memparse(p, &p);
+	if (p == oldp)
+		return -EINVAL;
+
+	if (*p == '@') {
+		start_at = memparse(p+1, &p);
+		memblock_add(start_at, mem_size);
+	} else if (*p == '#') {
+		pr_err("\"memmap=nn#ss\" (force ACPI data) invalid on LoongArch\n");
+		return -EINVAL;
+	} else if (*p == '$') {
+		start_at = memparse(p+1, &p);
+		memblock_add(start_at, mem_size);
+		memblock_reserve(start_at, mem_size);
+	} else {
+		pr_err("\"memmap\" invalid format!\n");
+		return -EINVAL;
+	}
+
+	if (*p == '\0') {
+		usermem = 1;
+		return 0;
+	} else
+		return -EINVAL;
+}
+early_param("memmap", early_parse_memmap);
+
+static void __init check_kernel_sections_mem(void)
+{
+	phys_addr_t start = __pa_symbol(&_text);
+	phys_addr_t size = __pa_symbol(&_end) - start;
+
+	if (!memblock_is_region_memory(start, size)) {
+		pr_info("Kernel sections are not in the memory maps\n");
+		memblock_add(start, size);
+	}
+}
+
+static void __init bootcmdline_append(const char *s, size_t max)
+{
+	if (!s[0] || !max)
+		return;
+
+	if (boot_command_line[0])
+		strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
+
+	strlcat(boot_command_line, s, max);
+}
+
+static void __init bootcmdline_init(char **cmdline_p)
+{
+	boot_command_line[0] = 0;
+
+	/*
+	 * Take arguments from the bootloader at first. Early code should have
+	 * filled arcs_cmdline with arguments from the bootloader.
+	 */
+	bootcmdline_append(arcs_cmdline, COMMAND_LINE_SIZE);
+
+	strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE);
+	*cmdline_p = command_line;
+
+	parse_early_param();
+}
+
+/*
+ * arch_mem_init - initialize memory management subsystem
+ */
+static void __init arch_mem_init(char **cmdline_p)
+{
+	/* call board setup routine */
+	plat_mem_setup();
+	memblock_set_bottom_up(true);
+
+	if (usermem)
+		pr_info("User-defined physical RAM map overwrite\n");
+
+	check_kernel_sections_mem();
+
+	memblock_set_node(0, PHYS_ADDR_MAX, &memblock.memory, 0);
+
+	bootmem_init();
+
+	/*
+	 * Prevent memblock from allocating high memory.
+	 * This cannot be done before max_low_pfn is detected, so up
+	 * to this point is possible to only reserve physical memory
+	 * with memblock_reserve; memblock_alloc* can be used
+	 * only after this point
+	 */
+	memblock_set_current_limit(PFN_PHYS(max_low_pfn));
+
+	/*
+	 * In order to reduce the possibility of kernel panic when failed to
+	 * get IO TLB memory under CONFIG_SWIOTLB, it is better to allocate
+	 * low memory as small as possible before plat_swiotlb_setup(), so
+	 * make sparse_init() using top-down allocation.
+	 */
+	memblock_set_bottom_up(false);
+	sparse_init();
+	memblock_set_bottom_up(true);
+
+	swiotlb_init(1);
+
+	dma_contiguous_reserve(PFN_PHYS(max_low_pfn));
+
+	memblock_dump_all();
+
+	early_memtest(PFN_PHYS(ARCH_PFN_OFFSET), PFN_PHYS(max_low_pfn));
+}
+
+static void __init resource_init(void)
+{
+	u64 i;
+	phys_addr_t start, end;
+
+	if (UNCAC_BASE != IO_BASE)
+		return;
+
+	code_resource.start = __pa_symbol(&_text);
+	code_resource.end = __pa_symbol(&_etext) - 1;
+	data_resource.start = __pa_symbol(&_etext);
+	data_resource.end = __pa_symbol(&_edata) - 1;
+	bss_resource.start = __pa_symbol(&__bss_start);
+	bss_resource.end = __pa_symbol(&__bss_stop) - 1;
+
+	for_each_mem_range(i, &start, &end) {
+		struct resource *res;
+
+		res = memblock_alloc(sizeof(struct resource), SMP_CACHE_BYTES);
+		if (!res)
+			panic("%s: Failed to allocate %zu bytes\n", __func__,
+			      sizeof(struct resource));
+
+		res->start = start;
+		/*
+		 * In memblock, end points to the first byte after the
+		 * range while in resourses, end points to the last byte in
+		 * the range.
+		 */
+		res->end = end - 1;
+		res->flags = IORESOURCE_SYSTEM_RAM | IORESOURCE_BUSY;
+		res->name = "System RAM";
+
+		request_resource(&iomem_resource, res);
+
+		/*
+		 *  We don't know which RAM region contains kernel data,
+		 *  so we try it repeatedly and let the resource manager
+		 *  test it.
+		 */
+		request_resource(res, &code_resource);
+		request_resource(res, &data_resource);
+		request_resource(res, &bss_resource);
+	}
+}
+
+void __init setup_arch(char **cmdline_p)
+{
+	cpu_probe();
+
+	early_init();
+	bootcmdline_init(cmdline_p);
+
+#ifdef CONFIG_ACPI
+	init_initrd();
+#endif
+	platform_init();
+
+	cpu_report();
+
+#if defined(CONFIG_VT)
+#if defined(CONFIG_VGA_CONSOLE)
+	conswitchp = &vga_con;
+#elif defined(CONFIG_DUMMY_CONSOLE)
+	conswitchp = &dummy_con;
+#endif
+#endif
+	arch_mem_init(cmdline_p);
+
+	resource_init();
+
+	cpu_cache_init();
+	paging_init();
+	boot_cpu_trap_init();
+}
+
+DEFINE_PER_CPU(unsigned long, kernelsp);
+unsigned long fw_arg0, fw_arg1, fw_arg2, fw_arg3;
+
+#ifdef CONFIG_DEBUG_FS
+struct dentry *loongarch_debugfs_dir;
+static int __init debugfs_loongarch(void)
+{
+	struct dentry *d;
+
+	d = debugfs_create_dir("loongarch", NULL);
+	if (!d)
+		return -ENOMEM;
+	loongarch_debugfs_dir = d;
+	return 0;
+}
+arch_initcall(debugfs_loongarch);
+#endif
diff --git a/arch/loongarch/kernel/time.c b/arch/loongarch/kernel/time.c
new file mode 100644
index 000000000000..2509a2e9fac3
--- /dev/null
+++ b/arch/loongarch/kernel/time.c
@@ -0,0 +1,227 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Common time service routines for LoongArch machines.
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/clockchips.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/sched_clock.h>
+#include <linux/spinlock.h>
+
+#include <asm/cpu-features.h>
+#include <asm/loongarchregs.h>
+#include <asm/time.h>
+
+u64 cpu_clock_freq;
+EXPORT_SYMBOL(cpu_clock_freq);
+u64 const_clock_freq;
+EXPORT_SYMBOL(const_clock_freq);
+
+static DEFINE_SPINLOCK(state_lock);
+DEFINE_PER_CPU(struct clock_event_device, constant_clockevent_device);
+
+void constant_event_handler(struct clock_event_device *dev)
+{
+}
+
+irqreturn_t constant_timer_interrupt(int irq, void *data)
+{
+	int cpu = smp_processor_id();
+	struct clock_event_device *cd;
+
+	/* Clear Timer Interrupt */
+	write_csr_tintclear(CSR_TINTCLR_TI);
+	cd = &per_cpu(constant_clockevent_device, cpu);
+	cd->event_handler(cd);
+
+	return IRQ_HANDLED;
+}
+
+static int constant_set_state_oneshot(struct clock_event_device *evt)
+{
+	unsigned long timer_config;
+
+	spin_lock(&state_lock);
+
+	timer_config = csr_readq(LOONGARCH_CSR_TCFG);
+	timer_config |=  CSR_TCFG_EN;
+	timer_config &= ~CSR_TCFG_PERIOD;
+	csr_writeq(timer_config, LOONGARCH_CSR_TCFG);
+
+	spin_unlock(&state_lock);
+
+	return 0;
+}
+
+static int constant_set_state_oneshot_stopped(struct clock_event_device *evt)
+{
+	return 0;
+}
+
+static int constant_set_state_periodic(struct clock_event_device *evt)
+{
+	unsigned long period;
+	unsigned long timer_config;
+
+	spin_lock(&state_lock);
+
+	period = const_clock_freq / HZ;
+	timer_config = period & CSR_TCFG_VAL;
+	timer_config |= (CSR_TCFG_PERIOD | CSR_TCFG_EN);
+	csr_writeq(timer_config, LOONGARCH_CSR_TCFG);
+
+	spin_unlock(&state_lock);
+
+	return 0;
+}
+
+static int constant_set_state_shutdown(struct clock_event_device *evt)
+{
+	return 0;
+}
+
+static int constant_timer_next_event(unsigned long delta, struct clock_event_device *evt)
+{
+	unsigned long timer_config;
+
+	delta &= CSR_TCFG_VAL;
+	timer_config = delta | CSR_TCFG_EN;
+	csr_writeq(timer_config, LOONGARCH_CSR_TCFG);
+
+	return 0;
+}
+
+static unsigned long __init get_loops_per_jiffy(void)
+{
+	unsigned long lpj = (unsigned long)const_clock_freq;
+
+	do_div(lpj, HZ);
+
+	return lpj;
+}
+
+static long init_timeval;
+
+void sync_counter(void)
+{
+	/* Ensure counter begin at 0 */
+	csr_writeq(-init_timeval, LOONGARCH_CSR_CNTC);
+}
+
+int constant_clockevent_init(void)
+{
+	unsigned int irq;
+	unsigned int cpu = smp_processor_id();
+	unsigned long min_delta = 0x600;
+	unsigned long max_delta = (1UL << 48) - 1;
+	struct clock_event_device *cd;
+	static int timer_irq_installed = 0;
+
+	irq = LOONGSON_TIMER_IRQ;
+
+	cd = &per_cpu(constant_clockevent_device, cpu);
+
+	cd->name = "Constant";
+	cd->features = CLOCK_EVT_FEAT_ONESHOT | CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_PERCPU;
+
+	cd->irq = irq;
+	cd->rating = 320;
+	cd->cpumask = cpumask_of(cpu);
+	cd->set_state_oneshot = constant_set_state_oneshot;
+	cd->set_state_oneshot_stopped = constant_set_state_oneshot_stopped;
+	cd->set_state_periodic = constant_set_state_periodic;
+	cd->set_state_shutdown = constant_set_state_shutdown;
+	cd->set_next_event = constant_timer_next_event;
+	cd->event_handler = constant_event_handler;
+
+	clockevents_config_and_register(cd, const_clock_freq, min_delta, max_delta);
+
+	if (timer_irq_installed)
+		return 0;
+
+	timer_irq_installed = 1;
+
+	sync_counter();
+
+	if (request_irq(irq, constant_timer_interrupt, IRQF_PERCPU | IRQF_TIMER, "timer", NULL))
+		pr_err("Failed to request irq %d (timer)\n", irq);
+
+	lpj_fine = get_loops_per_jiffy();
+	pr_info("Constant clock event device register\n");
+
+	return 0;
+}
+
+static u64 read_const_counter(struct clocksource *clk)
+{
+	return drdtime();
+}
+
+static struct clocksource clocksource_const = {
+	.name = "Constant",
+	.rating = 400,
+	.read = read_const_counter,
+	.mask = CLOCKSOURCE_MASK(64),
+	.flags = CLOCK_SOURCE_IS_CONTINUOUS,
+	.mult = 0,
+	.shift = 10,
+};
+
+unsigned long long notrace sched_clock(void)
+{
+	/* 64-bit arithmetic can overflow, so use 128-bit. */
+	u64 t1, t2, t3;
+	unsigned long long rv;
+	u64 mult = clocksource_const.mult;
+	u64 shift = clocksource_const.shift;
+	u64 cnt = read_const_counter(NULL);
+
+	__asm__ (
+		"nor		%[t1], $r0, %[shift]	\n\t"
+		"mulh.du	%[t2], %[cnt], %[mult]	\n\t"
+		"mul.d		%[t3], %[cnt], %[mult]	\n\t"
+		"slli.d		%[t2], %[t2], 1		\n\t"
+		"srl.d		%[rv], %[t3], %[shift]	\n\t"
+		"sll.d		%[t1], %[t2], %[t1]	\n\t"
+		"or		%[rv], %[t1], %[rv]	\n\t"
+		: [rv] "=&r" (rv), [t1] "=&r" (t1), [t2] "=&r" (t2), [t3] "=&r" (t3)
+		: [cnt] "r" (cnt), [mult] "r" (mult), [shift] "r" (shift)
+		: );
+
+	return rv;
+}
+
+int __init constant_clocksource_init(void)
+{
+	int res;
+	unsigned long freq;
+
+	freq = const_clock_freq;
+
+	clocksource_const.mult =
+		clocksource_hz2mult(freq, clocksource_const.shift);
+
+	res = clocksource_register_hz(&clocksource_const, freq);
+
+	pr_info("Constant clock source device register\n");
+
+	return res;
+}
+
+void __init time_init(void)
+{
+	if (!cpu_has_cpucfg)
+		const_clock_freq = cpu_clock_freq;
+	else
+		const_clock_freq = calc_const_freq();
+
+	init_timeval = drdtime() - csr_readq(LOONGARCH_CSR_CNTC);
+
+	constant_clockevent_init();
+	constant_clocksource_init();
+}
diff --git a/arch/loongarch/kernel/topology.c b/arch/loongarch/kernel/topology.c
new file mode 100644
index 000000000000..3b2cbb95875b
--- /dev/null
+++ b/arch/loongarch/kernel/topology.c
@@ -0,0 +1,13 @@
+// SPDX-License-Identifier: GPL-2.0
+#include <linux/cpu.h>
+#include <linux/init.h>
+#include <linux/percpu.h>
+
+static struct cpu cpu_device;
+
+static int __init topology_init(void)
+{
+	return register_cpu(&cpu_device, 0);
+}
+
+subsys_initcall(topology_init);
-- 
2.27.0


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

* [PATCH 06/19] LoongArch: Add exception/interrupt handling
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (3 preceding siblings ...)
  2021-07-06  4:18 ` [PATCH 05/19] LoongArch: Add boot and setup routines Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06 10:16   ` Arnd Bergmann
  2021-07-06 11:06   ` Peter Zijlstra
  2021-07-06  4:18 ` [PATCH 07/19] LoongArch: Add process management Huacai Chen
                   ` (15 subsequent siblings)
  20 siblings, 2 replies; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

This patch adds the exception and interrupt handling machanism for
LoongArch.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 arch/loongarch/include/asm/branch.h     |  21 +
 arch/loongarch/include/asm/break.h      |  10 +
 arch/loongarch/include/asm/bug.h        |  23 +
 arch/loongarch/include/asm/debug.h      |  18 +
 arch/loongarch/include/asm/hardirq.h    |  24 +
 arch/loongarch/include/asm/hw_irq.h     |  17 +
 arch/loongarch/include/asm/irq.h        |  53 ++
 arch/loongarch/include/asm/irq_regs.h   |  27 +
 arch/loongarch/include/asm/irqflags.h   |  52 ++
 arch/loongarch/include/asm/kdebug.h     |  23 +
 arch/loongarch/include/asm/stackframe.h | 240 ++++++++
 arch/loongarch/include/uapi/asm/break.h |  23 +
 arch/loongarch/kernel/access-helper.h   |  13 +
 arch/loongarch/kernel/entry.S           | 151 +++++
 arch/loongarch/kernel/genex.S           | 198 +++++++
 arch/loongarch/kernel/irq.c             |  99 ++++
 arch/loongarch/kernel/traps.c           | 717 ++++++++++++++++++++++++
 arch/loongarch/kernel/unaligned.c       | 461 +++++++++++++++
 18 files changed, 2170 insertions(+)
 create mode 100644 arch/loongarch/include/asm/branch.h
 create mode 100644 arch/loongarch/include/asm/break.h
 create mode 100644 arch/loongarch/include/asm/bug.h
 create mode 100644 arch/loongarch/include/asm/debug.h
 create mode 100644 arch/loongarch/include/asm/hardirq.h
 create mode 100644 arch/loongarch/include/asm/hw_irq.h
 create mode 100644 arch/loongarch/include/asm/irq.h
 create mode 100644 arch/loongarch/include/asm/irq_regs.h
 create mode 100644 arch/loongarch/include/asm/irqflags.h
 create mode 100644 arch/loongarch/include/asm/kdebug.h
 create mode 100644 arch/loongarch/include/asm/stackframe.h
 create mode 100644 arch/loongarch/include/uapi/asm/break.h
 create mode 100644 arch/loongarch/kernel/access-helper.h
 create mode 100644 arch/loongarch/kernel/entry.S
 create mode 100644 arch/loongarch/kernel/genex.S
 create mode 100644 arch/loongarch/kernel/irq.c
 create mode 100644 arch/loongarch/kernel/traps.c
 create mode 100644 arch/loongarch/kernel/unaligned.c

diff --git a/arch/loongarch/include/asm/branch.h b/arch/loongarch/include/asm/branch.h
new file mode 100644
index 000000000000..79121306625d
--- /dev/null
+++ b/arch/loongarch/include/asm/branch.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_BRANCH_H
+#define _ASM_BRANCH_H
+
+#include <asm/ptrace.h>
+
+static inline unsigned long exception_epc(struct pt_regs *regs)
+{
+	return regs->csr_epc;
+}
+
+static inline int compute_return_epc(struct pt_regs *regs)
+{
+	regs->csr_epc += 4;
+	return 0;
+}
+
+#endif /* _ASM_BRANCH_H */
diff --git a/arch/loongarch/include/asm/break.h b/arch/loongarch/include/asm/break.h
new file mode 100644
index 000000000000..109d0c85c582
--- /dev/null
+++ b/arch/loongarch/include/asm/break.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef __ASM_BREAK_H
+#define __ASM_BREAK_H
+
+#include <uapi/asm/break.h>
+
+#endif /* __ASM_BREAK_H */
diff --git a/arch/loongarch/include/asm/bug.h b/arch/loongarch/include/asm/bug.h
new file mode 100644
index 000000000000..bda49108a76d
--- /dev/null
+++ b/arch/loongarch/include/asm/bug.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_BUG_H
+#define __ASM_BUG_H
+
+#include <linux/compiler.h>
+
+#ifdef CONFIG_BUG
+
+#include <asm/break.h>
+
+static inline void __noreturn BUG(void)
+{
+	__asm__ __volatile__("break %0" : : "i" (BRK_BUG));
+	unreachable();
+}
+
+#define HAVE_ARCH_BUG
+
+#endif
+
+#include <asm-generic/bug.h>
+
+#endif /* __ASM_BUG_H */
diff --git a/arch/loongarch/include/asm/debug.h b/arch/loongarch/include/asm/debug.h
new file mode 100644
index 000000000000..426d5564304e
--- /dev/null
+++ b/arch/loongarch/include/asm/debug.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef __LOONGARCH_ASM_DEBUG_H__
+#define __LOONGARCH_ASM_DEBUG_H__
+
+#include <linux/dcache.h>
+
+/*
+ * loongarch_debugfs_dir corresponds to the "loongarch" directory at the top
+ * level of the DebugFS hierarchy. LoongArch-specific DebugFS entries should
+ * be placed beneath this directory.
+ */
+extern struct dentry *loongarch_debugfs_dir;
+
+#endif /* __LOONGARCH_ASM_DEBUG_H__ */
diff --git a/arch/loongarch/include/asm/hardirq.h b/arch/loongarch/include/asm/hardirq.h
new file mode 100644
index 000000000000..ccde14a45f67
--- /dev/null
+++ b/arch/loongarch/include/asm/hardirq.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_HARDIRQ_H
+#define _ASM_HARDIRQ_H
+
+#include <linux/cache.h>
+#include <linux/threads.h>
+#include <linux/irq.h>
+
+extern void ack_bad_irq(unsigned int irq);
+#define ack_bad_irq ack_bad_irq
+
+#define NR_IPI	2
+
+typedef struct {
+	unsigned int ipi_irqs[NR_IPI];
+	unsigned int __softirq_pending;
+} ____cacheline_aligned irq_cpustat_t;
+
+DECLARE_PER_CPU_ALIGNED(irq_cpustat_t, irq_stat);
+
+#endif /* _ASM_HARDIRQ_H */
diff --git a/arch/loongarch/include/asm/hw_irq.h b/arch/loongarch/include/asm/hw_irq.h
new file mode 100644
index 000000000000..53cccd8e02a0
--- /dev/null
+++ b/arch/loongarch/include/asm/hw_irq.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef __ASM_HW_IRQ_H
+#define __ASM_HW_IRQ_H
+
+#include <linux/atomic.h>
+
+extern atomic_t irq_err_count;
+
+/*
+ * interrupt-retrigger: NOP for now. This may not be appropriate for all
+ * machines, we'll see ...
+ */
+
+#endif /* __ASM_HW_IRQ_H */
diff --git a/arch/loongarch/include/asm/irq.h b/arch/loongarch/include/asm/irq.h
new file mode 100644
index 000000000000..01822ca77065
--- /dev/null
+++ b/arch/loongarch/include/asm/irq.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_IRQ_H
+#define _ASM_IRQ_H
+
+#include <linux/irqdomain.h>
+#include <irq.h>
+#include <asm-generic/irq.h>
+
+#define IRQ_STACK_SIZE			THREAD_SIZE
+#define IRQ_STACK_START			(IRQ_STACK_SIZE - 16)
+
+DECLARE_PER_CPU(unsigned long, irq_stack);
+
+/*
+ * The highest address on the IRQ stack contains a dummy frame put down in
+ * genex.S (except_vec_vi_handler) which is structured as follows:
+ *
+ *   top ------------
+ *       | task sp  | <- irq_stack[cpu] + IRQ_STACK_START
+ *       ------------
+ *       |          | <- First frame of IRQ context
+ *       ------------
+ *
+ * task sp holds a copy of the task stack pointer where the struct pt_regs
+ * from exception entry can be found.
+ */
+
+static inline bool on_irq_stack(int cpu, unsigned long sp)
+{
+	unsigned long low = per_cpu(irq_stack, cpu);
+	unsigned long high = low + IRQ_STACK_SIZE;
+
+	return (low <= sp && sp <= high);
+}
+
+struct irq_data;
+struct device_node;
+
+void arch_init_irq(void);
+void do_IRQ(unsigned int irq);
+void spurious_interrupt(void);
+int loongarch_cpu_irq_init(struct device_node *of_node, struct device_node *parent);
+
+#define NR_IRQS_LEGACY 16
+
+void arch_trigger_cpumask_backtrace(const struct cpumask *mask,
+					bool exclude_self);
+#define arch_trigger_cpumask_backtrace arch_trigger_cpumask_backtrace
+
+#endif /* _ASM_IRQ_H */
diff --git a/arch/loongarch/include/asm/irq_regs.h b/arch/loongarch/include/asm/irq_regs.h
new file mode 100644
index 000000000000..359a5bc4eb6b
--- /dev/null
+++ b/arch/loongarch/include/asm/irq_regs.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef __ASM_IRQ_REGS_H
+#define __ASM_IRQ_REGS_H
+
+#define ARCH_HAS_OWN_IRQ_REGS
+
+#include <linux/thread_info.h>
+
+static inline struct pt_regs *get_irq_regs(void)
+{
+	return current_thread_info()->regs;
+}
+
+static inline struct pt_regs *set_irq_regs(struct pt_regs *new_regs)
+{
+	struct pt_regs *old_regs;
+
+	old_regs = get_irq_regs();
+	current_thread_info()->regs = new_regs;
+
+	return old_regs;
+}
+
+#endif /* __ASM_IRQ_REGS_H */
diff --git a/arch/loongarch/include/asm/irqflags.h b/arch/loongarch/include/asm/irqflags.h
new file mode 100644
index 000000000000..7364fb66f217
--- /dev/null
+++ b/arch/loongarch/include/asm/irqflags.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_IRQFLAGS_H
+#define _ASM_IRQFLAGS_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/compiler.h>
+#include <linux/stringify.h>
+#include <asm/compiler.h>
+#include <asm/loongarchregs.h>
+
+static inline void arch_local_irq_enable(void)
+{
+	csr_xchgl(CSR_CRMD_IE, CSR_CRMD_IE, LOONGARCH_CSR_CRMD);
+}
+
+static inline void arch_local_irq_disable(void)
+{
+	csr_xchgl(0, CSR_CRMD_IE, LOONGARCH_CSR_CRMD);
+}
+
+static inline unsigned long arch_local_irq_save(void)
+{
+	return csr_xchgl(0, CSR_CRMD_IE, LOONGARCH_CSR_CRMD);
+}
+
+static inline void arch_local_irq_restore(unsigned long flags)
+{
+	csr_xchgl(flags, CSR_CRMD_IE, LOONGARCH_CSR_CRMD);
+}
+
+static inline unsigned long arch_local_save_flags(void)
+{
+	return csr_readl(LOONGARCH_CSR_CRMD);
+}
+
+static inline int arch_irqs_disabled_flags(unsigned long flags)
+{
+	return !(flags & CSR_CRMD_IE);
+}
+
+static inline int arch_irqs_disabled(void)
+{
+	return arch_irqs_disabled_flags(arch_local_save_flags());
+}
+
+#endif /* #ifndef __ASSEMBLY__ */
+
+#endif /* _ASM_IRQFLAGS_H */
diff --git a/arch/loongarch/include/asm/kdebug.h b/arch/loongarch/include/asm/kdebug.h
new file mode 100644
index 000000000000..beb1d9484e4e
--- /dev/null
+++ b/arch/loongarch/include/asm/kdebug.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_LOONGARCH_KDEBUG_H
+#define _ASM_LOONGARCH_KDEBUG_H
+
+#include <linux/notifier.h>
+
+enum die_val {
+	DIE_OOPS = 1,
+	DIE_RI,
+	DIE_FP,
+	DIE_SIMD,
+	DIE_TRAP,
+	DIE_PAGE_FAULT,
+	DIE_BREAK,
+	DIE_SSTEPBP,
+	DIE_UPROBE,
+	DIE_UPROBE_XOL,
+};
+
+#endif /* _ASM_LOONGARCH_KDEBUG_H */
diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h
new file mode 100644
index 000000000000..bf0a0ad263b1
--- /dev/null
+++ b/arch/loongarch/include/asm/stackframe.h
@@ -0,0 +1,240 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_STACKFRAME_H
+#define _ASM_STACKFRAME_H
+
+#include <linux/threads.h>
+
+#include <asm/asm.h>
+#include <asm/asmmacro.h>
+#include <asm/asm-offsets.h>
+#include <asm/loongarchregs.h>
+#include <asm/thread_info.h>
+
+/* Make the addition of cfi info a little easier. */
+	.macro cfi_rel_offset reg offset=0 docfi=0
+	.if \docfi
+	.cfi_rel_offset \reg, \offset
+	.endif
+	.endm
+
+	.macro cfi_st reg offset=0 docfi=0
+	cfi_rel_offset \reg, \offset, \docfi
+	LONG_S	\reg, sp, \offset
+	.endm
+
+	.macro cfi_restore reg offset=0 docfi=0
+	.if \docfi
+	.cfi_restore \reg
+	.endif
+	.endm
+
+	.macro cfi_ld reg offset=0 docfi=0
+	LONG_L	\reg, sp, \offset
+	cfi_restore \reg \offset \docfi
+	.endm
+
+	.macro BACKUP_T0T1
+	csrwr	t0, EXCEPTION_KS0
+	csrwr	t1, EXCEPTION_KS1
+	.endm
+
+	.macro RELOAD_T0T1
+	csrrd   t0, EXCEPTION_KS0
+	csrrd   t1, EXCEPTION_KS1
+	.endm
+
+	.macro	SAVE_TEMP docfi=0 reload=1
+	.if \reload
+	RELOAD_T0T1
+	.endif
+	cfi_st	t0, PT_R12, \docfi
+	cfi_st	t1, PT_R13, \docfi
+	cfi_st	t2, PT_R14, \docfi
+	cfi_st	t3, PT_R15, \docfi
+	cfi_st	t4, PT_R16, \docfi
+	cfi_st	t5, PT_R17, \docfi
+	cfi_st	t6, PT_R18, \docfi
+	cfi_st	t7, PT_R19, \docfi
+	cfi_st	t8, PT_R20, \docfi
+	.endm
+
+	.macro	SAVE_STATIC docfi=0
+	cfi_st	s0, PT_R23, \docfi
+	cfi_st	s1, PT_R24, \docfi
+	cfi_st	s2, PT_R25, \docfi
+	cfi_st	s3, PT_R26, \docfi
+	cfi_st	s4, PT_R27, \docfi
+	cfi_st	s5, PT_R28, \docfi
+	cfi_st	s6, PT_R29, \docfi
+	cfi_st	s7, PT_R30, \docfi
+	cfi_st	s8, PT_R31, \docfi
+	.endm
+
+/*
+ * get_saved_sp returns the SP for the current CPU by looking in the
+ * kernelsp array for it.  If tosp is set, it stores the current sp in
+ * t0 and loads the new value in sp.  If not, it clobbers t0 and
+ * stores the new value in t1, leaving sp unaffected.
+ */
+	.macro	get_saved_sp docfi=0 tosp=0
+	la.abs	t1, kernelsp
+	.if \tosp
+	move	t0, sp
+	.if \docfi
+	.cfi_register sp, t0
+	.endif
+	LONG_L	sp, t1, 0
+	.else
+	LONG_L	t1, t1, 0
+	.endif
+	.endm
+
+	.macro	set_saved_sp stackp temp temp2
+	la.abs	\temp, kernelsp
+	LONG_S	\stackp, \temp, 0
+	.endm
+
+	.macro	SAVE_SOME docfi=0
+	csrrd	t1, LOONGARCH_CSR_PRMD
+	andi	t1, t1, 0x3	/* extract pplv bit */
+	move	t0, sp
+	beqz	t1, 8f
+	/* Called from user mode, new stack. */
+	get_saved_sp docfi=\docfi tosp=1
+8:
+	PTR_ADDIU sp, sp, -PT_SIZE
+	.if \docfi
+	.cfi_def_cfa sp, 0
+	.endif
+	cfi_st	t0, PT_R3, \docfi
+	cfi_rel_offset  sp, PT_R3, \docfi
+	LONG_S	zero, sp, PT_R0
+	csrrd	t0, LOONGARCH_CSR_PRMD
+	LONG_S	t0, sp, PT_PRMD
+	csrrd	t0, LOONGARCH_CSR_CRMD
+	LONG_S	t0, sp, PT_CRMD
+	csrrd	t0, LOONGARCH_CSR_ECFG
+	LONG_S	t0, sp, PT_ECFG
+	csrrd	t0, LOONGARCH_CSR_EUEN
+	LONG_S  t0, sp, PT_EUEN
+	cfi_st	ra, PT_R1, \docfi
+	cfi_st	a0, PT_R4, \docfi
+	cfi_st	a1, PT_R5, \docfi
+	cfi_st	a2, PT_R6, \docfi
+	cfi_st	a3, PT_R7, \docfi
+	cfi_st	a4, PT_R8, \docfi
+	cfi_st	a5, PT_R9, \docfi
+	cfi_st	a6, PT_R10, \docfi
+	cfi_st	a7, PT_R11, \docfi
+	csrrd	ra, LOONGARCH_CSR_EPC
+	LONG_S	ra, sp, PT_EPC
+	.if \docfi
+	.cfi_rel_offset ra, PT_EPC
+	.endif
+	cfi_st	tp, PT_R2, \docfi
+	cfi_st	fp, PT_R22, \docfi
+
+	/* Set thread_info if we're coming from user mode */
+	csrrd	t0, LOONGARCH_CSR_PRMD
+	andi	t0, t0, 0x3	/* extract pplv bit */
+	beqz	t0, 9f
+
+	li.d	tp, ~_THREAD_MASK
+	and	tp, tp, sp
+	cfi_st  x0, PT_R21, \docfi
+9:
+	.endm
+
+	.macro	SAVE_ALL docfi=0
+	SAVE_SOME \docfi
+	SAVE_TEMP \docfi
+	SAVE_STATIC \docfi
+	.endm
+
+	.macro	RESTORE_TEMP docfi=0
+	cfi_ld	t0, PT_R12, \docfi
+	cfi_ld	t1, PT_R13, \docfi
+	cfi_ld	t2, PT_R14, \docfi
+	cfi_ld	t3, PT_R15, \docfi
+	cfi_ld	t4, PT_R16, \docfi
+	cfi_ld	t5, PT_R17, \docfi
+	cfi_ld	t6, PT_R18, \docfi
+	cfi_ld	t7, PT_R19, \docfi
+	cfi_ld	t8, PT_R20, \docfi
+	.endm
+
+	.macro	RESTORE_STATIC docfi=0
+	cfi_ld	s0, PT_R23, \docfi
+	cfi_ld	s1, PT_R24, \docfi
+	cfi_ld	s2, PT_R25, \docfi
+	cfi_ld	s3, PT_R26, \docfi
+	cfi_ld	s4, PT_R27, \docfi
+	cfi_ld	s5, PT_R28, \docfi
+	cfi_ld	s6, PT_R29, \docfi
+	cfi_ld	s7, PT_R30, \docfi
+	cfi_ld	s8, PT_R31, \docfi
+	.endm
+
+	.macro	RESTORE_SOME docfi=0
+	/* LoongArch clear IE and PLV */
+	LONG_L	v0, sp, PT_PRMD
+	csrwr	v0, LOONGARCH_CSR_PRMD
+	LONG_L	v0, sp, PT_EPC
+	csrwr	v0, LOONGARCH_CSR_EPC
+	andi    v0, v0, 0x3	/* extract pplv bit */
+	beqz    v0, 8f
+	cfi_ld  x0, PT_R21, \docfi
+8:
+	cfi_ld	ra, PT_R1, \docfi
+	cfi_ld	a0, PT_R4, \docfi
+	cfi_ld	a1, PT_R5, \docfi
+	cfi_ld	a2, PT_R6, \docfi
+	cfi_ld	a3, PT_R7, \docfi
+	cfi_ld	a4, PT_R8, \docfi
+	cfi_ld	a5, PT_R9, \docfi
+	cfi_ld	a6, PT_R10, \docfi
+	cfi_ld	a7, PT_R11, \docfi
+	cfi_ld	tp, PT_R2, \docfi
+	cfi_ld	fp, PT_R22, \docfi
+	.endm
+
+	.macro	RESTORE_SP docfi=0
+	cfi_ld	sp, PT_R3, \docfi
+	.endm
+
+	.macro	RESTORE_SP_AND_RET docfi=0
+	RESTORE_SP \docfi
+	ertn
+	.endm
+
+	.macro	RESTORE_ALL docfi=0
+	RESTORE_TEMP \docfi
+	RESTORE_STATIC \docfi
+	RESTORE_SOME \docfi
+	RESTORE_SP \docfi
+	.endm
+
+/* Move to kernel mode and disable interrupts. */
+	.macro	CLI
+	li.w	t0, 0x7
+	csrxchg	zero, t0, LOONGARCH_CSR_CRMD
+	csrrd	x0, PERCPU_BASE_KS
+	.endm
+
+/* Move to kernel mode and enable interrupts. */
+	.macro	STI
+	li.w	t0, 0x7
+	li.w	t1, (1 << 2)
+	csrxchg	t1, t0, LOONGARCH_CSR_CRMD
+	csrrd	x0, PERCPU_BASE_KS
+	.endm
+
+/* Just move to kernel mode and leave interrupts as they are. */
+	.macro	KMODE
+	csrrd	x0, PERCPU_BASE_KS
+	.endm
+
+#endif /* _ASM_STACKFRAME_H */
diff --git a/arch/loongarch/include/uapi/asm/break.h b/arch/loongarch/include/uapi/asm/break.h
new file mode 100644
index 000000000000..96e8dba56cc3
--- /dev/null
+++ b/arch/loongarch/include/uapi/asm/break.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef __UAPI_ASM_BREAK_H
+#define __UAPI_ASM_BREAK_H
+
+#define BRK_DEFAULT		0	/* Used as default */
+#define BRK_BUG			1	/* Used by BUG() */
+#define BRK_KDB			2	/* Used in KDB_ENTER() */
+#define BRK_MATHEMU		3	/* Used by FPU emulator */
+#define BRK_USERBP		4	/* User bp (used by debuggers) */
+#define BRK_SSTEPBP		5	/* User bp (used by debuggers) */
+#define BRK_OVERFLOW		6	/* Overflow check */
+#define BRK_DIVZERO		7	/* Divide by zero check */
+#define BRK_RANGE		8	/* Range error check */
+#define BRK_MULOVFL		9	/* Multiply overflow */
+#define BRK_KPROBE_BP		10	/* Kprobe break */
+#define BRK_KPROBE_SSTEPBP	11	/* Kprobe single step break */
+#define BRK_UPROBE_BP		12	/* See <asm/uprobes.h> */
+#define BRK_UPROBE_XOLBP	13	/* See <asm/uprobes.h> */
+
+#endif /* __UAPI_ASM_BREAK_H */
diff --git a/arch/loongarch/kernel/access-helper.h b/arch/loongarch/kernel/access-helper.h
new file mode 100644
index 000000000000..4a35ca81bd08
--- /dev/null
+++ b/arch/loongarch/kernel/access-helper.h
@@ -0,0 +1,13 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+
+#include <linux/uaccess.h>
+
+static inline int __get_inst(u32 *i, u32 *p, bool user)
+{
+	return user ? get_user(*i, (u32 __user *)p) : get_kernel_nofault(*i, p);
+}
+
+static inline int __get_addr(unsigned long *a, unsigned long *p, bool user)
+{
+	return user ? get_user(*a, (unsigned long __user *)p) : get_kernel_nofault(*a, p);
+}
diff --git a/arch/loongarch/kernel/entry.S b/arch/loongarch/kernel/entry.S
new file mode 100644
index 000000000000..caf56491c63e
--- /dev/null
+++ b/arch/loongarch/kernel/entry.S
@@ -0,0 +1,151 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <asm/asm.h>
+#include <asm/asmmacro.h>
+#include <asm/compiler.h>
+#include <asm/irqflags.h>
+#include <asm/regdef.h>
+#include <asm/loongarchregs.h>
+#include <asm/stackframe.h>
+#include <asm/thread_info.h>
+
+#ifndef CONFIG_PREEMPTION
+#define resume_kernel	restore_all
+#else
+#define __ret_from_irq	ret_from_exception
+#endif
+
+	.text
+	.align	5
+#ifndef CONFIG_PREEMPTION
+SYM_CODE_START(ret_from_exception)
+	local_irq_disable			# preempt stop
+	b	__ret_from_irq
+SYM_CODE_END(ret_from_exception)
+#endif
+SYM_CODE_START(ret_from_irq)
+	LONG_S	s0, tp, TI_REGS
+	b	__ret_from_irq
+SYM_CODE_END(ret_from_irq)
+
+SYM_CODE_START(__ret_from_irq)
+/*
+ * We can be coming here from a syscall done in the kernel space,
+ * e.g. a failed kernel_execve().
+ */
+resume_userspace_check:
+	LONG_L  t0, sp, PT_PRMD # returning to kernel mode?
+	andi    t0, t0, PLV_MASK
+	beqz	t0, resume_kernel
+
+resume_userspace:
+	local_irq_disable		# make sure we dont miss an
+					# interrupt setting need_resched
+					# between sampling and return
+	LONG_L	a2, tp, TI_FLAGS	# current->work
+	andi	t0, a2, _TIF_WORK_MASK	# (ignoring syscall_trace)
+	bnez	t0, work_pending
+	b	restore_all
+SYM_CODE_END(__ret_from_irq)
+
+#ifdef CONFIG_PREEMPTION
+resume_kernel:
+	local_irq_disable
+	ld.w	t0, tp, TI_PRE_COUNT
+	bnez	t0, restore_all
+need_resched:
+	LONG_L	t0, tp, TI_FLAGS
+	andi	t1, t0, _TIF_NEED_RESCHED
+	beqz	t1, restore_all
+
+	LONG_L  t0, sp, PT_PRMD		# Interrupts off?
+	andi	t0, t0, CSR_PRMD_PIE
+	beqz	t0, restore_all
+	bl	preempt_schedule_irq
+	b	need_resched
+#endif
+
+SYM_CODE_START(ret_from_kernel_thread)
+	bl	schedule_tail		# a0 = struct task_struct *prev
+	move	a0, s1
+	jirl	ra, s0, 0
+	b	syscall_exit
+SYM_CODE_END(ret_from_kernel_thread)
+
+SYM_CODE_START(ret_from_fork)
+	bl	schedule_tail		# a0 = struct task_struct *prev
+	b	syscall_exit
+SYM_CODE_END(ret_from_fork)
+
+SYM_CODE_START(syscall_exit)
+#ifdef CONFIG_DEBUG_RSEQ
+	move	a0, sp
+	bl	rseq_syscall
+#endif
+	local_irq_disable		# make sure need_resched and
+					# signals dont change between
+					# sampling and return
+	LONG_L	a2, tp, TI_FLAGS	# current->work
+	li.w	t0, _TIF_ALLWORK_MASK
+	and	t0, a2, t0
+	bnez	t0, syscall_exit_work
+
+restore_all:				# restore full frame
+	RESTORE_TEMP
+	RESTORE_STATIC
+restore_partial:		# restore partial frame
+	RESTORE_SOME
+	RESTORE_SP_AND_RET
+
+work_pending:
+	andi	t0, a2, _TIF_NEED_RESCHED # a2 is preloaded with TI_FLAGS
+	beqz	t0, work_notifysig
+work_resched:
+	bl	schedule
+
+	local_irq_disable		# make sure need_resched and
+					# signals dont change between
+					# sampling and return
+	LONG_L	a2, tp, TI_FLAGS
+	andi	t0, a2, _TIF_WORK_MASK	# is there any work to be done
+					# other than syscall tracing?
+	beqz	t0, restore_all
+	andi	t0, a2, _TIF_NEED_RESCHED
+	bnez	t0, work_resched
+
+work_notifysig:				# deal with pending signals and
+					# notify-resume requests
+	move	a0, sp
+	li.w	a1, 0
+	bl	do_notify_resume	# a2 already loaded
+	b	resume_userspace_check
+SYM_CODE_END(syscall_exit)
+
+SYM_CODE_START(syscall_exit_partial)
+#ifdef CONFIG_DEBUG_RSEQ
+	move	a0, sp
+	bl	rseq_syscall
+#endif
+	local_irq_disable		# make sure need_resched doesn't
+					# change between and return
+	LONG_L	a2, tp, TI_FLAGS	# current->work
+	li.w	t0, _TIF_ALLWORK_MASK
+	and	t0, t0, a2
+	beqz	t0, restore_partial
+	SAVE_STATIC
+syscall_exit_work:
+	LONG_L	t0, sp, PT_PRMD			# returning to kernel mode?
+	andi	t0, t0, PLV_MASK
+	beqz	t0, resume_kernel
+	li.w	t0, _TIF_WORK_SYSCALL_EXIT
+	and	t0, t0, a2			# a2 is preloaded with TI_FLAGS
+	beqz	t0, work_pending	# trace bit set?
+	local_irq_enable		# could let syscall_trace_leave()
+					# call schedule() instead
+	move	a0, sp
+	bl	syscall_trace_leave
+	b	resume_userspace
+SYM_CODE_END(syscall_exit_partial)
diff --git a/arch/loongarch/kernel/genex.S b/arch/loongarch/kernel/genex.S
new file mode 100644
index 000000000000..7f146339e552
--- /dev/null
+++ b/arch/loongarch/kernel/genex.S
@@ -0,0 +1,198 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/init.h>
+
+#include <asm/asm.h>
+#include <asm/asmmacro.h>
+#include <asm/cacheops.h>
+#include <asm/irqflags.h>
+#include <asm/regdef.h>
+#include <asm/fpregdef.h>
+#include <asm/loongarchregs.h>
+#include <asm/stackframe.h>
+#include <asm/thread_info.h>
+
+	.align	5	/* 32 byte rollback region */
+SYM_FUNC_START(__arch_cpu_idle)
+	/* start of rollback region */
+	LONG_L	t0, tp, TI_FLAGS
+	nop
+	andi	t0, t0, _TIF_NEED_RESCHED
+	bnez	t0, 1f
+	nop
+	nop
+	nop
+	idle	0
+	/* end of rollback region */
+1:
+	jirl	zero, ra, 0
+SYM_FUNC_END(__arch_cpu_idle)
+
+SYM_FUNC_START(except_vec_cex)
+	b	cache_parity_error
+	nop
+SYM_FUNC_END(except_vec_cex)
+
+	.macro	__build_clear_none
+	.endm
+
+	.macro	__build_clear_sti
+	STI
+	.endm
+
+	.macro	__build_clear_cli
+	CLI
+	.endm
+
+	.macro	__build_clear_fpe
+	movfcsr2gr	a1, fcsr0
+	CLI
+	.endm
+
+	.macro	__build_clear_ade
+	csrrd	t0, LOONGARCH_CSR_BADV
+	PTR_S	t0, sp, PT_BVADDR
+	KMODE
+	.endm
+
+	.macro	__build_clear_ale
+	csrrd	t0, LOONGARCH_CSR_BADV
+	PTR_S	t0, sp, PT_BVADDR
+	KMODE
+	.endm
+
+	.macro	__BUILD_silent exception
+	.endm
+
+	.macro	__BUILD_verbose nexception
+	LONG_L	a1, sp, PT_EPC
+	ASM_PRINT("Got \nexception at %016lx\012")
+	.endm
+
+	.macro	__BUILD_HANDLER exception handler clear verbose ext
+	.align	5
+	SYM_FUNC_START(handle_\exception)
+	BACKUP_T0T1
+	SAVE_ALL
+	SYM_INNER_LABEL(handle_\exception\ext, SYM_L_GLOBAL)
+	__build_clear_\clear
+	__BUILD_\verbose \exception
+	move	a0, sp
+	la.abs	t0, do_\handler
+	jirl    ra, t0, 0
+	la.abs	t0, ret_from_exception
+	jirl    zero, t0, 0
+	SYM_FUNC_END(handle_\exception)
+	.endm
+
+	.macro	BUILD_HANDLER exception handler clear verbose
+	__BUILD_HANDLER \exception \handler \clear \verbose _int
+	.endm
+
+	BUILD_HANDLER ade ade ade silent
+	BUILD_HANDLER ale ale ale silent
+	BUILD_HANDLER bp bp sti silent
+	BUILD_HANDLER ri ri sti silent
+	BUILD_HANDLER fpu fpu sti silent
+	BUILD_HANDLER fpe fpe fpe silent
+	BUILD_HANDLER lsx lsx sti silent
+	BUILD_HANDLER lasx lasx sti silent
+	BUILD_HANDLER lbt lbt sti silent
+	BUILD_HANDLER watch watch cli silent
+	BUILD_HANDLER reserved reserved sti verbose	/* others */
+
+SYM_FUNC_START(handle_syscall)
+	la.abs	t0, handle_sys
+	jirl    zero, t0, 0
+SYM_FUNC_END(handle_syscall)
+
+/*
+ * Common Vectored Interrupt
+ * Complete the register saves and invoke the do_vi() handler
+ */
+SYM_FUNC_START(except_vec_vi_handler)
+	la	t1, __arch_cpu_idle
+	LONG_L  t0, sp, PT_EPC
+	/* 32 byte rollback region */
+	ori	t0, t0, 0x1f
+	xori	t0, t0, 0x1f
+	bne	t0, t1, 1f
+	LONG_S  t0, sp, PT_EPC
+1:	SAVE_TEMP
+	SAVE_STATIC
+	CLI
+
+	LONG_L		s0, tp, TI_REGS
+	LONG_S		sp, tp, TI_REGS
+
+	move		s1, sp /* Preserve sp */
+
+	/* Get IRQ stack for this CPU */
+	la		t1, irq_stack
+	LONG_ADDU	t1, t1, x0
+	LONG_L		t0, t1, 0
+
+	/* Check if already on IRQ stack */
+	PTR_LI		t1, ~(_THREAD_SIZE-1)
+	and		t1, t1, sp
+	beq		t0, t1, 2f
+
+	/* Switch to IRQ stack */
+	li.w		t1, _IRQ_STACK_START
+	PTR_ADDU	sp, t0, t1
+
+	/* Save task's sp on IRQ stack so that unwinding can follow it */
+	LONG_S		s1, sp, 0
+2:	la		t0, do_vi
+	jirl		ra, t0, 0
+
+	move		sp, s1 /* Restore sp */
+	la		t0, ret_from_irq
+	jirl    	zero, t0, 0
+SYM_FUNC_END(except_vec_vi_handler)
+
+	.macro	BUILD_VI_HANDLER num
+	.align	5
+SYM_FUNC_START(handle_vi_\num)
+	BACKUP_T0T1
+	SAVE_SOME
+	addi.d	v0, zero, \num
+	la.abs	v1, except_vec_vi_handler
+	jirl	zero, v1, 0
+SYM_FUNC_END(handle_vi_\num)
+	.endm
+
+	BUILD_VI_HANDLER 0
+	BUILD_VI_HANDLER 1
+	BUILD_VI_HANDLER 2
+	BUILD_VI_HANDLER 3
+	BUILD_VI_HANDLER 4
+	BUILD_VI_HANDLER 5
+	BUILD_VI_HANDLER 6
+	BUILD_VI_HANDLER 7
+	BUILD_VI_HANDLER 8
+	BUILD_VI_HANDLER 9
+	BUILD_VI_HANDLER 10
+	BUILD_VI_HANDLER 11
+	BUILD_VI_HANDLER 12
+	BUILD_VI_HANDLER 13
+
+	.align	3
+SYM_DATA_START(vi_table)
+	PTR	handle_vi_0
+	PTR	handle_vi_1
+	PTR	handle_vi_2
+	PTR	handle_vi_3
+	PTR	handle_vi_4
+	PTR	handle_vi_5
+	PTR	handle_vi_6
+	PTR	handle_vi_7
+	PTR	handle_vi_8
+	PTR	handle_vi_9
+	PTR	handle_vi_10
+	PTR	handle_vi_11
+	PTR	handle_vi_12
+	PTR	handle_vi_13
+SYM_DATA_END(vi_table)
diff --git a/arch/loongarch/kernel/irq.c b/arch/loongarch/kernel/irq.c
new file mode 100644
index 000000000000..e0912976ef31
--- /dev/null
+++ b/arch/loongarch/kernel/irq.c
@@ -0,0 +1,99 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <linux/proc_fs.h>
+#include <linux/mm.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <linux/kallsyms.h>
+#include <linux/kgdb.h>
+#include <linux/ftrace.h>
+
+#include <linux/atomic.h>
+#include <linux/uaccess.h>
+
+DEFINE_PER_CPU(unsigned long, irq_stack);
+
+/*
+ * 'what should we do if we get a hw irq event on an illegal vector'.
+ * each architecture has to answer this themselves.
+ */
+void ack_bad_irq(unsigned int irq)
+{
+	pr_warn("Unexpected IRQ # %d\n", irq);
+}
+
+atomic_t irq_err_count;
+
+int arch_show_interrupts(struct seq_file *p, int prec)
+{
+	seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
+	return 0;
+}
+
+asmlinkage void spurious_interrupt(void)
+{
+	atomic_inc(&irq_err_count);
+}
+
+void __init init_IRQ(void)
+{
+	int i;
+	unsigned int order = get_order(IRQ_STACK_SIZE);
+
+	for (i = 0; i < NR_IRQS; i++)
+		irq_set_noprobe(i);
+
+	arch_init_irq();
+
+	for_each_possible_cpu(i) {
+		void *s = (void *)__get_free_pages(GFP_KERNEL, order);
+
+		per_cpu(irq_stack, i) = (unsigned long)s;
+		pr_debug("CPU%d IRQ stack at 0x%lx - 0x%lx\n", i,
+			per_cpu(irq_stack, i), per_cpu(irq_stack, i) + IRQ_STACK_SIZE);
+	}
+}
+
+#ifdef CONFIG_DEBUG_STACKOVERFLOW
+static inline void check_stack_overflow(void)
+{
+	unsigned long sp;
+
+	__asm__ __volatile__("move %0, $sp" : "=r" (sp));
+	sp &= THREAD_MASK;
+
+	/*
+	 * Check for stack overflow: is there less than STACK_WARN free?
+	 * STACK_WARN is defined as 1/8 of THREAD_SIZE by default.
+	 */
+	if (unlikely(sp < (sizeof(struct thread_info) + STACK_WARN))) {
+		pr_warn("do_IRQ: stack overflow: %ld\n",
+			sp - sizeof(struct thread_info));
+		dump_stack();
+	}
+}
+#else
+static inline void check_stack_overflow(void) {}
+#endif
+
+
+/*
+ * do_IRQ handles all normal device IRQ's (the special
+ * SMP cross-CPU interrupts have their own specific
+ * handlers).
+ */
+void __irq_entry do_IRQ(unsigned int irq)
+{
+	irq_enter();
+	check_stack_overflow();
+	generic_handle_irq(irq);
+	irq_exit();
+}
diff --git a/arch/loongarch/kernel/traps.c b/arch/loongarch/kernel/traps.c
new file mode 100644
index 000000000000..d180eacaaefa
--- /dev/null
+++ b/arch/loongarch/kernel/traps.c
@@ -0,0 +1,717 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/bitops.h>
+#include <linux/bug.h>
+#include <linux/compiler.h>
+#include <linux/context_tracking.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/extable.h>
+#include <linux/mm.h>
+#include <linux/sched/mm.h>
+#include <linux/sched/debug.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/kallsyms.h>
+#include <linux/memblock.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/kgdb.h>
+#include <linux/kdebug.h>
+#include <linux/kprobes.h>
+#include <linux/notifier.h>
+#include <linux/kdb.h>
+#include <linux/irq.h>
+#include <linux/perf_event.h>
+
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+#include <asm/branch.h>
+#include <asm/break.h>
+#include <asm/cpu.h>
+#include <asm/fpu.h>
+#include <asm/loongarchregs.h>
+#include <asm/pgtable.h>
+#include <asm/ptrace.h>
+#include <asm/sections.h>
+#include <asm/siginfo.h>
+#include <asm/tlb.h>
+#include <asm/mmu_context.h>
+#include <asm/types.h>
+
+#include "access-helper.h"
+
+extern asmlinkage void handle_ade(void);
+extern asmlinkage void handle_ale(void);
+extern asmlinkage void handle_sys(void);
+extern asmlinkage void handle_syscall(void);
+extern asmlinkage void handle_bp(void);
+extern asmlinkage void handle_ri(void);
+extern asmlinkage void handle_fpu(void);
+extern asmlinkage void handle_fpe(void);
+extern asmlinkage void handle_lbt(void);
+extern asmlinkage void handle_lsx(void);
+extern asmlinkage void handle_lasx(void);
+extern asmlinkage void handle_reserved(void);
+extern asmlinkage void handle_watch(void);
+
+extern void *vi_table[];
+static vi_handler_t ip_handlers[EXCCODE_INT_NUM];
+
+static void show_backtrace(struct task_struct *task, const struct pt_regs *regs,
+			   const char *loglvl)
+{
+	unsigned long addr;
+	unsigned long *sp = (unsigned long *)(regs->regs[3] & ~3);
+
+	printk("%sCall Trace:", loglvl);
+#ifdef CONFIG_KALLSYMS
+	printk("%s\n", loglvl);
+#endif
+	while (!kstack_end(sp)) {
+		unsigned long __user *p =
+			(unsigned long __user *)(unsigned long)sp++;
+		if (__get_user(addr, p)) {
+			printk("%s (Bad stack address)", loglvl);
+			break;
+		}
+		if (__kernel_text_address(addr))
+			print_ip_sym(loglvl, addr);
+	}
+	printk("%s\n", loglvl);
+}
+
+static void show_stacktrace(struct task_struct *task,
+	const struct pt_regs *regs, const char *loglvl, bool user)
+{
+	int i;
+	const int field = 2 * sizeof(unsigned long);
+	unsigned long stackdata;
+	unsigned long *sp = (unsigned long *)regs->regs[3];
+
+	printk("%sStack :", loglvl);
+	i = 0;
+	while ((unsigned long) sp & (PAGE_SIZE - 1)) {
+		if (i && ((i % (64 / field)) == 0)) {
+			pr_cont("\n");
+			printk("%s       ", loglvl);
+		}
+		if (i > 39) {
+			pr_cont(" ...");
+			break;
+		}
+
+		if (__get_addr(&stackdata, sp++, user)) {
+			pr_cont(" (Bad stack address)");
+			break;
+		}
+
+		pr_cont(" %0*lx", field, stackdata);
+		i++;
+	}
+	pr_cont("\n");
+	show_backtrace(task, regs, loglvl);
+}
+
+void show_stack(struct task_struct *task, unsigned long *sp, const char *loglvl)
+{
+	struct pt_regs regs;
+
+	regs.csr_crmd = 0;
+	if (sp) {
+		regs.regs[3] = (unsigned long)sp;
+		regs.regs[1] = 0;
+		regs.csr_epc = 0;
+	} else {
+		if (task && task != current) {
+			regs.regs[3] = task->thread.reg03;
+			regs.regs[1] = 0;
+			regs.csr_epc = task->thread.reg01;
+		} else {
+			memset(&regs, 0, sizeof(regs));
+		}
+	}
+
+	show_stacktrace(task, &regs, loglvl, false);
+}
+
+static void show_code(void *pc, bool user)
+{
+	long i;
+	unsigned int insn;
+
+	printk("Code:");
+
+	for(i = -3 ; i < 6 ; i++) {
+		if (__get_inst(&insn, pc + i, user)) {
+			pr_cont(" (Bad address in epc)\n");
+			break;
+		}
+		pr_cont("%c%08x%c", (i?' ':'<'), insn, (i?' ':'>'));
+	}
+	pr_cont("\n");
+}
+
+static void __show_regs(const struct pt_regs *regs)
+{
+	const int field = 2 * sizeof(unsigned long);
+	unsigned int excsubcode;
+	unsigned int exccode;
+	int i;
+
+	show_regs_print_info(KERN_DEFAULT);
+
+	/*
+	 * Saved main processor registers
+	 */
+	for (i = 0; i < 32; ) {
+		if ((i % 4) == 0)
+			printk("$%2d   :", i);
+		pr_cont(" %0*lx", field, regs->regs[i]);
+
+		i++;
+		if ((i % 4) == 0)
+			pr_cont("\n");
+	}
+
+	/*
+	 * Saved csr registers
+	 */
+	printk("epc   : %0*lx %pS\n", field, regs->csr_epc,
+	       (void *) regs->csr_epc);
+	printk("ra    : %0*lx %pS\n", field, regs->regs[1],
+	       (void *) regs->regs[1]);
+
+	printk("CSR crmd: %08lx	", regs->csr_crmd);
+	printk("CSR prmd: %08lx	", regs->csr_prmd);
+	printk("CSR ecfg: %08lx	", regs->csr_ecfg);
+	printk("CSR estat: %08lx	", regs->csr_estat);
+	printk("CSR euen: %08lx	", regs->csr_euen);
+
+	pr_cont("\n");
+
+	exccode = ((regs->csr_estat) & CSR_ESTAT_EXC) >> CSR_ESTAT_EXC_SHIFT;
+	excsubcode = ((regs->csr_estat) & CSR_ESTAT_ESUBCODE) >> CSR_ESTAT_ESUBCODE_SHIFT;
+	printk("ExcCode : %x (SubCode %x)\n", exccode, excsubcode);
+
+	if (exccode >= EXCCODE_TLBL && exccode <= EXCCODE_ALE)
+		printk("BadVA : %0*lx\n", field, regs->csr_badvaddr);
+
+	printk("PrId  : %08x (%s)\n", read_cpucfg(LOONGARCH_CPUCFG0),
+	       cpu_family_string());
+}
+
+void show_regs(struct pt_regs *regs)
+{
+	__show_regs((struct pt_regs *)regs);
+	dump_stack();
+}
+
+void show_registers(struct pt_regs *regs)
+{
+	__show_regs(regs);
+	print_modules();
+	printk("Process %s (pid: %d, threadinfo=%p, task=%p)\n",
+	       current->comm, current->pid, current_thread_info(), current);
+
+	show_stacktrace(current, regs, KERN_DEFAULT, user_mode(regs));
+	show_code((void *)regs->csr_epc, user_mode(regs));
+	printk("\n");
+}
+
+static DEFINE_RAW_SPINLOCK(die_lock);
+
+void __noreturn die(const char *str, struct pt_regs *regs)
+{
+	static int die_counter;
+	int sig = SIGSEGV;
+
+	oops_enter();
+
+	if (notify_die(DIE_OOPS, str, regs, 0, current->thread.trap_nr,
+		       SIGSEGV) == NOTIFY_STOP)
+		sig = 0;
+
+	console_verbose();
+	raw_spin_lock_irq(&die_lock);
+	bust_spinlocks(1);
+
+	printk("%s[#%d]:\n", str, ++die_counter);
+	show_registers(regs);
+	add_taint(TAINT_DIE, LOCKDEP_NOW_UNRELIABLE);
+	raw_spin_unlock_irq(&die_lock);
+
+	oops_exit();
+
+	if (in_interrupt())
+		panic("Fatal exception in interrupt");
+
+	if (panic_on_oops)
+		panic("Fatal exception");
+
+	do_exit(sig);
+}
+
+static inline void setup_vint_size(unsigned int size)
+{
+	unsigned int vs;
+
+	vs = ilog2(size/4);
+
+	if (vs == 0 || vs > 7)
+		panic("vint_size %d Not support yet", vs);
+
+	csr_xchgl(vs<<CSR_ECFG_VS_SHIFT, CSR_ECFG_VS, LOONGARCH_CSR_ECFG);
+}
+
+/*
+ * Send SIGFPE according to FCSR Cause bits, which must have already
+ * been masked against Enable bits.  This is impotant as Inexact can
+ * happen together with Overflow or Underflow, and `ptrace' can set
+ * any bits.
+ */
+void force_fcsr_sig(unsigned long fcsr, void __user *fault_addr,
+		     struct task_struct *tsk)
+{
+	int si_code = FPE_FLTUNK;
+
+	if (fcsr & FPU_CSR_INV_X)
+		si_code = FPE_FLTINV;
+	else if (fcsr & FPU_CSR_DIV_X)
+		si_code = FPE_FLTDIV;
+	else if (fcsr & FPU_CSR_OVF_X)
+		si_code = FPE_FLTOVF;
+	else if (fcsr & FPU_CSR_UDF_X)
+		si_code = FPE_FLTUND;
+	else if (fcsr & FPU_CSR_INE_X)
+		si_code = FPE_FLTRES;
+
+	force_sig_fault(SIGFPE, si_code, fault_addr);
+}
+
+int process_fpemu_return(int sig, void __user *fault_addr, unsigned long fcsr)
+{
+	int si_code;
+	struct vm_area_struct *vma;
+
+	switch (sig) {
+	case 0:
+		return 0;
+
+	case SIGFPE:
+		force_fcsr_sig(fcsr, fault_addr, current);
+		return 1;
+
+	case SIGBUS:
+		force_sig_fault(SIGBUS, BUS_ADRERR, fault_addr);
+		return 1;
+
+	case SIGSEGV:
+		mmap_read_lock(current->mm);
+		vma = find_vma(current->mm, (unsigned long)fault_addr);
+		if (vma && (vma->vm_start <= (unsigned long)fault_addr))
+			si_code = SEGV_ACCERR;
+		else
+			si_code = SEGV_MAPERR;
+		mmap_read_unlock(current->mm);
+		force_sig_fault(SIGSEGV, si_code, fault_addr);
+		return 1;
+
+	default:
+		force_sig(sig);
+		return 1;
+	}
+}
+
+/*
+ * Delayed fp exceptions when doing a lazy ctx switch
+ */
+asmlinkage void do_fpe(struct pt_regs *regs, unsigned long fcsr)
+{
+	enum ctx_state prev_state;
+	void __user *fault_addr;
+	int sig;
+
+	prev_state = exception_enter();
+	if (notify_die(DIE_FP, "FP exception", regs, 0, current->thread.trap_nr,
+		       SIGFPE) == NOTIFY_STOP)
+		goto out;
+
+	/* Clear FCSR.Cause before enabling interrupts */
+	write_fcsr(LOONGARCH_FCSR0, fcsr & ~mask_fcsr_x(fcsr));
+	local_irq_enable();
+
+	die_if_kernel("FP exception in kernel code", regs);
+
+	sig = SIGFPE;
+	fault_addr = (void __user *) regs->csr_epc;
+
+	/* Send a signal if required.  */
+	process_fpemu_return(sig, fault_addr, fcsr);
+
+out:
+	exception_exit(prev_state);
+}
+
+asmlinkage void do_bp(struct pt_regs *regs)
+{
+	bool user = user_mode(regs);
+	unsigned int opcode, bcode;
+	unsigned long epc = exception_epc(regs);
+	enum ctx_state prev_state;
+
+	prev_state = exception_enter();
+	current->thread.trap_nr = read_csr_excode();
+	if (__get_inst(&opcode, (u32 *)epc, user))
+		goto out_sigsegv;
+
+	bcode = (opcode & 0x7fff);
+
+	/*
+	 * notify the kprobe handlers, if instruction is likely to
+	 * pertain to them.
+	 */
+	switch (bcode) {
+	case BRK_KPROBE_BP:
+		if (notify_die(DIE_BREAK, "Kprobe", regs, bcode,
+			       current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP)
+			goto out;
+		else
+			break;
+	case BRK_KPROBE_SSTEPBP:
+		if (notify_die(DIE_SSTEPBP, "Kprobe_SingleStep", regs, bcode,
+			       current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP)
+			goto out;
+		else
+			break;
+	case BRK_UPROBE_BP:
+		if (notify_die(DIE_UPROBE, "Uprobe", regs, bcode,
+			       current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP)
+			goto out;
+		else
+			break;
+	case BRK_UPROBE_XOLBP:
+		if (notify_die(DIE_UPROBE_XOL, "Uprobe_XOL", regs, bcode,
+			       current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP)
+			goto out;
+		else
+			break;
+	default:
+		if (notify_die(DIE_TRAP, "Break", regs, bcode,
+			       current->thread.trap_nr, SIGTRAP) == NOTIFY_STOP)
+			goto out;
+		else
+			break;
+	}
+
+	switch (bcode) {
+	case BRK_BUG:
+		die_if_kernel("Kernel bug detected", regs);
+		force_sig(SIGTRAP);
+		break;
+	case BRK_DIVZERO:
+		die_if_kernel("Break instruction in kernel code", regs);
+		force_sig_fault(SIGFPE, FPE_INTDIV, (void __user *)regs->csr_epc);
+		break;
+	case BRK_OVERFLOW:
+		die_if_kernel("Break instruction in kernel code", regs);
+		force_sig_fault(SIGFPE, FPE_INTOVF, (void __user *)regs->csr_epc);
+		break;
+	default:
+		die_if_kernel("Break instruction in kernel code", regs);
+		force_sig_fault(SIGTRAP, TRAP_BRKPT, (void __user *)regs->csr_epc);
+		break;
+	}
+
+out:
+	exception_exit(prev_state);
+	return;
+
+out_sigsegv:
+	force_sig(SIGSEGV);
+	goto out;
+}
+
+asmlinkage void do_watch(struct pt_regs *regs)
+{
+}
+
+asmlinkage void do_ri(struct pt_regs *regs)
+{
+	unsigned int __user *epc = (unsigned int __user *)exception_epc(regs);
+	unsigned long old_epc = regs->csr_epc;
+	unsigned long old_ra = regs->regs[1];
+	enum ctx_state prev_state;
+	unsigned int opcode = 0;
+	int status = -1;
+
+	prev_state = exception_enter();
+	current->thread.trap_nr = read_csr_excode();
+
+	if (notify_die(DIE_RI, "RI Fault", regs, 0, current->thread.trap_nr,
+		       SIGILL) == NOTIFY_STOP)
+		goto out;
+
+	die_if_kernel("Reserved instruction in kernel code", regs);
+
+	if (unlikely(compute_return_epc(regs) < 0))
+		goto out;
+
+	if (unlikely(get_user(opcode, epc) < 0))
+		status = SIGSEGV;
+
+	if (status < 0)
+		status = SIGILL;
+
+	if (unlikely(status > 0)) {
+		regs->csr_epc = old_epc;		/* Undo skip-over.  */
+		regs->regs[1] = old_ra;
+		force_sig(status);
+	}
+
+out:
+	exception_exit(prev_state);
+}
+
+static void init_restore_fp(void)
+{
+	if (!used_math()) {
+		/* First time FP context user. */
+		init_fpu();
+	} else {
+		/* This task has formerly used the FP context */
+		if (!is_fpu_owner())
+			own_fpu_inatomic(1);
+	}
+
+	BUG_ON(!is_fp_enabled());
+}
+
+asmlinkage void do_fpu(struct pt_regs *regs)
+{
+	enum ctx_state prev_state;
+
+	prev_state = exception_enter();
+
+	die_if_kernel("do_fpu invoked from kernel context!", regs);
+
+	preempt_disable();
+	init_restore_fp();
+	preempt_enable();
+
+	exception_exit(prev_state);
+}
+
+asmlinkage void do_lsx(struct pt_regs *regs)
+{
+}
+
+asmlinkage void do_lasx(struct pt_regs *regs)
+{
+}
+
+asmlinkage void do_lbt(struct pt_regs *regs)
+{
+}
+
+asmlinkage void do_reserved(struct pt_regs *regs)
+{
+	/*
+	 * Game over - no way to handle this if it ever occurs.	 Most probably
+	 * caused by a new unknown cpu type or after another deadly
+	 * hard/software error.
+	 */
+	show_regs(regs);
+	panic("Caught reserved exception %u - should not happen.", read_csr_excode());
+}
+
+asmlinkage void cache_parity_error(void)
+{
+	const int field = 2 * sizeof(unsigned long);
+	unsigned int reg_val;
+
+	/* For the moment, report the problem and hang. */
+	pr_err("Cache error exception:\n");
+	pr_err("csr_merrepc == %0*llx\n", field, csr_readq(LOONGARCH_CSR_MERREPC));
+	reg_val = csr_readl(LOONGARCH_CSR_MERRCTL);
+	pr_err("csr_merrctl == %08x\n", reg_val);
+
+	pr_err("Decoded c0_cacheerr: %s cache fault in %s reference.\n",
+	       reg_val & (1<<30) ? "secondary" : "primary",
+	       reg_val & (1<<31) ? "data" : "insn");
+	if (((current_cpu_data.processor_id & 0xff0000) == PRID_COMP_LOONGSON)) {
+		pr_err("Error bits: %s%s%s%s%s%s%s%s\n",
+			reg_val & (1<<29) ? "ED " : "",
+			reg_val & (1<<28) ? "ET " : "",
+			reg_val & (1<<27) ? "ES " : "",
+			reg_val & (1<<26) ? "EE " : "",
+			reg_val & (1<<25) ? "EB " : "",
+			reg_val & (1<<24) ? "EI " : "",
+			reg_val & (1<<23) ? "E1 " : "",
+			reg_val & (1<<22) ? "E0 " : "");
+	} else {
+		pr_err("Error bits: %s%s%s%s%s%s%s\n",
+			reg_val & (1<<29) ? "ED " : "",
+			reg_val & (1<<28) ? "ET " : "",
+			reg_val & (1<<26) ? "EE " : "",
+			reg_val & (1<<25) ? "EB " : "",
+			reg_val & (1<<24) ? "EI " : "",
+			reg_val & (1<<23) ? "E1 " : "",
+			reg_val & (1<<22) ? "E0 " : "");
+	}
+	pr_err("IDX: 0x%08x\n", reg_val & ((1<<22)-1));
+
+	panic("Can't handle the cache error!");
+}
+
+unsigned long eentry;
+EXPORT_SYMBOL_GPL(eentry);
+unsigned long tlbrentry;
+EXPORT_SYMBOL_GPL(tlbrentry);
+unsigned long exception_handlers[32];
+static unsigned int vec_size = 0x200;
+
+void do_vi(int irq)
+{
+	vi_handler_t action;
+
+	action = ip_handlers[irq];
+	if (action)
+		action(irq);
+	else
+		pr_err("vi handler[%d] is not installed\n", irq);
+}
+
+void set_vi_handler(int n, vi_handler_t addr)
+{
+	if ((n < EXCCODE_INT_START) || (n >= EXCCODE_INT_END)) {
+		pr_err("Set invalid vector handler[%d]\n", n);
+		return;
+	}
+
+	ip_handlers[n - EXCCODE_INT_START] = addr;
+}
+
+extern void tlb_init(void);
+extern void cache_error_setup(void);
+
+static void configure_exception_vector(void)
+{
+	csr_writeq(eentry, LOONGARCH_CSR_EENTRY);
+	csr_writeq(eentry, LOONGARCH_CSR_MERRENTRY);
+	csr_writeq(tlbrentry, LOONGARCH_CSR_TLBRENTRY);
+}
+
+void __init boot_cpu_trap_init(void)
+{
+	unsigned long size = (64 + 14) * vec_size;
+
+	memblock_set_bottom_up(true);
+	eentry = (unsigned long)memblock_alloc(size, 1 << fls(size));
+	tlbrentry = (unsigned long)memblock_alloc(PAGE_SIZE, PAGE_SIZE);
+	memblock_set_bottom_up(false);
+
+	setup_vint_size(vec_size);
+	configure_exception_vector();
+
+	if (!cpu_data[0].asid_cache)
+		cpu_data[0].asid_cache = asid_first_version(0);
+
+	mmgrab(&init_mm);
+	current->active_mm = &init_mm;
+	BUG_ON(current->mm);
+	enter_lazy_tlb(&init_mm, current);
+
+	tlb_init();
+	TLBMISS_HANDLER_SETUP();
+}
+
+void nonboot_cpu_trap_init(void)
+{
+	unsigned int cpu = smp_processor_id();
+
+	cpu_cache_init();
+
+	setup_vint_size(vec_size);
+
+	configure_exception_vector();
+
+	if (!cpu_data[cpu].asid_cache)
+		cpu_data[cpu].asid_cache = asid_first_version(cpu);
+
+	mmgrab(&init_mm);
+	current->active_mm = &init_mm;
+	BUG_ON(current->mm);
+	enter_lazy_tlb(&init_mm, current);
+
+	tlb_init();
+	TLBMISS_HANDLER_SETUP();
+}
+
+/* Install CPU exception handler */
+void set_handler(unsigned long offset, void *addr, unsigned long size)
+{
+	memcpy((void *)(eentry + offset), addr, size);
+	local_flush_icache_range(eentry + offset, eentry + offset + size);
+}
+
+static const char panic_null_cerr[] =
+	"Trying to set NULL cache error exception handler\n";
+
+/*
+ * Install uncached CPU exception handler.
+ * This is suitable only for the cache error exception which is the only
+ * exception handler that is being run uncached.
+ */
+void set_merr_handler(unsigned long offset, void *addr, unsigned long size)
+{
+	unsigned long uncached_eentry = TO_UNCAC(__pa(eentry));
+
+	if (!addr)
+		panic(panic_null_cerr);
+
+	memcpy((void *)(uncached_eentry + offset), addr, size);
+}
+
+void __init trap_init(void)
+{
+	long i;
+	void *vec_start;
+
+	/* Initialise exception handlers */
+	for (i = 0; i < 64; i++)
+		set_handler(i * vec_size, handle_reserved, vec_size);
+
+	/* Set interrupt vector handler */
+	for (i = EXCCODE_INT_START; i < EXCCODE_INT_END; i++) {
+		vec_start = vi_table[i - EXCCODE_INT_START];
+		set_handler(i * vec_size, vec_start, vec_size);
+	}
+
+	set_handler(EXCCODE_TLBL * vec_size, handle_tlb_load, vec_size);
+	set_handler(EXCCODE_TLBS * vec_size, handle_tlb_store, vec_size);
+	set_handler(EXCCODE_TLBI * vec_size, handle_tlb_load, vec_size);
+	set_handler(EXCCODE_TLBM * vec_size, handle_tlb_modify, vec_size);
+	set_handler(EXCCODE_TLBRI * vec_size, handle_tlb_rixi, vec_size);
+	set_handler(EXCCODE_TLBXI * vec_size, handle_tlb_rixi, vec_size);
+	set_handler(EXCCODE_ADE * vec_size, handle_ade, vec_size);
+	set_handler(EXCCODE_ALE * vec_size, handle_ale, vec_size);
+	set_handler(EXCCODE_SYS * vec_size, handle_syscall, vec_size);
+	set_handler(EXCCODE_BP * vec_size, handle_bp, vec_size);
+	set_handler(EXCCODE_INE * vec_size, handle_ri, vec_size);
+	set_handler(EXCCODE_IPE * vec_size, handle_ri, vec_size);
+	set_handler(EXCCODE_FPDIS * vec_size, handle_fpu, vec_size);
+	set_handler(EXCCODE_LSXDIS * vec_size, handle_lsx, vec_size);
+	set_handler(EXCCODE_LASXDIS * vec_size, handle_lasx, vec_size);
+	set_handler(EXCCODE_FPE * vec_size, handle_fpe, vec_size);
+	set_handler(EXCCODE_BTDIS * vec_size, handle_lbt, vec_size);
+	set_handler(EXCCODE_WATCH * vec_size, handle_watch, vec_size);
+
+	cache_error_setup();
+
+	local_flush_icache_range(eentry, eentry + 0x400);
+}
diff --git a/arch/loongarch/kernel/unaligned.c b/arch/loongarch/kernel/unaligned.c
new file mode 100644
index 000000000000..d66e453297da
--- /dev/null
+++ b/arch/loongarch/kernel/unaligned.c
@@ -0,0 +1,461 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Handle unaligned accesses by emulation.
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/context_tracking.h>
+#include <linux/mm.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/debugfs.h>
+#include <linux/perf_event.h>
+
+#include <asm/asm.h>
+#include <asm/branch.h>
+#include <asm/byteorder.h>
+#include <asm/debug.h>
+#include <asm/fpu.h>
+#include <asm/inst.h>
+
+#include "access-helper.h"
+
+enum {
+	UNALIGNED_ACTION_QUIET,
+	UNALIGNED_ACTION_SIGNAL,
+	UNALIGNED_ACTION_SHOW,
+};
+#ifdef CONFIG_DEBUG_FS
+static u32 unaligned_instructions;
+static u32 unaligned_action;
+#else
+#define unaligned_action UNALIGNED_ACTION_QUIET
+#endif
+extern void show_registers(struct pt_regs *regs);
+
+static inline void write_fpr(unsigned int fd, unsigned long value)
+{
+#define WRITE_FPR(fd, value)		\
+{					\
+	__asm__ __volatile__(		\
+	"movgr2fr.d $f%1, %0\n\t"	\
+	:: "r"(value), "i"(fd));	\
+}
+
+	switch (fd) {
+	case 0:
+		WRITE_FPR(0, value);
+		break;
+	case 1:
+		WRITE_FPR(1, value);
+		break;
+	case 2:
+		WRITE_FPR(2, value);
+		break;
+	case 3:
+		WRITE_FPR(3, value);
+		break;
+	case 4:
+		WRITE_FPR(4, value);
+		break;
+	case 5:
+		WRITE_FPR(5, value);
+		break;
+	case 6:
+		WRITE_FPR(6, value);
+		break;
+	case 7:
+		WRITE_FPR(7, value);
+		break;
+	case 8:
+		WRITE_FPR(8, value);
+		break;
+	case 9:
+		WRITE_FPR(9, value);
+		break;
+	case 10:
+		WRITE_FPR(10, value);
+		break;
+	case 11:
+		WRITE_FPR(11, value);
+		break;
+	case 12:
+		WRITE_FPR(12, value);
+		break;
+	case 13:
+		WRITE_FPR(13, value);
+		break;
+	case 14:
+		WRITE_FPR(14, value);
+		break;
+	case 15:
+		WRITE_FPR(15, value);
+		break;
+	case 16:
+		WRITE_FPR(16, value);
+		break;
+	case 17:
+		WRITE_FPR(17, value);
+		break;
+	case 18:
+		WRITE_FPR(18, value);
+		break;
+	case 19:
+		WRITE_FPR(19, value);
+		break;
+	case 20:
+		WRITE_FPR(20, value);
+		break;
+	case 21:
+		WRITE_FPR(21, value);
+		break;
+	case 22:
+		WRITE_FPR(22, value);
+		break;
+	case 23:
+		WRITE_FPR(23, value);
+		break;
+	case 24:
+		WRITE_FPR(24, value);
+		break;
+	case 25:
+		WRITE_FPR(25, value);
+		break;
+	case 26:
+		WRITE_FPR(26, value);
+		break;
+	case 27:
+		WRITE_FPR(27, value);
+		break;
+	case 28:
+		WRITE_FPR(28, value);
+		break;
+	case 29:
+		WRITE_FPR(29, value);
+		break;
+	case 30:
+		WRITE_FPR(30, value);
+		break;
+	case 31:
+		WRITE_FPR(31, value);
+		break;
+	default:
+		panic("unexpected fd '%d'", fd);
+	}
+#undef WRITE_FPR
+}
+
+static inline unsigned long read_fpr(unsigned int fd)
+{
+#define READ_FPR(fd, __value)		\
+{					\
+	__asm__ __volatile__(		\
+	"movfr2gr.d\t%0, $f%1\n\t"	\
+	: "=r"(__value) : "i"(fd));	\
+}
+
+	unsigned long __value;
+
+	switch (fd) {
+	case 0:
+		READ_FPR(0, __value);
+		break;
+	case 1:
+		READ_FPR(1, __value);
+		break;
+	case 2:
+		READ_FPR(2, __value);
+		break;
+	case 3:
+		READ_FPR(3, __value);
+		break;
+	case 4:
+		READ_FPR(4, __value);
+		break;
+	case 5:
+		READ_FPR(5, __value);
+		break;
+	case 6:
+		READ_FPR(6, __value);
+		break;
+	case 7:
+		READ_FPR(7, __value);
+		break;
+	case 8:
+		READ_FPR(8, __value);
+		break;
+	case 9:
+		READ_FPR(9, __value);
+		break;
+	case 10:
+		READ_FPR(10, __value);
+		break;
+	case 11:
+		READ_FPR(11, __value);
+		break;
+	case 12:
+		READ_FPR(12, __value);
+		break;
+	case 13:
+		READ_FPR(13, __value);
+		break;
+	case 14:
+		READ_FPR(14, __value);
+		break;
+	case 15:
+		READ_FPR(15, __value);
+		break;
+	case 16:
+		READ_FPR(16, __value);
+		break;
+	case 17:
+		READ_FPR(17, __value);
+		break;
+	case 18:
+		READ_FPR(18, __value);
+		break;
+	case 19:
+		READ_FPR(19, __value);
+		break;
+	case 20:
+		READ_FPR(20, __value);
+		break;
+	case 21:
+		READ_FPR(21, __value);
+		break;
+	case 22:
+		READ_FPR(22, __value);
+		break;
+	case 23:
+		READ_FPR(23, __value);
+		break;
+	case 24:
+		READ_FPR(24, __value);
+		break;
+	case 25:
+		READ_FPR(25, __value);
+		break;
+	case 26:
+		READ_FPR(26, __value);
+		break;
+	case 27:
+		READ_FPR(27, __value);
+		break;
+	case 28:
+		READ_FPR(28, __value);
+		break;
+	case 29:
+		READ_FPR(29, __value);
+		break;
+	case 30:
+		READ_FPR(30, __value);
+		break;
+	case 31:
+		READ_FPR(31, __value);
+		break;
+	default:
+		panic("unexpected fd '%d'", fd);
+	}
+#undef READ_FPR
+	return __value;
+}
+
+static void emulate_load_store_insn(struct pt_regs *regs, void __user *addr, unsigned int *pc)
+{
+	bool user = user_mode(regs);
+	unsigned int res;
+	unsigned long value;
+	unsigned long origpc;
+	unsigned long origra;
+	union loongarch_instruction insn;
+
+	origpc = (unsigned long)pc;
+	origra = regs->regs[1];
+
+	perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
+
+	/*
+	 * This load never faults.
+	 */
+	__get_inst(&insn.word, pc, user);
+	if (user && !access_ok(addr, 8))
+		goto sigbus;
+
+	if (insn.reg2i12_format.opcode == ldd_op ||
+		insn.reg2i14_format.opcode == ldptrd_op ||
+		insn.reg3_format.opcode == ldxd_op) {
+		LoadDW(addr, value, res);
+		if (res)
+			goto fault;
+		regs->regs[insn.reg2i12_format.rd] = value;
+	} else if (insn.reg2i12_format.opcode == ldw_op ||
+		insn.reg2i14_format.opcode == ldptrw_op ||
+		insn.reg3_format.opcode == ldxw_op) {
+		LoadW(addr, value, res);
+		if (res)
+			goto fault;
+		regs->regs[insn.reg2i12_format.rd] = value;
+	} else if (insn.reg2i12_format.opcode == ldwu_op ||
+		insn.reg3_format.opcode == ldxwu_op) {
+		LoadWU(addr, value, res);
+		if (res)
+			goto fault;
+		regs->regs[insn.reg2i12_format.rd] = value;
+	} else if (insn.reg2i12_format.opcode == ldh_op ||
+		insn.reg3_format.opcode == ldxh_op) {
+		LoadHW(addr, value, res);
+		if (res)
+			goto fault;
+		regs->regs[insn.reg2i12_format.rd] = value;
+	} else if (insn.reg2i12_format.opcode == ldhu_op ||
+		insn.reg3_format.opcode == ldxhu_op) {
+		LoadHWU(addr, value, res);
+		if (res)
+			goto fault;
+		regs->regs[insn.reg2i12_format.rd] = value;
+	} else if (insn.reg2i12_format.opcode == std_op ||
+		insn.reg2i14_format.opcode == stptrd_op ||
+		insn.reg3_format.opcode == stxd_op) {
+		value = regs->regs[insn.reg2i12_format.rd];
+		StoreDW(addr, value, res);
+		if (res)
+			goto fault;
+	} else if (insn.reg2i12_format.opcode == stw_op ||
+		insn.reg2i14_format.opcode == stptrw_op ||
+		insn.reg3_format.opcode == stxw_op) {
+		value = regs->regs[insn.reg2i12_format.rd];
+		StoreW(addr, value, res);
+		if (res)
+			goto fault;
+	} else if (insn.reg2i12_format.opcode == sth_op ||
+		insn.reg3_format.opcode == stxh_op) {
+		value = regs->regs[insn.reg2i12_format.rd];
+		StoreHW(addr, value, res);
+		if (res)
+			goto fault;
+	} else if (insn.reg2i12_format.opcode == fldd_op ||
+		insn.reg3_format.opcode == fldxd_op) {
+		LoadDW(addr, value, res);
+		if (res)
+			goto fault;
+		write_fpr(insn.reg2i12_format.rd, value);
+	} else if (insn.reg2i12_format.opcode == flds_op ||
+		insn.reg3_format.opcode == fldxs_op) {
+		LoadW(addr, value, res);
+		if (res)
+			goto fault;
+		write_fpr(insn.reg2i12_format.rd, value);
+	} else if (insn.reg2i12_format.opcode == fstd_op ||
+		insn.reg3_format.opcode == fstxd_op) {
+		value = read_fpr(insn.reg2i12_format.rd);
+		StoreDW(addr, value, res);
+		if (res)
+			goto fault;
+	} else if (insn.reg2i12_format.opcode == fsts_op ||
+		insn.reg3_format.opcode == fstxs_op) {
+		value = read_fpr(insn.reg2i12_format.rd);
+		StoreW(addr, value, res);
+		if (res)
+			goto fault;
+	} else
+		goto sigbus;
+
+
+#ifdef CONFIG_DEBUG_FS
+	unaligned_instructions++;
+#endif
+
+	compute_return_epc(regs);
+	return;
+
+fault:
+	/* roll back jump/branch */
+	regs->csr_epc = origpc;
+	regs->regs[1] = origra;
+	/* Did we have an exception handler installed? */
+	if (fixup_exception(regs))
+		return;
+
+	die_if_kernel("Unhandled kernel unaligned access", regs);
+	force_sig(SIGSEGV);
+
+	return;
+
+sigbus:
+	die_if_kernel("Unhandled kernel unaligned access", regs);
+	force_sig(SIGBUS);
+
+	return;
+}
+
+asmlinkage void do_ade(struct pt_regs *regs)
+{
+	enum ctx_state prev_state;
+
+	prev_state = exception_enter();
+
+	if (unaligned_action == UNALIGNED_ACTION_SHOW)
+		show_registers(regs);
+
+	die_if_kernel("Kernel ade access", regs);
+	force_sig(SIGBUS);
+
+	/*
+	 * On return from the signal handler we should advance the epc
+	 */
+	exception_exit(prev_state);
+}
+
+asmlinkage void do_ale(struct pt_regs *regs)
+{
+	unsigned int *pc;
+	enum ctx_state prev_state;
+
+	prev_state = exception_enter();
+	perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS,
+			1, regs, regs->csr_badvaddr);
+	/*
+	 * Did we catch a fault trying to load an instruction?
+	 */
+	if (regs->csr_badvaddr == regs->csr_epc)
+		goto sigbus;
+
+	if (user_mode(regs) && !test_thread_flag(TIF_FIXADE))
+		goto sigbus;
+	if (unaligned_action == UNALIGNED_ACTION_SIGNAL)
+		goto sigbus;
+
+	/*
+	 * Do branch emulation only if we didn't forward the exception.
+	 * This is all so but ugly ...
+	 */
+
+	if (unaligned_action == UNALIGNED_ACTION_SHOW)
+		show_registers(regs);
+	pc = (unsigned int *)exception_epc(regs);
+
+	emulate_load_store_insn(regs, (void __user *)regs->csr_badvaddr, pc);
+
+	return;
+
+sigbus:
+	die_if_kernel("Kernel unaligned instruction access", regs);
+	force_sig(SIGBUS);
+
+	/*
+	 * On return from the signal handler we should advance the epc
+	 */
+	exception_exit(prev_state);
+}
+
+#ifdef CONFIG_DEBUG_FS
+static int __init debugfs_unaligned(void)
+{
+	debugfs_create_u32("unaligned_instructions", S_IRUGO,
+			       loongarch_debugfs_dir, &unaligned_instructions);
+	debugfs_create_u32("unaligned_action", S_IRUGO | S_IWUSR,
+			       loongarch_debugfs_dir, &unaligned_action);
+	return 0;
+}
+arch_initcall(debugfs_unaligned);
+#endif
-- 
2.27.0


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

* [PATCH 07/19] LoongArch: Add process management
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (4 preceding siblings ...)
  2021-07-06  4:18 ` [PATCH 06/19] LoongArch: Add exception/interrupt handling Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06 10:16   ` Arnd Bergmann
  2021-07-06  4:18 ` [PATCH 08/19] LoongArch: Add memory management Huacai Chen
                   ` (14 subsequent siblings)
  20 siblings, 1 reply; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

This patch adds process management support for LoongArch, including:
thread info definition, context switch and process tracing.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 arch/loongarch/include/asm/idle.h        |   9 +
 arch/loongarch/include/asm/mmu.h         |  16 +
 arch/loongarch/include/asm/mmu_context.h | 174 +++++++++
 arch/loongarch/include/asm/ptrace.h      | 138 +++++++
 arch/loongarch/include/asm/switch_to.h   |  37 ++
 arch/loongarch/include/asm/thread_info.h | 141 +++++++
 arch/loongarch/include/uapi/asm/ptrace.h |  47 +++
 arch/loongarch/kernel/fpu.S              | 298 +++++++++++++++
 arch/loongarch/kernel/idle.c             |  17 +
 arch/loongarch/kernel/process.c          | 267 +++++++++++++
 arch/loongarch/kernel/ptrace.c           | 459 +++++++++++++++++++++++
 arch/loongarch/kernel/switch.S           |  48 +++
 12 files changed, 1651 insertions(+)
 create mode 100644 arch/loongarch/include/asm/idle.h
 create mode 100644 arch/loongarch/include/asm/mmu.h
 create mode 100644 arch/loongarch/include/asm/mmu_context.h
 create mode 100644 arch/loongarch/include/asm/ptrace.h
 create mode 100644 arch/loongarch/include/asm/switch_to.h
 create mode 100644 arch/loongarch/include/asm/thread_info.h
 create mode 100644 arch/loongarch/include/uapi/asm/ptrace.h
 create mode 100644 arch/loongarch/kernel/fpu.S
 create mode 100644 arch/loongarch/kernel/idle.c
 create mode 100644 arch/loongarch/kernel/process.c
 create mode 100644 arch/loongarch/kernel/ptrace.c
 create mode 100644 arch/loongarch/kernel/switch.S

diff --git a/arch/loongarch/include/asm/idle.h b/arch/loongarch/include/asm/idle.h
new file mode 100644
index 000000000000..f7f2b7dbf958
--- /dev/null
+++ b/arch/loongarch/include/asm/idle.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_IDLE_H
+#define __ASM_IDLE_H
+
+#include <linux/linkage.h>
+
+extern asmlinkage void __arch_cpu_idle(void);
+
+#endif /* __ASM_IDLE_H  */
diff --git a/arch/loongarch/include/asm/mmu.h b/arch/loongarch/include/asm/mmu.h
new file mode 100644
index 000000000000..11f63fb0159b
--- /dev/null
+++ b/arch/loongarch/include/asm/mmu.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef __ASM_MMU_H
+#define __ASM_MMU_H
+
+#include <linux/atomic.h>
+#include <linux/spinlock.h>
+
+typedef struct {
+	u64 asid[NR_CPUS];
+	void *vdso;
+} mm_context_t;
+
+#endif /* __ASM_MMU_H */
diff --git a/arch/loongarch/include/asm/mmu_context.h b/arch/loongarch/include/asm/mmu_context.h
new file mode 100644
index 000000000000..71fd2890a34e
--- /dev/null
+++ b/arch/loongarch/include/asm/mmu_context.h
@@ -0,0 +1,174 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Switch a MMU context.
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_MMU_CONTEXT_H
+#define _ASM_MMU_CONTEXT_H
+
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/mm_types.h>
+#include <linux/smp.h>
+#include <linux/slab.h>
+
+#include <asm/cacheflush.h>
+#include <asm/tlbflush.h>
+#include <asm-generic/mm_hooks.h>
+
+#define TLBMISS_HANDLER_SETUP()					\
+	do {							\
+		csr_writeq((unsigned long)invalid_pg_dir, LOONGARCH_CSR_PGDL);	   \
+		csr_writeq((unsigned long)smp_processor_id(), LOONGARCH_CSR_TMID); \
+	} while (0)
+
+/*
+ *  All unused by hardware upper bits will be considered
+ *  as a software asid extension.
+ */
+static inline u64 asid_version_mask(unsigned int cpu)
+{
+	unsigned long asid_mask = cpu_asid_mask(&cpu_data[cpu]);
+
+	return ~(u64)(asid_mask | (asid_mask - 1));
+}
+
+static inline u64 asid_first_version(unsigned int cpu)
+{
+	return ~asid_version_mask(cpu) + 1;
+}
+
+#define cpu_context(cpu, mm)	((mm)->context.asid[cpu])
+#define asid_cache(cpu)		(cpu_data[cpu].asid_cache)
+#define cpu_asid(cpu, mm)	(cpu_context((cpu), (mm)) & cpu_asid_mask(&cpu_data[cpu]))
+
+static inline int asid_valid(struct mm_struct *mm, unsigned int cpu)
+{
+	if ((cpu_context(cpu, mm) ^ asid_cache(cpu)) & asid_version_mask(cpu))
+		return 0;
+
+	return 1;
+}
+
+static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
+{
+}
+
+/* Normal, classic get_new_mmu_context */
+static inline void
+get_new_mmu_context(struct mm_struct *mm, unsigned long cpu)
+{
+	u64 asid = asid_cache(cpu);
+
+	if (!((++asid) & cpu_asid_mask(&cpu_data[cpu])))
+		local_flush_tlb_all();	/* start new asid cycle */
+
+	cpu_context(cpu, mm) = asid_cache(cpu) = asid;
+}
+
+/*
+ * Initialize the context related info for a new mm_struct
+ * instance.
+ */
+static inline int
+init_new_context(struct task_struct *tsk, struct mm_struct *mm)
+{
+	int i;
+
+	for_each_possible_cpu(i)
+		cpu_context(i, mm) = 0;
+
+	return 0;
+}
+
+static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
+			     struct task_struct *tsk)
+{
+	unsigned int cpu = smp_processor_id();
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	/* Check if our ASID is of an older version and thus invalid */
+	if (asid_valid(next, cpu) == 0)
+		get_new_mmu_context(next, cpu);
+	write_csr_asid(cpu_asid(cpu, next));
+	if (next != &init_mm)
+		csr_writeq((unsigned long)next->pgd, LOONGARCH_CSR_PGDL);
+	else
+		csr_writeq((unsigned long)invalid_pg_dir, LOONGARCH_CSR_PGDL);
+
+	/*
+	 * Mark current->active_mm as not "active" anymore.
+	 * We don't want to mislead possible IPI tlb flush routines.
+	 */
+	cpumask_set_cpu(cpu, mm_cpumask(next));
+
+	local_irq_restore(flags);
+}
+
+/*
+ * Destroy context related info for an mm_struct that is about
+ * to be put to rest.
+ */
+static inline void destroy_context(struct mm_struct *mm)
+{
+}
+
+/*
+ * After we have set current->mm to a new value, this activates
+ * the context for the new mm so we see the new mappings.
+ */
+static inline void
+activate_mm(struct mm_struct *prev, struct mm_struct *next)
+{
+	unsigned long flags;
+	unsigned int cpu = smp_processor_id();
+
+	local_irq_save(flags);
+
+	/* Unconditionally get a new ASID.  */
+	get_new_mmu_context(next, cpu);
+
+	write_csr_asid(cpu_asid(cpu, next));
+	csr_writeq((unsigned long)next->pgd, LOONGARCH_CSR_PGDL);
+
+	/* mark mmu ownership change */
+	cpumask_set_cpu(cpu, mm_cpumask(next));
+
+	local_irq_restore(flags);
+}
+
+#define deactivate_mm(tsk, mm)	do { } while (0)
+
+/*
+ * If mm is currently active, we can't really drop it.
+ * Instead, we will get a new one for it.
+ */
+static inline void
+drop_mmu_context(struct mm_struct *mm, unsigned int cpu)
+{
+	int asid;
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	asid = read_csr_asid() & cpu_asid_mask(&current_cpu_data);
+
+	if (asid == cpu_asid(cpu, mm)) {
+		if (!current->mm || (current->mm == mm)) {
+			get_new_mmu_context(mm, cpu);
+			write_csr_asid(cpu_asid(cpu, mm));
+			goto out;
+		}
+	}
+
+	/* Will get a new context next time */
+	cpu_context(cpu, mm) = 0;
+	cpumask_clear_cpu(cpu, mm_cpumask(mm));
+out:
+	local_irq_restore(flags);
+}
+
+#endif /* _ASM_MMU_CONTEXT_H */
diff --git a/arch/loongarch/include/asm/ptrace.h b/arch/loongarch/include/asm/ptrace.h
new file mode 100644
index 000000000000..8bd8c747b060
--- /dev/null
+++ b/arch/loongarch/include/asm/ptrace.h
@@ -0,0 +1,138 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_PTRACE_H
+#define _ASM_PTRACE_H
+
+#include <linux/compiler.h>
+#include <linux/linkage.h>
+#include <linux/types.h>
+#include <asm/page.h>
+#include <asm/thread_info.h>
+#include <uapi/asm/ptrace.h>
+
+static inline unsigned long kernel_stack_pointer(struct pt_regs *regs)
+{
+	return regs->regs[3];
+}
+
+/*
+ * Don't use asm-generic/ptrace.h it defines FP accessors that don't make
+ * sense on LoongArch.  We rather want an error if they get invoked.
+ */
+
+static inline void instruction_pointer_set(struct pt_regs *regs, unsigned long val)
+{
+	regs->csr_epc = val;
+}
+
+/* Query offset/name of register from its name/offset */
+extern int regs_query_register_offset(const char *name);
+#define MAX_REG_OFFSET (offsetof(struct pt_regs, __last))
+
+/**
+ * regs_get_register() - get register value from its offset
+ * @regs:       pt_regs from which register value is gotten.
+ * @offset:     offset number of the register.
+ *
+ * regs_get_register returns the value of a register. The @offset is the
+ * offset of the register in struct pt_regs address which specified by @regs.
+ * If @offset is bigger than MAX_REG_OFFSET, this returns 0.
+ */
+static inline unsigned long regs_get_register(struct pt_regs *regs, unsigned int offset)
+{
+	if (unlikely(offset > MAX_REG_OFFSET))
+		return 0;
+
+	return *(unsigned long *)((unsigned long)regs + offset);
+}
+
+/**
+ * regs_within_kernel_stack() - check the address in the stack
+ * @regs:       pt_regs which contains kernel stack pointer.
+ * @addr:       address which is checked.
+ *
+ * regs_within_kernel_stack() checks @addr is within the kernel stack page(s).
+ * If @addr is within the kernel stack, it returns true. If not, returns false.
+ */
+static inline int regs_within_kernel_stack(struct pt_regs *regs, unsigned long addr)
+{
+	return ((addr & ~(THREAD_SIZE - 1))  ==
+		(kernel_stack_pointer(regs) & ~(THREAD_SIZE - 1)));
+}
+
+/**
+ * regs_get_kernel_stack_nth() - get Nth entry of the stack
+ * @regs:       pt_regs which contains kernel stack pointer.
+ * @n:          stack entry number.
+ *
+ * regs_get_kernel_stack_nth() returns @n th entry of the kernel stack which
+ * is specified by @regs. If the @n th entry is NOT in the kernel stack,
+ * this returns 0.
+ */
+static inline unsigned long regs_get_kernel_stack_nth(struct pt_regs *regs, unsigned int n)
+{
+	unsigned long *addr = (unsigned long *)kernel_stack_pointer(regs);
+
+	addr += n;
+	if (regs_within_kernel_stack(regs, (unsigned long)addr))
+		return *addr;
+	else
+		return 0;
+}
+
+struct task_struct;
+
+/*
+ * Does the process account for user or for system time?
+ */
+#define user_mode(regs) (((regs)->csr_prmd & PLV_MASK) == PLV_USER)
+
+static inline int is_syscall_success(struct pt_regs *regs)
+{
+	return !regs->regs[7];
+}
+
+static inline long regs_return_value(struct pt_regs *regs)
+{
+	if (is_syscall_success(regs) || !user_mode(regs))
+		return regs->regs[4];
+	else
+		return -regs->regs[4];
+}
+
+#define instruction_pointer(regs) ((regs)->csr_epc)
+#define profile_pc(regs) instruction_pointer(regs)
+
+extern asmlinkage long syscall_trace_enter(struct pt_regs *regs, long syscall);
+extern asmlinkage void syscall_trace_leave(struct pt_regs *regs);
+
+extern void die(const char *, struct pt_regs *) __noreturn;
+
+static inline void die_if_kernel(const char *str, struct pt_regs *regs)
+{
+	if (unlikely(!user_mode(regs)))
+		die(str, regs);
+}
+
+#define current_pt_regs()						\
+({									\
+	unsigned long sp = (unsigned long)__builtin_frame_address(0);	\
+	(struct pt_regs *)((sp | (THREAD_SIZE - 1)) + 1 - 32) - 1;	\
+})
+
+/* Helpers for working with the user stack pointer */
+
+static inline unsigned long user_stack_pointer(struct pt_regs *regs)
+{
+	return regs->regs[3];
+}
+
+static inline void user_stack_pointer_set(struct pt_regs *regs,
+	unsigned long val)
+{
+	regs->regs[3] = val;
+}
+
+#endif /* _ASM_PTRACE_H */
diff --git a/arch/loongarch/include/asm/switch_to.h b/arch/loongarch/include/asm/switch_to.h
new file mode 100644
index 000000000000..43fa9d8fc192
--- /dev/null
+++ b/arch/loongarch/include/asm/switch_to.h
@@ -0,0 +1,37 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_SWITCH_TO_H
+#define _ASM_SWITCH_TO_H
+
+#include <asm/cpu-features.h>
+#include <asm/fpu.h>
+
+struct task_struct;
+
+/**
+ * resume - resume execution of a task
+ * @prev:	The task previously executed.
+ * @next:	The task to begin executing.
+ * @next_ti:	task_thread_info(next).
+ *
+ * This function is used whilst scheduling to save the context of prev & load
+ * the context of next. Returns prev.
+ */
+extern asmlinkage struct task_struct *resume(struct task_struct *prev,
+		struct task_struct *next, struct thread_info *next_ti);
+
+/*
+ * For newly created kernel threads switch_to() will return to
+ * ret_from_kernel_thread, newly created user threads to ret_from_fork.
+ * That is, everything following resume() will be skipped for new threads.
+ * So everything that matters to new threads should be placed before resume().
+ */
+#define switch_to(prev, next, last)					\
+do {									\
+	lose_fpu_inatomic(1, prev);					\
+	(last) = resume(prev, next, task_thread_info(next));		\
+} while (0)
+
+#endif /* _ASM_SWITCH_TO_H */
diff --git a/arch/loongarch/include/asm/thread_info.h b/arch/loongarch/include/asm/thread_info.h
new file mode 100644
index 000000000000..0accd02e2a81
--- /dev/null
+++ b/arch/loongarch/include/asm/thread_info.h
@@ -0,0 +1,141 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * thread_info.h: LoongArch low-level thread information
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef _ASM_THREAD_INFO_H
+#define _ASM_THREAD_INFO_H
+
+#ifdef __KERNEL__
+
+#ifndef __ASSEMBLY__
+
+#include <asm/processor.h>
+
+/*
+ * low level task data that entry.S needs immediate access to
+ * - this struct should fit entirely inside of one cache line
+ * - this struct shares the supervisor stack pages
+ * - if the contents of this structure are changed, the assembly constants
+ *   must also be changed
+ */
+struct thread_info {
+	struct task_struct	*task;		/* main task structure */
+	unsigned long		flags;		/* low level flags */
+	unsigned long		tp_value;	/* thread pointer */
+	__u32			cpu;		/* current CPU */
+	int			preempt_count;	/* 0 => preemptible, <0 => BUG */
+	struct pt_regs		*regs;
+	long			syscall;	/* syscall number */
+};
+
+/*
+ * macros/functions for gaining access to the thread information structure
+ */
+#define INIT_THREAD_INFO(tsk)			\
+{						\
+	.task		= &tsk,			\
+	.flags		= _TIF_FIXADE,		\
+	.cpu		= 0,			\
+	.preempt_count	= INIT_PREEMPT_COUNT,	\
+}
+
+/* How to get the thread information struct from C. */
+register struct thread_info *__current_thread_info __asm__("$r2");
+
+static inline struct thread_info *current_thread_info(void)
+{
+	return __current_thread_info;
+}
+
+#endif /* !__ASSEMBLY__ */
+
+/* thread information allocation */
+#if defined(CONFIG_PAGE_SIZE_4KB) && defined(CONFIG_32BIT)
+#define THREAD_SIZE_ORDER (1)
+#endif
+#if defined(CONFIG_PAGE_SIZE_4KB) && defined(CONFIG_64BIT)
+#define THREAD_SIZE_ORDER (2)
+#endif
+#ifdef CONFIG_PAGE_SIZE_16KB
+#define THREAD_SIZE_ORDER (0)
+#endif
+#ifdef CONFIG_PAGE_SIZE_64KB
+#define THREAD_SIZE_ORDER (0)
+#endif
+
+#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER)
+#define THREAD_MASK (THREAD_SIZE - 1UL)
+
+#define STACK_WARN	(THREAD_SIZE / 8)
+
+/*
+ * thread information flags
+ * - these are process state flags that various assembly files may need to
+ *   access
+ * - pending work-to-be-done flags are in LSW
+ * - other flags in MSW
+ */
+#define TIF_SIGPENDING		1	/* signal pending */
+#define TIF_NEED_RESCHED	2	/* rescheduling necessary */
+#define TIF_NOTIFY_RESUME	3	/* callback before returning to user */
+#define TIF_NOTIFY_SIGNAL	4	/* signal notifications exist */
+#define TIF_RESTORE_SIGMASK	5	/* restore signal mask in do_signal() */
+#define TIF_NOHZ		6	/* in adaptive nohz mode */
+#define TIF_SYSCALL_AUDIT	7	/* syscall auditing active */
+#define TIF_SYSCALL_TRACE	8	/* syscall trace active */
+#define TIF_SYSCALL_TRACEPOINT	9	/* syscall tracepoint instrumentation */
+#define TIF_SECCOMP		10	/* secure computing */
+#define TIF_UPROBE		11	/* breakpointed or singlestepping */
+#define TIF_USEDFPU		12	/* FPU was used by this task this quantum (SMP) */
+#define TIF_USEDSIMD		13	/* SIMD has been used this quantum */
+#define TIF_MEMDIE		14	/* is terminating due to OOM killer */
+#define TIF_FIXADE		15	/* Fix address errors in software */
+#define TIF_LOGADE		16	/* Log address errors to syslog */
+#define TIF_32BIT_REGS		17	/* 32-bit general purpose registers */
+#define TIF_32BIT_ADDR		18	/* 32-bit address space */
+#define TIF_LOAD_WATCH		19	/* If set, load watch registers */
+#define TIF_LSX_CTX_LIVE	20	/* LSX context must be preserved */
+#define TIF_LASX_CTX_LIVE	21	/* LASX context must be preserved */
+
+#define _TIF_SIGPENDING		(1<<TIF_SIGPENDING)
+#define _TIF_NEED_RESCHED	(1<<TIF_NEED_RESCHED)
+#define _TIF_NOTIFY_RESUME	(1<<TIF_NOTIFY_RESUME)
+#define _TIF_NOTIFY_SIGNAL	(1<<TIF_NOTIFY_SIGNAL)
+#define _TIF_NOHZ		(1<<TIF_NOHZ)
+#define _TIF_SYSCALL_AUDIT	(1<<TIF_SYSCALL_AUDIT)
+#define _TIF_SYSCALL_TRACE	(1<<TIF_SYSCALL_TRACE)
+#define _TIF_SYSCALL_TRACEPOINT	(1<<TIF_SYSCALL_TRACEPOINT)
+#define _TIF_SECCOMP		(1<<TIF_SECCOMP)
+#define _TIF_UPROBE		(1<<TIF_UPROBE)
+#define _TIF_USEDFPU		(1<<TIF_USEDFPU)
+#define _TIF_USEDSIMD		(1<<TIF_USEDSIMD)
+#define _TIF_FIXADE		(1<<TIF_FIXADE)
+#define _TIF_LOGADE		(1<<TIF_LOGADE)
+#define _TIF_32BIT_REGS		(1<<TIF_32BIT_REGS)
+#define _TIF_32BIT_ADDR		(1<<TIF_32BIT_ADDR)
+#define _TIF_LOAD_WATCH		(1<<TIF_LOAD_WATCH)
+#define _TIF_LSX_CTX_LIVE	(1<<TIF_LSX_CTX_LIVE)
+#define _TIF_LASX_CTX_LIVE	(1<<TIF_LASX_CTX_LIVE)
+
+#define _TIF_WORK_SYSCALL_ENTRY	(_TIF_NOHZ | _TIF_SYSCALL_TRACE |	\
+				 _TIF_SYSCALL_AUDIT | \
+				 _TIF_SYSCALL_TRACEPOINT | _TIF_SECCOMP)
+
+/* work to do in syscall_trace_leave() */
+#define _TIF_WORK_SYSCALL_EXIT	(_TIF_NOHZ | _TIF_SYSCALL_TRACE |	\
+				 _TIF_SYSCALL_AUDIT | _TIF_SYSCALL_TRACEPOINT)
+
+/* work to do on interrupt/exception return */
+#define _TIF_WORK_MASK		\
+	(_TIF_SIGPENDING | _TIF_NEED_RESCHED | _TIF_NOTIFY_RESUME |	\
+	 _TIF_NOTIFY_SIGNAL | _TIF_UPROBE)
+/* work to do on any return to u-space */
+#define _TIF_ALLWORK_MASK	(_TIF_NOHZ | _TIF_WORK_MASK |		\
+				 _TIF_WORK_SYSCALL_EXIT |		\
+				 _TIF_SYSCALL_TRACEPOINT)
+
+#endif /* __KERNEL__ */
+#endif /* _ASM_THREAD_INFO_H */
diff --git a/arch/loongarch/include/uapi/asm/ptrace.h b/arch/loongarch/include/uapi/asm/ptrace.h
new file mode 100644
index 000000000000..0d3f29f3cc83
--- /dev/null
+++ b/arch/loongarch/include/uapi/asm/ptrace.h
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Author: Hanlu Li <lihanlu@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _UAPI_ASM_PTRACE_H
+#define _UAPI_ASM_PTRACE_H
+
+#include <linux/types.h>
+
+#ifndef __KERNEL__
+#include <stdint.h>
+#endif
+
+/* For PTRACE_{POKE,PEEK}USR. 0 - 31 are GPRs, 32 is PC, 33 is BADVADDR. */
+#define GPR_BASE	0
+#define GPR_NUM		32
+#define GPR_END		(GPR_BASE + GPR_NUM - 1)
+#define PC		(GPR_END + 1)
+#define BADVADDR	(GPR_END + 2)
+
+/*
+ * This struct defines the way the registers are stored on the stack during a
+ * system call/exception.
+ *
+ * If you add a register here, also add it to regoffset_table[] in
+ * arch/loongarch/kernel/ptrace.c.
+ */
+struct pt_regs {
+	/* Saved main processor registers. */
+	unsigned long regs[32];
+
+	/* Saved special registers. */
+	unsigned long csr_crmd;
+	unsigned long csr_prmd;
+	unsigned long csr_euen;
+	unsigned long csr_ecfg;
+	unsigned long csr_estat;
+	unsigned long csr_epc;
+	unsigned long csr_badvaddr;
+	unsigned long orig_a0;
+	unsigned long __last[0];
+} __aligned(8);
+
+#endif /* _UAPI_ASM_PTRACE_H */
diff --git a/arch/loongarch/kernel/fpu.S b/arch/loongarch/kernel/fpu.S
new file mode 100644
index 000000000000..0a67f1f02712
--- /dev/null
+++ b/arch/loongarch/kernel/fpu.S
@@ -0,0 +1,298 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Author: Lu Zeng <zenglu@loongson.cn>
+ *         Pei Huang <huangpei@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <asm/asm.h>
+#include <asm/asmmacro.h>
+#include <asm/errno.h>
+#include <asm/export.h>
+#include <asm/fpregdef.h>
+#include <asm/loongarchregs.h>
+#include <asm/asm-offsets.h>
+#include <asm/regdef.h>
+
+#undef v0
+#undef v1
+
+#define FPU_SREG_WIDTH	32
+#define SC_FPR0  0
+#define SC_FPR1  (SC_FPR0  + FPU_SREG_WIDTH)
+#define SC_FPR2  (SC_FPR1  + FPU_SREG_WIDTH)
+#define SC_FPR3  (SC_FPR2  + FPU_SREG_WIDTH)
+#define SC_FPR4  (SC_FPR3  + FPU_SREG_WIDTH)
+#define SC_FPR5  (SC_FPR4  + FPU_SREG_WIDTH)
+#define SC_FPR6  (SC_FPR5  + FPU_SREG_WIDTH)
+#define SC_FPR7  (SC_FPR6  + FPU_SREG_WIDTH)
+#define SC_FPR8  (SC_FPR7  + FPU_SREG_WIDTH)
+#define SC_FPR9  (SC_FPR8  + FPU_SREG_WIDTH)
+#define SC_FPR10 (SC_FPR9  + FPU_SREG_WIDTH)
+#define SC_FPR11 (SC_FPR10 + FPU_SREG_WIDTH)
+#define SC_FPR12 (SC_FPR11 + FPU_SREG_WIDTH)
+#define SC_FPR13 (SC_FPR12 + FPU_SREG_WIDTH)
+#define SC_FPR14 (SC_FPR13 + FPU_SREG_WIDTH)
+#define SC_FPR15 (SC_FPR14 + FPU_SREG_WIDTH)
+#define SC_FPR16 (SC_FPR15 + FPU_SREG_WIDTH)
+#define SC_FPR17 (SC_FPR16 + FPU_SREG_WIDTH)
+#define SC_FPR18 (SC_FPR17 + FPU_SREG_WIDTH)
+#define SC_FPR19 (SC_FPR18 + FPU_SREG_WIDTH)
+#define SC_FPR20 (SC_FPR19 + FPU_SREG_WIDTH)
+#define SC_FPR21 (SC_FPR20 + FPU_SREG_WIDTH)
+#define SC_FPR22 (SC_FPR21 + FPU_SREG_WIDTH)
+#define SC_FPR23 (SC_FPR22 + FPU_SREG_WIDTH)
+#define SC_FPR24 (SC_FPR23 + FPU_SREG_WIDTH)
+#define SC_FPR25 (SC_FPR24 + FPU_SREG_WIDTH)
+#define SC_FPR26 (SC_FPR25 + FPU_SREG_WIDTH)
+#define SC_FPR27 (SC_FPR26 + FPU_SREG_WIDTH)
+#define SC_FPR28 (SC_FPR27 + FPU_SREG_WIDTH)
+#define SC_FPR29 (SC_FPR28 + FPU_SREG_WIDTH)
+#define SC_FPR30 (SC_FPR29 + FPU_SREG_WIDTH)
+#define SC_FPR31 (SC_FPR30 + FPU_SREG_WIDTH)
+
+/* preprocessor replaces the fp in ".set fp=64" with $30 otherwise */
+#undef fp
+
+	.macro	EX insn, reg, src, offs
+.ex\@:	\insn	\reg, \src, \offs
+	.section __ex_table,"a"
+	PTR	.ex\@, fault
+	.previous
+	.endm
+
+	.macro sc_save_fp base
+	EX	fst.d $f0,  \base, SC_FPR0
+	EX	fst.d $f1,  \base, SC_FPR1
+	EX	fst.d $f2,  \base, SC_FPR2
+	EX	fst.d $f3,  \base, SC_FPR3
+	EX	fst.d $f4,  \base, SC_FPR4
+	EX	fst.d $f5,  \base, SC_FPR5
+	EX	fst.d $f6,  \base, SC_FPR6
+	EX	fst.d $f7,  \base, SC_FPR7
+	EX	fst.d $f8,  \base, SC_FPR8
+	EX	fst.d $f9,  \base, SC_FPR9
+	EX	fst.d $f10, \base, SC_FPR10
+	EX	fst.d $f11, \base, SC_FPR11
+	EX	fst.d $f12, \base, SC_FPR12
+	EX	fst.d $f13, \base, SC_FPR13
+	EX	fst.d $f14, \base, SC_FPR14
+	EX	fst.d $f15, \base, SC_FPR15
+	EX	fst.d $f16, \base, SC_FPR16
+	EX	fst.d $f17, \base, SC_FPR17
+	EX	fst.d $f18, \base, SC_FPR18
+	EX	fst.d $f19, \base, SC_FPR19
+	EX	fst.d $f20, \base, SC_FPR20
+	EX	fst.d $f21, \base, SC_FPR21
+	EX	fst.d $f22, \base, SC_FPR22
+	EX	fst.d $f23, \base, SC_FPR23
+	EX	fst.d $f24, \base, SC_FPR24
+	EX	fst.d $f25, \base, SC_FPR25
+	EX	fst.d $f26, \base, SC_FPR26
+	EX	fst.d $f27, \base, SC_FPR27
+	EX	fst.d $f28, \base, SC_FPR28
+	EX	fst.d $f29, \base, SC_FPR29
+	EX	fst.d $f30, \base, SC_FPR30
+	EX	fst.d $f31, \base, SC_FPR31
+	.endm
+
+	.macro sc_restore_fp base
+	EX	fld.d $f0,  \base, SC_FPR0
+	EX	fld.d $f1,  \base, SC_FPR1
+	EX	fld.d $f2,  \base, SC_FPR2
+	EX	fld.d $f3,  \base, SC_FPR3
+	EX	fld.d $f4,  \base, SC_FPR4
+	EX	fld.d $f5,  \base, SC_FPR5
+	EX	fld.d $f6,  \base, SC_FPR6
+	EX	fld.d $f7,  \base, SC_FPR7
+	EX	fld.d $f8,  \base, SC_FPR8
+	EX	fld.d $f9,  \base, SC_FPR9
+	EX	fld.d $f10, \base, SC_FPR10
+	EX	fld.d $f11, \base, SC_FPR11
+	EX	fld.d $f12, \base, SC_FPR12
+	EX	fld.d $f13, \base, SC_FPR13
+	EX	fld.d $f14, \base, SC_FPR14
+	EX	fld.d $f15, \base, SC_FPR15
+	EX	fld.d $f16, \base, SC_FPR16
+	EX	fld.d $f17, \base, SC_FPR17
+	EX	fld.d $f18, \base, SC_FPR18
+	EX	fld.d $f19, \base, SC_FPR19
+	EX	fld.d $f20, \base, SC_FPR20
+	EX	fld.d $f21, \base, SC_FPR21
+	EX	fld.d $f22, \base, SC_FPR22
+	EX	fld.d $f23, \base, SC_FPR23
+	EX	fld.d $f24, \base, SC_FPR24
+	EX	fld.d $f25, \base, SC_FPR25
+	EX	fld.d $f26, \base, SC_FPR26
+	EX	fld.d $f27, \base, SC_FPR27
+	EX	fld.d $f28, \base, SC_FPR28
+	EX	fld.d $f29, \base, SC_FPR29
+	EX	fld.d $f30, \base, SC_FPR30
+	EX	fld.d $f31, \base, SC_FPR31
+	.endm
+
+	.macro sc_save_fcc base, tmp0, tmp1
+	movcf2gr	\tmp0, $fcc0
+	move	\tmp1, \tmp0
+	movcf2gr	\tmp0, $fcc1
+	bstrins.d	\tmp1, \tmp0, 15, 8
+	movcf2gr	\tmp0, $fcc2
+	bstrins.d	\tmp1, \tmp0, 23, 16
+	movcf2gr	\tmp0, $fcc3
+	bstrins.d	\tmp1, \tmp0, 31, 24
+	movcf2gr	\tmp0, $fcc4
+	bstrins.d	\tmp1, \tmp0, 39, 32
+	movcf2gr	\tmp0, $fcc5
+	bstrins.d	\tmp1, \tmp0, 47, 40
+	movcf2gr	\tmp0, $fcc6
+	bstrins.d	\tmp1, \tmp0, 55, 48
+	movcf2gr	\tmp0, $fcc7
+	bstrins.d	\tmp1, \tmp0, 63, 56
+	EX	st.d \tmp1, \base, 0
+	.endm
+
+	.macro sc_restore_fcc base, tmp0, tmp1
+	EX	ld.d \tmp0, \base, 0
+	bstrpick.d	\tmp1, \tmp0, 7, 0
+	movgr2cf	$fcc0, \tmp1
+	bstrpick.d	\tmp1, \tmp0, 15, 8
+	movgr2cf	$fcc1, \tmp1
+	bstrpick.d	\tmp1, \tmp0, 23, 16
+	movgr2cf	$fcc2, \tmp1
+	bstrpick.d	\tmp1, \tmp0, 31, 24
+	movgr2cf	$fcc3, \tmp1
+	bstrpick.d	\tmp1, \tmp0, 39, 32
+	movgr2cf	$fcc4, \tmp1
+	bstrpick.d	\tmp1, \tmp0, 47, 40
+	movgr2cf	$fcc5, \tmp1
+	bstrpick.d	\tmp1, \tmp0, 55, 48
+	movgr2cf	$fcc6, \tmp1
+	bstrpick.d	\tmp1, \tmp0, 63, 56
+	movgr2cf	$fcc7, \tmp1
+	.endm
+
+	.macro sc_save_fcsr base, tmp0
+	movfcsr2gr	\tmp0, fcsr0
+	EX	st.w \tmp0, \base, 0
+	.endm
+
+	.macro sc_restore_fcsr base, tmp0
+	EX	ld.w \tmp0, \base, 0
+	movgr2fcsr	fcsr0, \tmp0
+	.endm
+
+	.macro sc_save_vcsr base, tmp0
+	movfcsr2gr	\tmp0, vcsr16
+	EX	st.w \tmp0, \base, 0
+	.endm
+
+	.macro sc_restore_vcsr base, tmp0
+	EX	ld.w \tmp0, \base, 0
+	movgr2fcsr	vcsr16, \tmp0
+	.endm
+
+/*
+ * Save a thread's fp context.
+ */
+SYM_FUNC_START(_save_fp)
+	fpu_save_double a0 t1			# clobbers t1
+	fpu_save_csr	a0 t1
+	fpu_save_cc	a0 t1 t2		# clobbers t1, t2
+	jirl zero, ra, 0
+SYM_FUNC_END(_save_fp)
+EXPORT_SYMBOL(_save_fp)
+
+/*
+ * Restore a thread's fp context.
+ */
+SYM_FUNC_START(_restore_fp)
+	fpu_restore_double a0 t1		# clobbers t1
+	fpu_restore_csr	a0 t1
+	fpu_restore_cc	a0 t1 t2		# clobbers t1, t2
+	jirl zero, ra, 0
+SYM_FUNC_END(_restore_fp)
+
+/*
+ * Load the FPU with signalling NANS.  This bit pattern we're using has
+ * the property that no matter whether considered as single or as double
+ * precision represents signaling NANS.
+ *
+ * The value to initialize fcsr0 to comes in $a0.
+ */
+
+SYM_FUNC_START(_init_fpu)
+	csrrd	t0, LOONGARCH_CSR_EUEN
+	li.w	t1, CSR_EUEN_FPEN
+	or	t0, t0, t1
+	csrwr	t0, LOONGARCH_CSR_EUEN
+
+	movgr2fcsr	fcsr0, a0
+
+	li.w	t1, -1				# SNaN
+
+	movgr2fr.d	$f0, t1
+	movgr2fr.d	$f1, t1
+	movgr2fr.d	$f2, t1
+	movgr2fr.d	$f3, t1
+	movgr2fr.d	$f4, t1
+	movgr2fr.d	$f5, t1
+	movgr2fr.d	$f6, t1
+	movgr2fr.d	$f7, t1
+	movgr2fr.d	$f8, t1
+	movgr2fr.d	$f9, t1
+	movgr2fr.d	$f10, t1
+	movgr2fr.d	$f11, t1
+	movgr2fr.d	$f12, t1
+	movgr2fr.d	$f13, t1
+	movgr2fr.d	$f14, t1
+	movgr2fr.d	$f15, t1
+	movgr2fr.d	$f16, t1
+	movgr2fr.d	$f17, t1
+	movgr2fr.d	$f18, t1
+	movgr2fr.d	$f19, t1
+	movgr2fr.d	$f20, t1
+	movgr2fr.d	$f21, t1
+	movgr2fr.d	$f22, t1
+	movgr2fr.d	$f23, t1
+	movgr2fr.d	$f24, t1
+	movgr2fr.d	$f25, t1
+	movgr2fr.d	$f26, t1
+	movgr2fr.d	$f27, t1
+	movgr2fr.d	$f28, t1
+	movgr2fr.d	$f29, t1
+	movgr2fr.d	$f30, t1
+	movgr2fr.d	$f31, t1
+
+	jirl zero, ra, 0
+SYM_FUNC_END(_init_fpu)
+
+/*
+ * a0: fpregs
+ * a1: fcc
+ * a2: fcsr
+ */
+SYM_FUNC_START(_save_fp_context)
+	sc_save_fp a0
+	sc_save_fcc a1 t1 t2
+	sc_save_fcsr a2 t1
+	li.w	a0, 0					# success
+	jirl zero, ra, 0
+SYM_FUNC_END(_save_fp_context)
+
+/*
+ * a0: fpregs
+ * a1: fcc
+ * a2: fcsr
+ */
+SYM_FUNC_START(_restore_fp_context)
+	sc_restore_fp a0
+	sc_restore_fcc a1 t1 t2
+	sc_restore_fcsr a2 t1
+	li.w	a0, 0					# success
+	jirl zero, ra, 0
+SYM_FUNC_END(_restore_fp_context)
+
+	.type	fault, @function
+fault:	li.w	a0, -EFAULT				# failure
+	jirl zero, ra, 0
diff --git a/arch/loongarch/kernel/idle.c b/arch/loongarch/kernel/idle.c
new file mode 100644
index 000000000000..f85b64b8b1f7
--- /dev/null
+++ b/arch/loongarch/kernel/idle.c
@@ -0,0 +1,17 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * LoongArch idle loop support.
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/cpu.h>
+#include <linux/export.h>
+#include <linux/irqflags.h>
+#include <asm/cpu.h>
+#include <asm/idle.h>
+
+void arch_cpu_idle(void)
+{
+	local_irq_enable();
+	__arch_cpu_idle();
+}
diff --git a/arch/loongarch/kernel/process.c b/arch/loongarch/kernel/process.c
new file mode 100644
index 000000000000..214528503b8b
--- /dev/null
+++ b/arch/loongarch/kernel/process.c
@@ -0,0 +1,267 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/sched/debug.h>
+#include <linux/sched/task.h>
+#include <linux/sched/task_stack.h>
+#include <linux/tick.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/stddef.h>
+#include <linux/unistd.h>
+#include <linux/export.h>
+#include <linux/ptrace.h>
+#include <linux/mman.h>
+#include <linux/personality.h>
+#include <linux/sys.h>
+#include <linux/init.h>
+#include <linux/completion.h>
+#include <linux/kallsyms.h>
+#include <linux/random.h>
+#include <linux/prctl.h>
+#include <linux/nmi.h>
+#include <linux/cpu.h>
+
+#include <asm/abi.h>
+#include <asm/asm.h>
+#include <asm/bootinfo.h>
+#include <asm/cpu.h>
+#include <asm/fpu.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/loongarchregs.h>
+#include <asm/processor.h>
+#include <asm/reg.h>
+#include <asm/io.h>
+#include <asm/elf.h>
+#include <asm/inst.h>
+#include <asm/irq_regs.h>
+
+/*
+ * Idle related variables and functions
+ */
+
+unsigned long boot_option_idle_override = IDLE_NO_OVERRIDE;
+EXPORT_SYMBOL(boot_option_idle_override);
+
+#ifdef CONFIG_HOTPLUG_CPU
+void arch_cpu_idle_dead(void)
+{
+	play_dead();
+}
+#endif
+
+asmlinkage void ret_from_fork(void);
+asmlinkage void ret_from_kernel_thread(void);
+
+void start_thread(struct pt_regs *regs, unsigned long pc, unsigned long sp)
+{
+	unsigned long crmd;
+	unsigned long prmd;
+	unsigned long euen;
+
+	/* New thread loses kernel privileges. */
+	crmd = regs->csr_crmd & ~(PLV_MASK);
+	crmd |= PLV_USER;
+	regs->csr_crmd = crmd;
+
+	prmd = regs->csr_prmd & ~(PLV_MASK);
+	prmd |= PLV_USER;
+	regs->csr_prmd = prmd;
+
+	euen = regs->csr_euen & ~(CSR_EUEN_FPEN);
+	regs->csr_euen = euen;
+	lose_fpu(0);
+
+	clear_thread_flag(TIF_LSX_CTX_LIVE);
+	clear_thread_flag(TIF_LASX_CTX_LIVE);
+	clear_used_math();
+	regs->csr_epc = pc;
+	regs->regs[3] = sp;
+}
+
+void exit_thread(struct task_struct *tsk)
+{
+}
+
+int arch_dup_task_struct(struct task_struct *dst, struct task_struct *src)
+{
+	/*
+	 * Save any process state which is live in hardware registers to the
+	 * parent context prior to duplication. This prevents the new child
+	 * state becoming stale if the parent is preempted before copy_thread()
+	 * gets a chance to save the parent's live hardware registers to the
+	 * child context.
+	 */
+	preempt_disable();
+
+	if (is_fpu_owner())
+		save_fp(current);
+
+	preempt_enable();
+
+	if (used_math())
+		memcpy(dst, src, sizeof(struct task_struct));
+	else
+		memcpy(dst, src, offsetof(struct task_struct, thread.fpu.fpr));
+
+	return 0;
+}
+
+/*
+ * Copy architecture-specific thread state
+ */
+int copy_thread(unsigned long clone_flags, unsigned long usp,
+	unsigned long kthread_arg, struct task_struct *p, unsigned long tls)
+{
+	unsigned long childksp;
+	struct pt_regs *childregs, *regs = current_pt_regs();
+
+	childksp = (unsigned long)task_stack_page(p) + THREAD_SIZE - 32;
+
+	/* set up new TSS. */
+	childregs = (struct pt_regs *) childksp - 1;
+	/*  Put the stack after the struct pt_regs.  */
+	childksp = (unsigned long) childregs;
+	p->thread.csr_euen = 0;
+	p->thread.csr_crmd = csr_readl(LOONGARCH_CSR_CRMD);
+	p->thread.csr_prmd = csr_readl(LOONGARCH_CSR_PRMD);
+	p->thread.csr_ecfg = csr_readl(LOONGARCH_CSR_ECFG);
+	if (unlikely(p->flags & PF_KTHREAD)) {
+		/* kernel thread */
+		unsigned long csr_crmd = p->thread.csr_crmd;
+		unsigned long csr_euen = p->thread.csr_euen;
+		unsigned long csr_prmd = p->thread.csr_prmd;
+		unsigned long csr_ecfg = p->thread.csr_ecfg;
+
+		memset(childregs, 0, sizeof(struct pt_regs));
+		p->thread.reg23 = usp; /* fn */
+		p->thread.reg24 = kthread_arg;
+		p->thread.reg03 = childksp;
+		p->thread.reg01 = (unsigned long) ret_from_kernel_thread;
+		childregs->csr_euen = csr_euen;
+		childregs->csr_prmd = csr_prmd;
+		childregs->csr_ecfg = csr_ecfg;
+		childregs->csr_crmd = csr_crmd;
+		return 0;
+	}
+
+	/* user thread */
+	*childregs = *regs;
+	childregs->regs[7] = 0; /* Clear error flag */
+	childregs->regs[4] = 0; /* Child gets zero as return value */
+	if (usp)
+		childregs->regs[3] = usp;
+
+	p->thread.reg03 = (unsigned long) childregs;
+	p->thread.reg01 = (unsigned long) ret_from_fork;
+
+	/*
+	 * New tasks lose permission to use the fpu. This accelerates context
+	 * switching for most programs since they don't use the fpu.
+	 */
+	childregs->csr_euen = 0;
+
+	clear_tsk_thread_flag(p, TIF_USEDFPU);
+	clear_tsk_thread_flag(p, TIF_USEDSIMD);
+	clear_tsk_thread_flag(p, TIF_LSX_CTX_LIVE);
+	clear_tsk_thread_flag(p, TIF_LASX_CTX_LIVE);
+
+	if (clone_flags & CLONE_SETTLS)
+		childregs->regs[2] = tls;
+
+	return 0;
+}
+
+unsigned long get_wchan(struct task_struct *task)
+{
+	return 0;
+}
+
+unsigned long stack_top(void)
+{
+	unsigned long top = TASK_SIZE & PAGE_MASK;
+
+	/* Space for the VDSO & data page */
+	top -= PAGE_ALIGN(current->thread.abi->vdso->size);
+	top -= PAGE_SIZE;
+
+	/* Space to randomize the VDSO base */
+	if (current->flags & PF_RANDOMIZE)
+		top -= VDSO_RANDOMIZE_SIZE;
+
+	return top;
+}
+
+/*
+ * Don't forget that the stack pointer must be aligned on a 8 bytes
+ * boundary for 32-bits ABI and 16 bytes for 64-bits ABI.
+ */
+unsigned long arch_align_stack(unsigned long sp)
+{
+	if (!(current->personality & ADDR_NO_RANDOMIZE) && randomize_va_space)
+		sp -= get_random_int() & ~PAGE_MASK;
+
+	return sp & ALMASK;
+}
+
+static DEFINE_PER_CPU(call_single_data_t, backtrace_csd);
+static struct cpumask backtrace_csd_busy;
+
+static void handle_backtrace(void *info)
+{
+	nmi_cpu_backtrace(get_irq_regs());
+	cpumask_clear_cpu(smp_processor_id(), &backtrace_csd_busy);
+}
+
+static void raise_backtrace(cpumask_t *mask)
+{
+	call_single_data_t *csd;
+	int cpu;
+
+	for_each_cpu(cpu, mask) {
+		/*
+		 * If we previously sent an IPI to the target CPU & it hasn't
+		 * cleared its bit in the busy cpumask then it didn't handle
+		 * our previous IPI & it's not safe for us to reuse the
+		 * call_single_data_t.
+		 */
+		if (cpumask_test_and_set_cpu(cpu, &backtrace_csd_busy)) {
+			pr_warn("Unable to send backtrace IPI to CPU%u - perhaps it hung?\n",
+				cpu);
+			continue;
+		}
+
+		csd = &per_cpu(backtrace_csd, cpu);
+		csd->func = handle_backtrace;
+		smp_call_function_single_async(cpu, csd);
+	}
+}
+
+void arch_trigger_cpumask_backtrace(const cpumask_t *mask, bool exclude_self)
+{
+	nmi_trigger_cpumask_backtrace(mask, exclude_self, raise_backtrace);
+}
+
+#ifdef CONFIG_64BIT
+void loongarch_dump_regs64(u64 *uregs, const struct pt_regs *regs)
+{
+	unsigned int i;
+
+	for (i = LOONGARCH64_EF_R1; i <= LOONGARCH64_EF_R31; i++) {
+		uregs[i] = regs->regs[i - LOONGARCH64_EF_R0];
+	}
+
+	uregs[LOONGARCH64_EF_CSR_EPC] = regs->csr_epc;
+	uregs[LOONGARCH64_EF_CSR_BADVADDR] = regs->csr_badvaddr;
+	uregs[LOONGARCH64_EF_CSR_CRMD] = regs->csr_crmd;
+	uregs[LOONGARCH64_EF_CSR_PRMD] = regs->csr_prmd;
+	uregs[LOONGARCH64_EF_CSR_EUEN] = regs->csr_euen;
+	uregs[LOONGARCH64_EF_CSR_ECFG] = regs->csr_ecfg;
+	uregs[LOONGARCH64_EF_CSR_ESTAT] = regs->csr_estat;
+}
+#endif /* CONFIG_64BIT */
diff --git a/arch/loongarch/kernel/ptrace.c b/arch/loongarch/kernel/ptrace.c
new file mode 100644
index 000000000000..fa0def6a67cd
--- /dev/null
+++ b/arch/loongarch/kernel/ptrace.c
@@ -0,0 +1,459 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Hanlu Li <lihanlu@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/compiler.h>
+#include <linux/context_tracking.h>
+#include <linux/elf.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/sched/task_stack.h>
+#include <linux/mm.h>
+#include <linux/errno.h>
+#include <linux/ptrace.h>
+#include <linux/regset.h>
+#include <linux/smp.h>
+#include <linux/security.h>
+#include <linux/stddef.h>
+#include <linux/tracehook.h>
+#include <linux/audit.h>
+#include <linux/seccomp.h>
+#include <linux/ftrace.h>
+
+#include <asm/byteorder.h>
+#include <asm/cpu.h>
+#include <asm/cpu-info.h>
+#include <asm/fpu.h>
+#include <asm/loongarchregs.h>
+#include <asm/pgtable.h>
+#include <asm/page.h>
+#include <asm/processor.h>
+#include <asm/syscall.h>
+#include <linux/uaccess.h>
+#include <asm/bootinfo.h>
+#include <asm/reg.h>
+
+#define CREATE_TRACE_POINTS
+#include <trace/events/syscalls.h>
+
+static void init_fp_ctx(struct task_struct *target)
+{
+	/* The target already has context */
+	if (tsk_used_math(target))
+		return;
+
+	/* Begin with data registers set to all 1s... */
+	memset(&target->thread.fpu.fpr, ~0, sizeof(target->thread.fpu.fpr));
+	set_stopped_child_used_math(target);
+}
+
+/*
+ * Called by kernel/ptrace.c when detaching..
+ *
+ * Make sure single step bits etc are not set.
+ */
+void ptrace_disable(struct task_struct *child)
+{
+	/* Don't load the watchpoint registers for the ex-child. */
+	clear_tsk_thread_flag(child, TIF_LOAD_WATCH);
+}
+
+/* regset get/set implementations */
+
+static int gpr_get(struct task_struct *target,
+		   const struct user_regset *regset,
+		   struct membuf to)
+{
+	int r;
+	struct pt_regs *regs = task_pt_regs(target);
+
+	r = membuf_write(&to, &regs->regs, sizeof(u64) * GPR_NUM);
+	r = membuf_write(&to, &regs->csr_epc, sizeof(u64));
+	r = membuf_write(&to, &regs->csr_badvaddr, sizeof(u64));
+
+	return r;
+}
+
+static int gpr_set(struct task_struct *target,
+		   const struct user_regset *regset,
+		   unsigned int pos, unsigned int count,
+		   const void *kbuf, const void __user *ubuf)
+{
+	int err;
+	int epc_start = sizeof(u64) * GPR_NUM;
+	int badvaddr_start = epc_start + sizeof(u64);
+	struct pt_regs *regs = task_pt_regs(target);
+
+	err = user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+				 &regs->regs,
+				 0, epc_start);
+	err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+				 &regs->csr_epc,
+				 epc_start, epc_start + sizeof(u64));
+	err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+				 &regs->csr_badvaddr,
+				 badvaddr_start, badvaddr_start + sizeof(u64));
+
+	return err;
+}
+
+
+/*
+ * Get the general floating-point registers.
+ */
+static int gfpr_get(struct task_struct *target, struct membuf *to)
+{
+	return membuf_write(to, &target->thread.fpu, sizeof(elf_fpreg_t) * NUM_FPU_REGS);
+}
+
+static int gfpr_get_simd(struct task_struct *target, struct membuf *to)
+{
+	int i, r;
+	u64 fpr_val;
+
+	BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t));
+	for (i = 0; i < NUM_FPU_REGS; i++) {
+		fpr_val = get_fpr64(&target->thread.fpu.fpr[i], 0);
+		r = membuf_write(to, &fpr_val, sizeof(elf_fpreg_t));
+	}
+
+	return r;
+}
+
+/*
+ * Choose the appropriate helper for general registers, and then copy
+ * the FCC and FCSR registers separately.
+ */
+static int fpr_get(struct task_struct *target,
+		   const struct user_regset *regset,
+		   struct membuf to)
+{
+	int r;
+
+	if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t))
+		r = gfpr_get(target, &to);
+	else
+		r = gfpr_get_simd(target, &to);
+
+	r = membuf_write(&to, &target->thread.fpu.fcc, sizeof(target->thread.fpu.fcc));
+	r = membuf_write(&to, &target->thread.fpu.fcsr, sizeof(target->thread.fpu.fcsr));
+
+	return r;
+}
+
+static int gfpr_set(struct task_struct *target,
+		    unsigned int *pos, unsigned int *count,
+		    const void **kbuf, const void __user **ubuf)
+{
+	return user_regset_copyin(pos, count, kbuf, ubuf,
+				  &target->thread.fpu,
+				  0, NUM_FPU_REGS * sizeof(elf_fpreg_t));
+}
+
+static int gfpr_set_simd(struct task_struct *target,
+		       unsigned int *pos, unsigned int *count,
+		       const void **kbuf, const void __user **ubuf)
+{
+	int i, err;
+	u64 fpr_val;
+
+	BUILD_BUG_ON(sizeof(fpr_val) != sizeof(elf_fpreg_t));
+	for (i = 0; i < NUM_FPU_REGS && *count > 0; i++) {
+		err = user_regset_copyin(pos, count, kbuf, ubuf,
+					 &fpr_val, i * sizeof(elf_fpreg_t),
+					 (i + 1) * sizeof(elf_fpreg_t));
+		if (err)
+			return err;
+		set_fpr64(&target->thread.fpu.fpr[i], 0, fpr_val);
+	}
+
+	return 0;
+}
+
+/*
+ * Choose the appropriate helper for general registers, and then copy
+ * the FCC register separately.
+ */
+static int fpr_set(struct task_struct *target,
+		   const struct user_regset *regset,
+		   unsigned int pos, unsigned int count,
+		   const void *kbuf, const void __user *ubuf)
+{
+	const int fcc_start = NUM_FPU_REGS * sizeof(elf_fpreg_t);
+	const int fcc_end = fcc_start + sizeof(u64);
+	int err;
+
+	BUG_ON(count % sizeof(elf_fpreg_t));
+	if (pos + count > sizeof(elf_fpregset_t))
+		return -EIO;
+
+	init_fp_ctx(target);
+
+	if (sizeof(target->thread.fpu.fpr[0]) == sizeof(elf_fpreg_t))
+		err = gfpr_set(target, &pos, &count, &kbuf, &ubuf);
+	else
+		err = gfpr_set_simd(target, &pos, &count, &kbuf, &ubuf);
+	if (err)
+		return err;
+
+	if (count > 0)
+		err |= user_regset_copyin(&pos, &count, &kbuf, &ubuf,
+					  &target->thread.fpu.fcc,
+					  fcc_start, fcc_end);
+
+	return err;
+}
+
+static int cfg_get(struct task_struct *target,
+		   const struct user_regset *regset,
+		   struct membuf to)
+{
+	int i, r;
+	u32 cfg_val;
+
+	i = 0;
+	while (to.left > 0) {
+		cfg_val = read_cpucfg(i++);
+		r = membuf_write(&to, &cfg_val, sizeof(u32));
+	}
+
+	return r;
+}
+
+/*
+ * CFG registers are read-only.
+ */
+static int cfg_set(struct task_struct *target,
+		   const struct user_regset *regset,
+		   unsigned int pos, unsigned int count,
+		   const void *kbuf, const void __user *ubuf)
+{
+	return 0;
+}
+
+struct pt_regs_offset {
+	const char *name;
+	int offset;
+};
+
+#define REG_OFFSET_NAME(n, r) {.name = #n, .offset = offsetof(struct pt_regs, r)}
+#define REG_OFFSET_END {.name = NULL, .offset = 0}
+
+static const struct pt_regs_offset regoffset_table[] = {
+	REG_OFFSET_NAME(r0, regs[0]),
+	REG_OFFSET_NAME(r1, regs[1]),
+	REG_OFFSET_NAME(r2, regs[2]),
+	REG_OFFSET_NAME(r3, regs[3]),
+	REG_OFFSET_NAME(r4, regs[4]),
+	REG_OFFSET_NAME(r5, regs[5]),
+	REG_OFFSET_NAME(r6, regs[6]),
+	REG_OFFSET_NAME(r7, regs[7]),
+	REG_OFFSET_NAME(r8, regs[8]),
+	REG_OFFSET_NAME(r9, regs[9]),
+	REG_OFFSET_NAME(r10, regs[10]),
+	REG_OFFSET_NAME(r11, regs[11]),
+	REG_OFFSET_NAME(r12, regs[12]),
+	REG_OFFSET_NAME(r13, regs[13]),
+	REG_OFFSET_NAME(r14, regs[14]),
+	REG_OFFSET_NAME(r15, regs[15]),
+	REG_OFFSET_NAME(r16, regs[16]),
+	REG_OFFSET_NAME(r17, regs[17]),
+	REG_OFFSET_NAME(r18, regs[18]),
+	REG_OFFSET_NAME(r19, regs[19]),
+	REG_OFFSET_NAME(r20, regs[20]),
+	REG_OFFSET_NAME(r21, regs[21]),
+	REG_OFFSET_NAME(r22, regs[22]),
+	REG_OFFSET_NAME(r23, regs[23]),
+	REG_OFFSET_NAME(r24, regs[24]),
+	REG_OFFSET_NAME(r25, regs[25]),
+	REG_OFFSET_NAME(r26, regs[26]),
+	REG_OFFSET_NAME(r27, regs[27]),
+	REG_OFFSET_NAME(r28, regs[28]),
+	REG_OFFSET_NAME(r29, regs[29]),
+	REG_OFFSET_NAME(r30, regs[30]),
+	REG_OFFSET_NAME(r31, regs[31]),
+	REG_OFFSET_NAME(csr_crmd, csr_crmd),
+	REG_OFFSET_NAME(csr_prmd, csr_prmd),
+	REG_OFFSET_NAME(csr_euen, csr_euen),
+	REG_OFFSET_NAME(csr_ecfg, csr_ecfg),
+	REG_OFFSET_NAME(csr_estat, csr_estat),
+	REG_OFFSET_NAME(csr_epc, csr_epc),
+	REG_OFFSET_NAME(csr_badvaddr, csr_badvaddr),
+	REG_OFFSET_END,
+};
+
+/**
+ * regs_query_register_offset() - query register offset from its name
+ * @name:       the name of a register
+ *
+ * regs_query_register_offset() returns the offset of a register in struct
+ * pt_regs from its name. If the name is invalid, this returns -EINVAL;
+ */
+int regs_query_register_offset(const char *name)
+{
+	const struct pt_regs_offset *roff;
+
+	for (roff = regoffset_table; roff->name != NULL; roff++)
+		if (!strcmp(roff->name, name))
+			return roff->offset;
+	return -EINVAL;
+}
+
+enum loongarch_regset {
+	REGSET_GPR,
+	REGSET_FPR,
+	REGSET_CPUCFG,
+};
+
+static const struct user_regset loongarch64_regsets[] = {
+	[REGSET_GPR] = {
+		.core_note_type	= NT_PRSTATUS,
+		.n		= ELF_NGREG,
+		.size		= sizeof(elf_greg_t),
+		.align		= sizeof(elf_greg_t),
+		.regset_get	= gpr_get,
+		.set		= gpr_set,
+	},
+	[REGSET_FPR] = {
+		.core_note_type	= NT_PRFPREG,
+		.n		= ELF_NFPREG,
+		.size		= sizeof(elf_fpreg_t),
+		.align		= sizeof(elf_fpreg_t),
+		.regset_get	= fpr_get,
+		.set		= fpr_set,
+	},
+	[REGSET_CPUCFG] = {
+		.core_note_type	= NT_LOONGARCH_CPUCFG,
+		.n		= 64,
+		.size		= sizeof(u32),
+		.align		= sizeof(u32),
+		.regset_get	= cfg_get,
+		.set		= cfg_set,
+	},
+};
+
+static const struct user_regset_view user_loongarch64_view = {
+	.name		= "loongarch64",
+	.e_machine	= ELF_ARCH,
+	.regsets	= loongarch64_regsets,
+	.n		= ARRAY_SIZE(loongarch64_regsets),
+};
+
+
+const struct user_regset_view *task_user_regset_view(struct task_struct *task)
+{
+	return &user_loongarch64_view;
+}
+
+static inline int read_user(struct task_struct *target, unsigned long addr,
+			    unsigned long __user *data)
+{
+	unsigned long tmp = 0;
+
+	switch (addr) {
+	case 0 ... 31:
+		tmp = task_pt_regs(target)->regs[addr];
+		break;
+	case PC:
+		tmp = task_pt_regs(target)->csr_epc;
+		break;
+	case BADVADDR:
+		tmp = task_pt_regs(target)->csr_badvaddr;
+		break;
+	default:
+		return -EIO;
+	}
+
+	return put_user(tmp, data);
+}
+
+static inline int write_user(struct task_struct *target, unsigned long addr,
+			    unsigned long data)
+{
+	switch (addr) {
+	case 0 ... 31:
+		task_pt_regs(target)->regs[addr] = data;
+		break;
+	case PC:
+		task_pt_regs(target)->csr_epc = data;
+		break;
+	case BADVADDR:
+		task_pt_regs(target)->csr_badvaddr = data;
+		break;
+	default:
+		return -EIO;
+	}
+
+	return 0;
+}
+
+long arch_ptrace(struct task_struct *child, long request,
+		 unsigned long addr, unsigned long data)
+{
+	int ret;
+	unsigned long __user *datap = (void __user *) data;
+
+	switch (request) {
+	case PTRACE_PEEKUSR:
+		ret = read_user(child, addr, datap);
+		break;
+
+	case PTRACE_POKEUSR:
+		ret = write_user(child, addr, data);
+		break;
+
+	default:
+		ret = ptrace_request(child, request, addr, data);
+		break;
+	}
+
+	return ret;
+}
+
+asmlinkage long syscall_trace_enter(struct pt_regs *regs, long syscall)
+{
+	user_exit();
+
+	if (test_thread_flag(TIF_SYSCALL_TRACE))
+		if (tracehook_report_syscall_entry(regs))
+			syscall_set_nr(current, regs, -1);
+
+#ifdef CONFIG_SECCOMP
+	if (secure_computing() == -1)
+		return -1;
+#endif
+
+	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
+		trace_sys_enter(regs, syscall);
+
+	audit_syscall_entry(syscall, regs->regs[4], regs->regs[5],
+			    regs->regs[6], regs->regs[7]);
+
+	/* set errno for negative syscall number. */
+	if (syscall < 0)
+		syscall_set_return_value(current, regs, -ENOSYS, 0);
+	return syscall;
+}
+
+asmlinkage void syscall_trace_leave(struct pt_regs *regs)
+{
+	/*
+	 * We may come here right after calling schedule_user()
+	 * or do_notify_resume(), in which case we can be in RCU
+	 * user mode.
+	 */
+	user_exit();
+
+	audit_syscall_exit(regs);
+
+	if (unlikely(test_thread_flag(TIF_SYSCALL_TRACEPOINT)))
+		trace_sys_exit(regs, regs_return_value(regs));
+
+	if (test_thread_flag(TIF_SYSCALL_TRACE))
+		tracehook_report_syscall_exit(regs, 0);
+
+	user_enter();
+}
diff --git a/arch/loongarch/kernel/switch.S b/arch/loongarch/kernel/switch.S
new file mode 100644
index 000000000000..0955d9938044
--- /dev/null
+++ b/arch/loongarch/kernel/switch.S
@@ -0,0 +1,48 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <asm/asm.h>
+#include <asm/loongarchregs.h>
+#include <asm/asm-offsets.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+#include <asm/thread_info.h>
+
+#include <asm/asmmacro.h>
+
+/*
+ * task_struct *resume(task_struct *prev, task_struct *next,
+ *		       struct thread_info *next_ti)
+ */
+	.align	5
+SYM_FUNC_START(resume)
+	csrrd	t1, LOONGARCH_CSR_PRMD
+	stptr.d	t1, a0, THREAD_CSRPRMD
+	csrrd	t1, LOONGARCH_CSR_CRMD
+	stptr.d	t1, a0, THREAD_CSRCRMD
+	csrrd	t1, LOONGARCH_CSR_ECFG
+	stptr.d	t1, a0, THREAD_CSRECFG
+	csrrd	t1, LOONGARCH_CSR_EUEN
+	stptr.d	t1, a0, THREAD_CSREUEN
+	cpu_save_nonscratch a0
+	stptr.d	ra, a0, THREAD_REG01
+
+	/*
+	 * The order of restoring the registers takes care of the race
+	 * updating $28, $29 and kernelsp without disabling ints.
+	 */
+	move	tp, a2
+	cpu_restore_nonscratch a1
+
+	li.w	t0, _THREAD_SIZE - 32
+	PTR_ADDU	t0, t0, tp
+	set_saved_sp	t0, t1, t2
+
+	ldptr.d	t1, a1, THREAD_CSRPRMD
+	csrwr	t1, LOONGARCH_CSR_PRMD
+	ldptr.d	t1, a1, THREAD_CSREUEN
+	csrwr	t1, LOONGARCH_CSR_EUEN
+
+	jr	ra
+SYM_FUNC_END(resume)
-- 
2.27.0


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

* [PATCH 08/19] LoongArch: Add memory management
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (5 preceding siblings ...)
  2021-07-06  4:18 ` [PATCH 07/19] LoongArch: Add process management Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06 10:16   ` Arnd Bergmann
  2021-08-16  1:57   ` Guo Ren
  2021-07-06  4:18 ` [PATCH 09/19] LoongArch: Add system call support Huacai Chen
                   ` (13 subsequent siblings)
  20 siblings, 2 replies; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

This patch adds memory management support for LoongArch, including:
cache and tlb management, page fault handling and ioremap/mmap support.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 arch/loongarch/include/asm/cache.h      |  15 +
 arch/loongarch/include/asm/cacheflush.h |  79 ++++
 arch/loongarch/include/asm/cacheops.h   |  32 ++
 arch/loongarch/include/asm/fixmap.h     |  15 +
 arch/loongarch/include/asm/hugetlb.h    |  79 ++++
 arch/loongarch/include/asm/kmalloc.h    |  10 +
 arch/loongarch/include/asm/shmparam.h   |  12 +
 arch/loongarch/include/asm/sparsemem.h  |  21 ++
 arch/loongarch/include/asm/tlb.h        | 216 +++++++++++
 arch/loongarch/include/asm/tlbflush.h   |  36 ++
 arch/loongarch/include/asm/vmalloc.h    |   4 +
 arch/loongarch/mm/cache.c               | 194 ++++++++++
 arch/loongarch/mm/extable.c             |  22 ++
 arch/loongarch/mm/fault.c               | 289 +++++++++++++++
 arch/loongarch/mm/hugetlbpage.c         |  87 +++++
 arch/loongarch/mm/init.c                | 199 ++++++++++
 arch/loongarch/mm/ioremap.c             |  27 ++
 arch/loongarch/mm/maccess.c             |  10 +
 arch/loongarch/mm/mmap.c                | 204 ++++++++++
 arch/loongarch/mm/page.S                |  93 +++++
 arch/loongarch/mm/pgtable-64.c          | 116 ++++++
 arch/loongarch/mm/pgtable.c             |  24 ++
 arch/loongarch/mm/tlb.c                 | 278 ++++++++++++++
 arch/loongarch/mm/tlbex.S               | 473 ++++++++++++++++++++++++
 24 files changed, 2535 insertions(+)
 create mode 100644 arch/loongarch/include/asm/cache.h
 create mode 100644 arch/loongarch/include/asm/cacheflush.h
 create mode 100644 arch/loongarch/include/asm/cacheops.h
 create mode 100644 arch/loongarch/include/asm/fixmap.h
 create mode 100644 arch/loongarch/include/asm/hugetlb.h
 create mode 100644 arch/loongarch/include/asm/kmalloc.h
 create mode 100644 arch/loongarch/include/asm/shmparam.h
 create mode 100644 arch/loongarch/include/asm/sparsemem.h
 create mode 100644 arch/loongarch/include/asm/tlb.h
 create mode 100644 arch/loongarch/include/asm/tlbflush.h
 create mode 100644 arch/loongarch/include/asm/vmalloc.h
 create mode 100644 arch/loongarch/mm/cache.c
 create mode 100644 arch/loongarch/mm/extable.c
 create mode 100644 arch/loongarch/mm/fault.c
 create mode 100644 arch/loongarch/mm/hugetlbpage.c
 create mode 100644 arch/loongarch/mm/init.c
 create mode 100644 arch/loongarch/mm/ioremap.c
 create mode 100644 arch/loongarch/mm/maccess.c
 create mode 100644 arch/loongarch/mm/mmap.c
 create mode 100644 arch/loongarch/mm/page.S
 create mode 100644 arch/loongarch/mm/pgtable-64.c
 create mode 100644 arch/loongarch/mm/pgtable.c
 create mode 100644 arch/loongarch/mm/tlb.c
 create mode 100644 arch/loongarch/mm/tlbex.S

diff --git a/arch/loongarch/include/asm/cache.h b/arch/loongarch/include/asm/cache.h
new file mode 100644
index 000000000000..641b2a0090e8
--- /dev/null
+++ b/arch/loongarch/include/asm/cache.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_CACHE_H
+#define _ASM_CACHE_H
+
+#include <asm/kmalloc.h>
+
+#define L1_CACHE_SHIFT		CONFIG_L1_CACHE_SHIFT
+#define L1_CACHE_BYTES		(1 << L1_CACHE_SHIFT)
+
+#define __read_mostly __section(".data..read_mostly")
+
+#endif /* _ASM_CACHE_H */
diff --git a/arch/loongarch/include/asm/cacheflush.h b/arch/loongarch/include/asm/cacheflush.h
new file mode 100644
index 000000000000..214ec714a27b
--- /dev/null
+++ b/arch/loongarch/include/asm/cacheflush.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_CACHEFLUSH_H
+#define _ASM_CACHEFLUSH_H
+
+#include <linux/mm.h>
+#include <asm/cpu-features.h>
+#include <asm/cacheops.h>
+
+/*
+ * This flag is used to indicate that the page pointed to by a pte
+ * is dirty and requires cleaning before returning it to the user.
+ */
+#define PG_dcache_dirty			PG_arch_1
+
+#define Page_dcache_dirty(page)		\
+	test_bit(PG_dcache_dirty, &(page)->flags)
+#define SetPageDcacheDirty(page)	\
+	set_bit(PG_dcache_dirty, &(page)->flags)
+#define ClearPageDcacheDirty(page)	\
+	clear_bit(PG_dcache_dirty, &(page)->flags)
+
+extern void local_flush_icache_range(unsigned long start, unsigned long end);
+
+#define flush_icache_range	local_flush_icache_range
+#define flush_icache_user_range	local_flush_icache_range
+
+extern void copy_to_user_page(struct vm_area_struct *vma,
+	struct page *page, unsigned long vaddr, void *dst, const void *src,
+	unsigned long len);
+
+extern void copy_from_user_page(struct vm_area_struct *vma,
+	struct page *page, unsigned long vaddr, void *dst, const void *src,
+	unsigned long len);
+
+#define ARCH_IMPLEMENTS_FLUSH_DCACHE_PAGE 1
+
+#define flush_cache_all()				do { } while (0)
+#define flush_cache_mm(mm)				do { } while (0)
+#define flush_cache_dup_mm(mm)				do { } while (0)
+#define flush_cache_range(vma, start, end)		do { } while (0)
+#define flush_cache_page(vma, vmaddr, pfn)		do { } while (0)
+#define flush_cache_vmap(start, end)			do { } while (0)
+#define flush_cache_vunmap(start, end)			do { } while (0)
+#define flush_icache_page(vma, page)			do { } while (0)
+#define flush_icache_user_page(vma, page, addr, len)	do { } while (0)
+#define flush_dcache_page(page)				do { } while (0)
+#define flush_dcache_mmap_lock(mapping)			do { } while (0)
+#define flush_dcache_mmap_unlock(mapping)		do { } while (0)
+
+#define cache_op(op, addr)						\
+	__asm__ __volatile__(						\
+	"	cacop	%0, %1					\n"	\
+	:								\
+	: "i" (op), "R" (*(unsigned char *)(addr)))
+
+static inline void flush_icache_line_indexed(unsigned long addr)
+{
+	cache_op(Index_Invalidate_I, addr);
+}
+
+static inline void flush_dcache_line_indexed(unsigned long addr)
+{
+	cache_op(Index_Writeback_Inv_D, addr);
+}
+
+static inline void flush_icache_line(unsigned long addr)
+{
+	cache_op(Hit_Invalidate_I, addr);
+}
+
+static inline void flush_dcache_line(unsigned long addr)
+{
+	cache_op(Hit_Writeback_Inv_D, addr);
+}
+
+#endif /* _ASM_CACHEFLUSH_H */
diff --git a/arch/loongarch/include/asm/cacheops.h b/arch/loongarch/include/asm/cacheops.h
new file mode 100644
index 000000000000..470354b92d0d
--- /dev/null
+++ b/arch/loongarch/include/asm/cacheops.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Cache operations for the cache instruction.
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef __ASM_CACHEOPS_H
+#define __ASM_CACHEOPS_H
+
+/*
+ * Most cache ops are split into a 2 bit field identifying the cache, and a 3
+ * bit field identifying the cache operation.
+ */
+#define CacheOp_Cache			0x03
+#define CacheOp_Op			0x1c
+
+#define Cache_I				0x00
+#define Cache_D				0x01
+#define Cache_V				0x02
+#define Cache_S				0x03
+
+#define Index_Invalidate		0x08
+#define Index_Writeback_Inv		0x08
+#define Hit_Invalidate			0x10
+#define Hit_Writeback_Inv		0x10
+
+#define Index_Invalidate_I		(Cache_I | Index_Invalidate)
+#define Index_Writeback_Inv_D		(Cache_D | Index_Writeback_Inv)
+#define Hit_Invalidate_I		(Cache_I | Hit_Invalidate)
+#define Hit_Writeback_Inv_D		(Cache_D | Hit_Writeback_Inv)
+
+#endif	/* __ASM_CACHEOPS_H */
diff --git a/arch/loongarch/include/asm/fixmap.h b/arch/loongarch/include/asm/fixmap.h
new file mode 100644
index 000000000000..2ba4df6ec88d
--- /dev/null
+++ b/arch/loongarch/include/asm/fixmap.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * fixmap.h: compile-time virtual memory allocation
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef _ASM_FIXMAP_H
+#define _ASM_FIXMAP_H
+
+#include <asm/page.h>
+
+#define NR_FIX_BTMAPS 64
+
+#endif
diff --git a/arch/loongarch/include/asm/hugetlb.h b/arch/loongarch/include/asm/hugetlb.h
new file mode 100644
index 000000000000..5548ab8ceac2
--- /dev/null
+++ b/arch/loongarch/include/asm/hugetlb.h
@@ -0,0 +1,79 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef __ASM_HUGETLB_H
+#define __ASM_HUGETLB_H
+
+#include <asm/page.h>
+
+uint64_t pmd_to_entrylo(unsigned long pmd_val);
+
+#define __HAVE_ARCH_PREPARE_HUGEPAGE_RANGE
+static inline int prepare_hugepage_range(struct file *file,
+					 unsigned long addr,
+					 unsigned long len)
+{
+	unsigned long task_size = STACK_TOP;
+	struct hstate *h = hstate_file(file);
+
+	if (len & ~huge_page_mask(h))
+		return -EINVAL;
+	if (addr & ~huge_page_mask(h))
+		return -EINVAL;
+	if (len > task_size)
+		return -ENOMEM;
+	if (task_size - len < addr)
+		return -EINVAL;
+	return 0;
+}
+
+#define __HAVE_ARCH_HUGE_PTEP_GET_AND_CLEAR
+static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
+					    unsigned long addr, pte_t *ptep)
+{
+	pte_t clear;
+	pte_t pte = *ptep;
+
+	pte_val(clear) = (unsigned long)invalid_pte_table;
+	set_pte_at(mm, addr, ptep, clear);
+	return pte;
+}
+
+#define __HAVE_ARCH_HUGE_PTEP_CLEAR_FLUSH
+static inline void huge_ptep_clear_flush(struct vm_area_struct *vma,
+					 unsigned long addr, pte_t *ptep)
+{
+	flush_tlb_page(vma, addr & huge_page_mask(hstate_vma(vma)));
+}
+
+#define __HAVE_ARCH_HUGE_PTE_NONE
+static inline int huge_pte_none(pte_t pte)
+{
+	unsigned long val = pte_val(pte) & ~_PAGE_GLOBAL;
+	return !val || (val == (unsigned long)invalid_pte_table);
+}
+
+#define __HAVE_ARCH_HUGE_PTEP_SET_ACCESS_FLAGS
+static inline int huge_ptep_set_access_flags(struct vm_area_struct *vma,
+					     unsigned long addr,
+					     pte_t *ptep, pte_t pte,
+					     int dirty)
+{
+	int changed = !pte_same(*ptep, pte);
+
+	if (changed) {
+		set_pte_at(vma->vm_mm, addr, ptep, pte);
+		/*
+		 * There could be some standard sized pages in there,
+		 * get them all.
+		 */
+		flush_tlb_range(vma, addr, addr + HPAGE_SIZE);
+	}
+	return changed;
+}
+
+#include <asm-generic/hugetlb.h>
+
+#endif /* __ASM_HUGETLB_H */
diff --git a/arch/loongarch/include/asm/kmalloc.h b/arch/loongarch/include/asm/kmalloc.h
new file mode 100644
index 000000000000..b318c41520d8
--- /dev/null
+++ b/arch/loongarch/include/asm/kmalloc.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef __ASM_KMALLOC_H
+#define __ASM_KMALLOC_H
+
+#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
+
+#endif /* __ASM_KMALLOC_H */
diff --git a/arch/loongarch/include/asm/shmparam.h b/arch/loongarch/include/asm/shmparam.h
new file mode 100644
index 000000000000..f726ac537710
--- /dev/null
+++ b/arch/loongarch/include/asm/shmparam.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_SHMPARAM_H
+#define _ASM_SHMPARAM_H
+
+#define __ARCH_FORCE_SHMLBA	1
+
+#define	SHMLBA	(4 * PAGE_SIZE)		 /* attach addr a multiple of this */
+
+#endif /* _ASM_SHMPARAM_H */
diff --git a/arch/loongarch/include/asm/sparsemem.h b/arch/loongarch/include/asm/sparsemem.h
new file mode 100644
index 000000000000..9b57dc69f523
--- /dev/null
+++ b/arch/loongarch/include/asm/sparsemem.h
@@ -0,0 +1,21 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _LOONGARCH_SPARSEMEM_H
+#define _LOONGARCH_SPARSEMEM_H
+
+#ifdef CONFIG_SPARSEMEM
+
+/*
+ * SECTION_SIZE_BITS		2^N: how big each section will be
+ * MAX_PHYSMEM_BITS		2^N: how much memory we can have in that space
+ */
+#define SECTION_SIZE_BITS	29
+#define MAX_PHYSMEM_BITS	48
+
+#endif /* CONFIG_SPARSEMEM */
+
+#ifdef CONFIG_MEMORY_HOTPLUG
+int memory_add_physaddr_to_nid(u64 addr);
+#define memory_add_physaddr_to_nid memory_add_physaddr_to_nid
+#endif
+
+#endif /* _LOONGARCH_SPARSEMEM_H */
diff --git a/arch/loongarch/include/asm/tlb.h b/arch/loongarch/include/asm/tlb.h
new file mode 100644
index 000000000000..7a1745ea404d
--- /dev/null
+++ b/arch/loongarch/include/asm/tlb.h
@@ -0,0 +1,216 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef __ASM_TLB_H
+#define __ASM_TLB_H
+
+#include <linux/mm_types.h>
+#include <asm/cpu-features.h>
+#include <asm/loongarchregs.h>
+
+/*
+ * TLB Invalidate Flush
+ */
+static inline void tlbclr(void)
+{
+	__asm__ __volatile__("tlbclr");
+}
+
+static inline void tlbflush(void)
+{
+	__asm__ __volatile__("tlbflush");
+}
+
+/*
+ * TLB R/W operations.
+ */
+static inline void tlb_probe(void)
+{
+	__asm__ __volatile__("tlbsrch");
+}
+
+static inline void tlb_read(void)
+{
+	__asm__ __volatile__("tlbrd");
+}
+
+static inline void tlb_write_indexed(void)
+{
+	__asm__ __volatile__("tlbwr");
+}
+
+static inline void tlb_write_random(void)
+{
+	__asm__ __volatile__("tlbfill");
+}
+
+/*
+ * Guest TLB Invalidate Flush
+ */
+static inline void guest_tlbflush(void)
+{
+	__asm__ __volatile__(
+		".word 0x6482401\n\t");
+}
+
+/*
+ * Guest TLB R/W operations.
+ */
+static inline void guest_tlb_probe(void)
+{
+	__asm__ __volatile__(
+		".word 0x6482801\n\t");
+}
+
+static inline void guest_tlb_read(void)
+{
+	__asm__ __volatile__(
+		".word 0x6482c01\n\t");
+}
+
+static inline void guest_tlb_write_indexed(void)
+{
+	__asm__ __volatile__(
+		".word 0x6483001\n\t");
+}
+
+static inline void guest_tlb_write_random(void)
+{
+	__asm__ __volatile__(
+		".word 0x6483401\n\t");
+}
+
+enum invtlb_ops {
+	/* Invalid all tlb */
+	INVTLB_ALL = 0x0,
+	/* Invalid current tlb */
+	INVTLB_CURRENT_ALL = 0x1,
+	/* Invalid all global=1 lines in current tlb */
+	INVTLB_CURRENT_GTRUE = 0x2,
+	/* Invalid all global=0 lines in current tlb */
+	INVTLB_CURRENT_GFALSE = 0x3,
+	/* Invalid global=0 and matched asid lines in current tlb */
+	INVTLB_GFALSE_AND_ASID = 0x4,
+	/* Invalid addr with global=0 and matched asid in current tlb */
+	INVTLB_ADDR_GFALSE_AND_ASID = 0x5,
+	/* Invalid addr with global=1 or matched asid in current tlb */
+	INVTLB_ADDR_GTRUE_OR_ASID = 0x6,
+	/* Invalid matched gid in guest tlb */
+	INVGTLB_GID = 0x9,
+	/* Invalid global=1, matched gid in guest tlb */
+	INVGTLB_GID_GTRUE = 0xa,
+	/* Invalid global=0, matched gid in guest tlb */
+	INVGTLB_GID_GFALSE = 0xb,
+	/* Invalid global=0, matched gid and asid in guest tlb */
+	INVGTLB_GID_GFALSE_ASID = 0xc,
+	/* Invalid global=0 , matched gid, asid and addr in guest tlb */
+	INVGTLB_GID_GFALSE_ASID_ADDR = 0xd,
+	/* Invalid global=1 , matched gid, asid and addr in guest tlb */
+	INVGTLB_GID_GTRUE_ASID_ADDR = 0xe,
+	/* Invalid all gid gva-->gpa guest tlb */
+	INVGTLB_ALLGID_GVA_TO_GPA = 0x10,
+	/* Invalid all gid gpa-->hpa tlb */
+	INVTLB_ALLGID_GPA_TO_HPA = 0x11,
+	/* Invalid all gid tlb, including  gva-->gpa and gpa-->hpa */
+	INVTLB_ALLGID = 0x12,
+	/* Invalid matched gid gva-->gpa guest tlb */
+	INVGTLB_GID_GVA_TO_GPA = 0x13,
+	/* Invalid matched gid gpa-->hpa tlb */
+	INVTLB_GID_GPA_TO_HPA = 0x14,
+	/* Invalid matched gid tlb,including gva-->gpa and gpa-->hpa */
+	INVTLB_GID_ALL = 0x15,
+	/* Invalid matched gid and addr gpa-->hpa tlb */
+	INVTLB_GID_ADDR = 0x16,
+};
+
+/*
+ * invtlb op info addr
+ * (0x1 << 26) | (0x24 << 20) | (0x13 << 15) |
+ * (addr << 10) | (info << 5) | op
+ */
+static inline void invtlb(u32 op, u32 info, u64 addr)
+{
+	__asm__ __volatile__(
+		"parse_r addr,%0\n\t"
+		"parse_r info,%1\n\t"
+		".word ((0x6498000) | (addr << 10) | (info << 5) | %2)\n\t"
+		:
+		: "r"(addr), "r"(info), "i"(op)
+		:
+		);
+}
+
+static inline void invtlb_addr(u32 op, u32 info, u64 addr)
+{
+	__asm__ __volatile__(
+		"parse_r addr,%0\n\t"
+		".word ((0x6498000) | (addr << 10) | (0 << 5) | %1)\n\t"
+		:
+		: "r"(addr), "i"(op)
+		:
+		);
+}
+
+static inline void invtlb_info(u32 op, u32 info, u64 addr)
+{
+	__asm__ __volatile__(
+		"parse_r info,%0\n\t"
+		".word ((0x6498000) | (0 << 10) | (info << 5) | %1)\n\t"
+		:
+		: "r"(info), "i"(op)
+		:
+		);
+}
+
+static inline void invtlb_all(u32 op, u32 info, u64 addr)
+{
+	__asm__ __volatile__(
+		".word ((0x6498000) | (0 << 10) | (0 << 5) | %0)\n\t"
+		:
+		: "i"(op)
+		:
+		);
+}
+
+/*
+ * LoongArch doesn't need any special per-pte or per-vma handling, except
+ * we need to flush cache for area to be unmapped.
+ */
+#define tlb_start_vma(tlb, vma)					\
+	do {							\
+		if (!(tlb)->fullmm)				\
+			flush_cache_range(vma, vma->vm_start, vma->vm_end); \
+	}  while (0)
+#define tlb_end_vma(tlb, vma) do { } while (0)
+#define __tlb_remove_tlb_entry(tlb, ptep, address) do { } while (0)
+
+static void tlb_flush(struct mmu_gather *tlb);
+
+#define tlb_flush tlb_flush
+#include <asm-generic/tlb.h>
+
+static inline void tlb_flush(struct mmu_gather *tlb)
+{
+	struct vm_area_struct vma;
+
+	vma.vm_mm = tlb->mm;
+	vma.vm_flags = 0;
+	if (tlb->fullmm) {
+		flush_tlb_mm(tlb->mm);
+		return;
+	}
+
+	flush_tlb_range(&vma, tlb->start, tlb->end);
+}
+
+extern void handle_tlb_load(void);
+extern void handle_tlb_store(void);
+extern void handle_tlb_modify(void);
+extern void handle_tlb_refill(void);
+extern void handle_tlb_rixi(void);
+
+extern void dump_tlb_all(void);
+extern void dump_tlb_regs(void);
+
+#endif /* __ASM_TLB_H */
diff --git a/arch/loongarch/include/asm/tlbflush.h b/arch/loongarch/include/asm/tlbflush.h
new file mode 100644
index 000000000000..a5672367d966
--- /dev/null
+++ b/arch/loongarch/include/asm/tlbflush.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef __ASM_TLBFLUSH_H
+#define __ASM_TLBFLUSH_H
+
+#include <linux/mm.h>
+
+/*
+ * TLB flushing:
+ *
+ *  - flush_tlb_all() flushes all processes TLB entries
+ *  - flush_tlb_mm(mm) flushes the specified mm context TLB entries
+ *  - flush_tlb_page(vma, vmaddr) flushes one page
+ *  - flush_tlb_range(vma, start, end) flushes a range of pages
+ *  - flush_tlb_kernel_range(start, end) flushes a range of kernel pages
+ */
+extern void local_flush_tlb_all(void);
+extern void local_flush_tlb_mm(struct mm_struct *mm);
+extern void local_flush_tlb_range(struct vm_area_struct *vma,
+	unsigned long start, unsigned long end);
+extern void local_flush_tlb_kernel_range(unsigned long start,
+	unsigned long end);
+extern void local_flush_tlb_page(struct vm_area_struct *vma,
+	unsigned long page);
+extern void local_flush_tlb_one(unsigned long vaddr);
+
+#define flush_tlb_all()			local_flush_tlb_all()
+#define flush_tlb_mm(mm)		local_flush_tlb_mm(mm)
+#define flush_tlb_range(vma, vmaddr, end)	local_flush_tlb_range(vma, vmaddr, end)
+#define flush_tlb_kernel_range(vmaddr, end)	local_flush_tlb_kernel_range(vmaddr, end)
+#define flush_tlb_page(vma, page)	local_flush_tlb_page(vma, page)
+#define flush_tlb_one(vaddr)		local_flush_tlb_one(vaddr)
+
+#endif /* __ASM_TLBFLUSH_H */
diff --git a/arch/loongarch/include/asm/vmalloc.h b/arch/loongarch/include/asm/vmalloc.h
new file mode 100644
index 000000000000..965a0d41ac2d
--- /dev/null
+++ b/arch/loongarch/include/asm/vmalloc.h
@@ -0,0 +1,4 @@
+#ifndef _ASM_LOONGARCH_VMALLOC_H
+#define _ASM_LOONGARCH_VMALLOC_H
+
+#endif /* _ASM_LOONGARCH_VMALLOC_H */
diff --git a/arch/loongarch/mm/cache.c b/arch/loongarch/mm/cache.c
new file mode 100644
index 000000000000..945087a88d03
--- /dev/null
+++ b/arch/loongarch/mm/cache.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/export.h>
+#include <linux/fcntl.h>
+#include <linux/fs.h>
+#include <linux/highmem.h>
+#include <linux/kernel.h>
+#include <linux/linkage.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/syscalls.h>
+
+#include <asm/cacheflush.h>
+#include <asm/cpu.h>
+#include <asm/cpu-features.h>
+#include <asm/dma.h>
+#include <asm/loongarchregs.h>
+#include <asm/processor.h>
+#include <asm/setup.h>
+
+/* Cache operations. */
+void local_flush_icache_range(unsigned long start, unsigned long end)
+{
+	asm volatile ("\tibar 0\n"::);
+}
+
+void __update_cache(unsigned long address, pte_t pte)
+{
+	struct page *page;
+	unsigned long pfn, addr;
+
+	pfn = pte_pfn(pte);
+	if (unlikely(!pfn_valid(pfn)))
+		return;
+	page = pfn_to_page(pfn);
+	if (Page_dcache_dirty(page)) {
+		if (PageHighMem(page))
+			addr = (unsigned long)kmap_atomic(page);
+		else
+			addr = (unsigned long)page_address(page);
+
+		if (PageHighMem(page))
+			kunmap_atomic((void *)addr);
+
+		ClearPageDcacheDirty(page);
+	}
+}
+
+static inline void setup_protection_map(void)
+{
+	protection_map[0]  = __pgprot(_CACHE_CC |
+		_PAGE_PROTNONE | _PAGE_NO_EXEC | _PAGE_NO_READ);
+	protection_map[1]  = __pgprot(_CACHE_CC |
+		_PAGE_USER | _PAGE_PRESENT | _PAGE_NO_EXEC);
+	protection_map[2]  = __pgprot(_CACHE_CC |
+		_PAGE_USER | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_NO_READ);
+	protection_map[3]  = __pgprot(_CACHE_CC |
+		_PAGE_USER | _PAGE_PRESENT | _PAGE_NO_EXEC);
+	protection_map[4]  = __pgprot(_CACHE_CC |
+		_PAGE_USER | _PAGE_PRESENT);
+	protection_map[5]  = __pgprot(_CACHE_CC |
+		_PAGE_USER | _PAGE_PRESENT);
+	protection_map[6]  = __pgprot(_CACHE_CC |
+		_PAGE_USER | _PAGE_PRESENT);
+	protection_map[7]  = __pgprot(_CACHE_CC |
+		_PAGE_USER | _PAGE_PRESENT);
+
+	protection_map[8]  = __pgprot(_CACHE_CC |
+		_PAGE_PROTNONE | _PAGE_NO_EXEC | _PAGE_NO_READ);
+	protection_map[9]  = __pgprot(_CACHE_CC |
+		_PAGE_USER | _PAGE_PRESENT | _PAGE_NO_EXEC);
+	protection_map[10] = __pgprot(_CACHE_CC |
+		_PAGE_USER | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_WRITE | _PAGE_NO_READ);
+	protection_map[11] = __pgprot(_CACHE_CC |
+		_PAGE_USER | _PAGE_PRESENT | _PAGE_NO_EXEC | _PAGE_WRITE);
+	protection_map[12] = __pgprot(_CACHE_CC |
+		_PAGE_USER | _PAGE_PRESENT);
+	protection_map[13] = __pgprot(_CACHE_CC |
+		_PAGE_USER | _PAGE_PRESENT);
+	protection_map[14] = __pgprot(_CACHE_CC |
+		_PAGE_USER | _PAGE_PRESENT | _PAGE_WRITE);
+	protection_map[15] = __pgprot(_CACHE_CC |
+		_PAGE_USER | _PAGE_PRESENT | _PAGE_WRITE);
+}
+
+void cache_error_setup(void)
+{
+	extern char __weak except_vec_cex;
+	set_merr_handler(0x0, &except_vec_cex, 0x80);
+}
+
+static unsigned long icache_size __read_mostly;
+static unsigned long dcache_size __read_mostly;
+static unsigned long vcache_size __read_mostly;
+static unsigned long scache_size __read_mostly;
+
+static char *way_string[] = { NULL, "direct mapped", "2-way",
+	"3-way", "4-way", "5-way", "6-way", "7-way", "8-way",
+	"9-way", "10-way", "11-way", "12-way",
+	"13-way", "14-way", "15-way", "16-way",
+};
+
+static void probe_pcache(void)
+{
+	struct cpuinfo_loongarch *c = &current_cpu_data;
+	unsigned int lsize, sets, ways;
+	unsigned int config;
+
+	config = read_cpucfg(LOONGARCH_CPUCFG17);
+	lsize = 1 << ((config & CPUCFG17_L1I_SIZE_M) >> CPUCFG17_L1I_SIZE);
+	sets  = 1 << ((config & CPUCFG17_L1I_SETS_M) >> CPUCFG17_L1I_SETS);
+	ways  = ((config & CPUCFG17_L1I_WAYS_M) >> CPUCFG17_L1I_WAYS) + 1;
+
+	c->icache.linesz = lsize;
+	c->icache.sets = sets;
+	c->icache.ways = ways;
+	icache_size = sets * ways * lsize;
+	c->icache.waysize = icache_size / c->icache.ways;
+
+	config = read_cpucfg(LOONGARCH_CPUCFG18);
+	lsize = 1 << ((config & CPUCFG18_L1D_SIZE_M) >> CPUCFG18_L1D_SIZE);
+	sets  = 1 << ((config & CPUCFG18_L1D_SETS_M) >> CPUCFG18_L1D_SETS);
+	ways  = ((config & CPUCFG18_L1D_WAYS_M) >> CPUCFG18_L1D_WAYS) + 1;
+
+	c->dcache.linesz = lsize;
+	c->dcache.sets = sets;
+	c->dcache.ways = ways;
+	dcache_size = sets * ways * lsize;
+	c->dcache.waysize = dcache_size / c->dcache.ways;
+
+	c->options |= LOONGARCH_CPU_PREFETCH;
+
+	pr_info("Primary instruction cache %ldkB, %s, %s, linesize %d bytes.\n",
+		icache_size >> 10, way_string[c->icache.ways], "VIPT", c->icache.linesz);
+
+	pr_info("Primary data cache %ldkB, %s, %s, %s, linesize %d bytes\n",
+		dcache_size >> 10, way_string[c->dcache.ways], "VIPT", "no aliases", c->dcache.linesz);
+}
+
+static void probe_vcache(void)
+{
+	struct cpuinfo_loongarch *c = &current_cpu_data;
+	unsigned int lsize, sets, ways;
+	unsigned int config;
+
+	config = read_cpucfg(LOONGARCH_CPUCFG19);
+	lsize = 1 << ((config & CPUCFG19_L2_SIZE_M) >> CPUCFG19_L2_SIZE);
+	sets  = 1 << ((config & CPUCFG19_L2_SETS_M) >> CPUCFG19_L2_SETS);
+	ways  = ((config & CPUCFG19_L2_WAYS_M) >> CPUCFG19_L2_WAYS) + 1;
+
+	c->vcache.linesz = lsize;
+	c->vcache.sets = sets;
+	c->vcache.ways = ways;
+	vcache_size = lsize * sets * ways;
+	c->vcache.waysize = vcache_size / c->vcache.ways;
+
+	pr_info("Unified victim cache %ldkB %s, linesize %d bytes.\n",
+		vcache_size >> 10, way_string[c->vcache.ways], c->vcache.linesz);
+}
+
+static void probe_scache(void)
+{
+	struct cpuinfo_loongarch *c = &current_cpu_data;
+	unsigned int lsize, sets, ways;
+	unsigned int config;
+
+	config = read_cpucfg(LOONGARCH_CPUCFG20);
+	lsize = 1 << ((config & CPUCFG20_L3_SIZE_M) >> CPUCFG20_L3_SIZE);
+	sets  = 1 << ((config & CPUCFG20_L3_SETS_M) >> CPUCFG20_L3_SETS);
+	ways  = ((config & CPUCFG20_L3_WAYS_M) >> CPUCFG20_L3_WAYS) + 1;
+
+	c->scache.linesz = lsize;
+	c->scache.sets = sets;
+	c->scache.ways = ways;
+	/* 4 cores. scaches are shared */
+	scache_size = lsize * sets * ways;
+	c->scache.waysize = scache_size / c->scache.ways;
+
+	pr_info("Unified secondary cache %ldkB %s, linesize %d bytes.\n",
+		scache_size >> 10, way_string[c->scache.ways], c->scache.linesz);
+}
+
+void cpu_cache_init(void)
+{
+	probe_pcache();
+	probe_vcache();
+	probe_scache();
+
+	shm_align_mask = PAGE_SIZE - 1;
+
+	setup_protection_map();
+}
diff --git a/arch/loongarch/mm/extable.c b/arch/loongarch/mm/extable.c
new file mode 100644
index 000000000000..7b367a8dd7e0
--- /dev/null
+++ b/arch/loongarch/mm/extable.c
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/extable.h>
+#include <linux/spinlock.h>
+#include <asm/branch.h>
+#include <linux/uaccess.h>
+
+int fixup_exception(struct pt_regs *regs)
+{
+	const struct exception_table_entry *fixup;
+
+	fixup = search_exception_tables(exception_epc(regs));
+	if (fixup) {
+		regs->csr_epc = fixup->fixup;
+
+		return 1;
+	}
+
+	return 0;
+}
diff --git a/arch/loongarch/mm/fault.c b/arch/loongarch/mm/fault.c
new file mode 100644
index 000000000000..f3a523379993
--- /dev/null
+++ b/arch/loongarch/mm/fault.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/context_tracking.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/ratelimit.h>
+#include <linux/mman.h>
+#include <linux/mm.h>
+#include <linux/smp.h>
+#include <linux/kdebug.h>
+#include <linux/kprobes.h>
+#include <linux/perf_event.h>
+#include <linux/uaccess.h>
+
+#include <asm/branch.h>
+#include <asm/mmu_context.h>
+#include <asm/ptrace.h>
+
+int show_unhandled_signals = 1;
+
+/*
+ * This routine handles page faults.  It determines the address,
+ * and the problem, and then passes it off to one of the appropriate
+ * routines.
+ */
+static void __kprobes __do_page_fault(struct pt_regs *regs, unsigned long write,
+	unsigned long address)
+{
+	int si_code;
+	const int field = sizeof(unsigned long) * 2;
+	unsigned int flags = FAULT_FLAG_ALLOW_RETRY | FAULT_FLAG_KILLABLE;
+	struct task_struct *tsk = current;
+	struct mm_struct *mm = tsk->mm;
+	struct vm_area_struct *vma = NULL;
+	vm_fault_t fault;
+
+	static DEFINE_RATELIMIT_STATE(ratelimit_state, 5 * HZ, 10);
+
+	si_code = SEGV_MAPERR;
+
+	if (user_mode(regs) && (address & __UA_LIMIT))
+		goto bad_area_nosemaphore;
+
+	/*
+	 * We fault-in kernel-space virtual memory on-demand. The
+	 * 'reference' page table is init_mm.pgd.
+	 *
+	 * NOTE! We MUST NOT take any locks for this case. We may
+	 * be in an interrupt or a critical region, and should
+	 * only copy the information from the master page table,
+	 * nothing more.
+	 */
+#ifdef CONFIG_64BIT
+# define VMALLOC_FAULT_TARGET no_context
+#else
+# define VMALLOC_FAULT_TARGET vmalloc_fault
+#endif
+	if (unlikely(address >= VMALLOC_START && address <= VMALLOC_END))
+		goto VMALLOC_FAULT_TARGET;
+
+	/* Enable interrupts if they were enabled in the parent context. */
+	if (likely(regs->csr_prmd & CSR_PRMD_PIE))
+		local_irq_enable();
+
+	/*
+	 * If we're in an interrupt or have no user
+	 * context, we must not take the fault..
+	 */
+	if (faulthandler_disabled() || !mm)
+		goto bad_area_nosemaphore;
+
+	if (user_mode(regs))
+		flags |= FAULT_FLAG_USER;
+retry:
+	mmap_read_lock(mm);
+	vma = find_vma(mm, address);
+	if (!vma)
+		goto bad_area;
+	if (vma->vm_start <= address)
+		goto good_area;
+	if (!(vma->vm_flags & VM_GROWSDOWN))
+		goto bad_area;
+	if (expand_stack(vma, address))
+		goto bad_area;
+/*
+ * Ok, we have a good vm_area for this memory access, so
+ * we can handle it..
+ */
+good_area:
+	si_code = SEGV_ACCERR;
+
+	if (write) {
+		if (!(vma->vm_flags & VM_WRITE))
+			goto bad_area;
+		flags |= FAULT_FLAG_WRITE;
+	} else {
+		if (address == regs->csr_epc && !(vma->vm_flags & VM_EXEC))
+			goto bad_area;
+		if (!(vma->vm_flags & VM_READ) &&
+		    exception_epc(regs) != address)
+			goto bad_area;
+	}
+
+	/*
+	 * If for any reason at all we couldn't handle the fault,
+	 * make sure we exit gracefully rather than endlessly redo
+	 * the fault.
+	 */
+	fault = handle_mm_fault(vma, address, flags, regs);
+
+	if ((fault & VM_FAULT_RETRY) && fatal_signal_pending(current))
+		return;
+
+	perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);
+	if (unlikely(fault & VM_FAULT_ERROR)) {
+		if (fault & VM_FAULT_OOM)
+			goto out_of_memory;
+		else if (fault & VM_FAULT_SIGSEGV)
+			goto bad_area;
+		else if (fault & VM_FAULT_SIGBUS)
+			goto do_sigbus;
+		BUG();
+	}
+	if (flags & FAULT_FLAG_ALLOW_RETRY) {
+		if (fault & VM_FAULT_MAJOR) {
+			perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MAJ, 1,
+						  regs, address);
+			tsk->maj_flt++;
+		} else {
+			perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS_MIN, 1,
+						  regs, address);
+			tsk->min_flt++;
+		}
+		if (fault & VM_FAULT_RETRY) {
+			flags &= ~FAULT_FLAG_ALLOW_RETRY;
+			flags |= FAULT_FLAG_TRIED;
+
+			/*
+			 * No need to mmap_read_unlock(mm) as we would
+			 * have already released it in __lock_page_or_retry
+			 * in mm/filemap.c.
+			 */
+
+			goto retry;
+		}
+	}
+
+	mmap_read_unlock(mm);
+	return;
+
+/*
+ * Something tried to access memory that isn't in our memory map..
+ * Fix it, but check if it's kernel or user first..
+ */
+bad_area:
+	mmap_read_unlock(mm);
+
+bad_area_nosemaphore:
+	/* User mode accesses just cause a SIGSEGV */
+	if (user_mode(regs)) {
+		tsk->thread.csr_badvaddr = address;
+		tsk->thread.error_code = write;
+		if (show_unhandled_signals &&
+		    unhandled_signal(tsk, SIGSEGV) &&
+		    __ratelimit(&ratelimit_state)) {
+			pr_info("do_page_fault(): sending SIGSEGV to %s for invalid %s %0*lx\n",
+				tsk->comm,
+				write ? "write access to" : "read access from",
+				field, address);
+			pr_info("epc = %0*lx in", field,
+				(unsigned long) regs->csr_epc);
+			print_vma_addr(KERN_CONT " ", regs->csr_epc);
+			pr_cont("\n");
+			pr_info("ra  = %0*lx in", field,
+				(unsigned long) regs->regs[1]);
+			print_vma_addr(KERN_CONT " ", regs->regs[1]);
+			pr_cont("\n");
+		}
+		current->thread.trap_nr = read_csr_excode();
+		force_sig_fault(SIGSEGV, si_code, (void __user *)address);
+		return;
+	}
+
+no_context:
+	/* Are we prepared to handle this kernel fault?	 */
+	if (fixup_exception(regs)) {
+		current->thread.csr_baduaddr = address;
+		return;
+	}
+
+	/*
+	 * Oops. The kernel tried to access some bad page. We'll have to
+	 * terminate things with extreme prejudice.
+	 */
+	bust_spinlocks(1);
+
+	pr_alert("CPU %d Unable to handle kernel paging request at "
+	       "virtual address %0*lx, epc == %0*lx, ra == %0*lx\n",
+	       raw_smp_processor_id(), field, address, field, regs->csr_epc,
+	       field,  regs->regs[1]);
+	die("Oops", regs);
+
+out_of_memory:
+	/*
+	 * We ran out of memory, call the OOM killer, and return the userspace
+	 * (which will retry the fault, or kill us if we got oom-killed).
+	 */
+	mmap_read_unlock(mm);
+	if (!user_mode(regs))
+		goto no_context;
+	pagefault_out_of_memory();
+	return;
+
+do_sigbus:
+	mmap_read_unlock(mm);
+
+	/* Kernel mode? Handle exceptions or die */
+	if (!user_mode(regs))
+		goto no_context;
+
+	/*
+	 * Send a sigbus, regardless of whether we were in kernel
+	 * or user mode.
+	 */
+	current->thread.trap_nr = read_csr_excode();
+	tsk->thread.csr_badvaddr = address;
+	force_sig_fault(SIGBUS, BUS_ADRERR, (void __user *)address);
+
+	return;
+
+#ifndef CONFIG_64BIT
+vmalloc_fault:
+	{
+		/*
+		 * Synchronize this task's top level page-table
+		 * with the 'reference' page table.
+		 *
+		 * Do _not_ use "tsk" here. We might be inside
+		 * an interrupt in the middle of a task switch..
+		 */
+		int offset = __pgd_offset(address);
+		pgd_t *pgd, *pgd_k;
+		pud_t *pud, *pud_k;
+		pmd_t *pmd, *pmd_k;
+		pte_t *pte_k;
+
+		pgd = (pgd_t *) pgd_current[raw_smp_processor_id()] + offset;
+		pgd_k = init_mm.pgd + offset;
+
+		if (!pgd_present(*pgd_k))
+			goto no_context;
+		set_pgd(pgd, *pgd_k);
+
+		pud = pud_offset(pgd, address);
+		pud_k = pud_offset(pgd_k, address);
+		if (!pud_present(*pud_k))
+			goto no_context;
+
+		pmd = pmd_offset(pud, address);
+		pmd_k = pmd_offset(pud_k, address);
+		if (!pmd_present(*pmd_k))
+			goto no_context;
+		set_pmd(pmd, *pmd_k);
+
+		pte_k = pte_offset_kernel(pmd_k, address);
+		if (!pte_present(*pte_k))
+			goto no_context;
+		return;
+	}
+#endif
+}
+
+asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
+	unsigned long write, unsigned long address)
+{
+	enum ctx_state prev_state;
+
+	prev_state = exception_enter();
+	__do_page_fault(regs, write, address);
+	exception_exit(prev_state);
+}
diff --git a/arch/loongarch/mm/hugetlbpage.c b/arch/loongarch/mm/hugetlbpage.c
new file mode 100644
index 000000000000..f6f56f5e8a08
--- /dev/null
+++ b/arch/loongarch/mm/hugetlbpage.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/hugetlb.h>
+#include <linux/pagemap.h>
+#include <linux/err.h>
+#include <linux/sysctl.h>
+#include <asm/mman.h>
+#include <asm/tlb.h>
+#include <asm/tlbflush.h>
+
+pte_t *huge_pte_alloc(struct mm_struct *mm, struct vm_area_struct *vma,
+		      unsigned long addr, unsigned long sz)
+{
+	pgd_t *pgd;
+	p4d_t *p4d;
+	pud_t *pud;
+	pte_t *pte = NULL;
+
+	pgd = pgd_offset(mm, addr);
+	p4d = p4d_alloc(mm, pgd, addr);
+	pud = pud_alloc(mm, p4d, addr);
+	if (pud)
+		pte = (pte_t *)pmd_alloc(mm, pud, addr);
+
+	return pte;
+}
+
+pte_t *huge_pte_offset(struct mm_struct *mm, unsigned long addr,
+		       unsigned long sz)
+{
+	pgd_t *pgd;
+	p4d_t *p4d;
+	pud_t *pud;
+	pmd_t *pmd = NULL;
+
+	pgd = pgd_offset(mm, addr);
+	if (pgd_present(*pgd)) {
+		p4d = p4d_offset(pgd, addr);
+		if (p4d_present(*p4d)) {
+			pud = pud_offset(p4d, addr);
+			if (pud_present(*pud))
+				pmd = pmd_offset(pud, addr);
+		}
+	}
+	return (pte_t *) pmd;
+}
+
+/*
+ * This function checks for proper alignment of input addr and len parameters.
+ */
+int is_aligned_hugepage_range(unsigned long addr, unsigned long len)
+{
+	if (len & ~HPAGE_MASK)
+		return -EINVAL;
+	if (addr & ~HPAGE_MASK)
+		return -EINVAL;
+	return 0;
+}
+
+int pmd_huge(pmd_t pmd)
+{
+	return (pmd_val(pmd) & _PAGE_HUGE) != 0;
+}
+
+int pud_huge(pud_t pud)
+{
+	return (pud_val(pud) & _PAGE_HUGE) != 0;
+}
+
+uint64_t pmd_to_entrylo(unsigned long pmd_val)
+{
+	uint64_t val;
+	/* PMD as PTE. Must be huge page */
+	if (!pmd_huge(__pmd(pmd_val)))
+		panic("%s", __func__);
+
+	val = pmd_val ^ _PAGE_HUGE;
+	val |= ((val & _PAGE_HGLOBAL) >>
+		(_PAGE_HGLOBAL_SHIFT - _PAGE_GLOBAL_SHIFT));
+
+	return val;
+}
diff --git a/arch/loongarch/mm/init.c b/arch/loongarch/mm/init.c
new file mode 100644
index 000000000000..e661017ca23e
--- /dev/null
+++ b/arch/loongarch/mm/init.c
@@ -0,0 +1,199 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/bug.h>
+#include <linux/init.h>
+#include <linux/export.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/pagemap.h>
+#include <linux/ptrace.h>
+#include <linux/memblock.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/highmem.h>
+#include <linux/swap.h>
+#include <linux/proc_fs.h>
+#include <linux/pfn.h>
+#include <linux/hardirq.h>
+#include <linux/gfp.h>
+#include <linux/initrd.h>
+#include <linux/mmzone.h>
+
+#include <asm/asm-offsets.h>
+#include <asm/bootinfo.h>
+#include <asm/cpu.h>
+#include <asm/dma.h>
+#include <asm/mmu_context.h>
+#include <asm/sections.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/tlb.h>
+
+/*
+ * We have up to 8 empty zeroed pages so we can map one of the right colour
+ * when needed.	 Since page is never written to after the initialization we
+ * don't have to care about aliases on other CPUs.
+ */
+unsigned long empty_zero_page, zero_page_mask;
+EXPORT_SYMBOL_GPL(empty_zero_page);
+EXPORT_SYMBOL(zero_page_mask);
+
+void setup_zero_pages(void)
+{
+	unsigned int order, i;
+	struct page *page;
+
+	order = 0;
+
+	empty_zero_page = __get_free_pages(GFP_KERNEL | __GFP_ZERO, order);
+	if (!empty_zero_page)
+		panic("Oh boy, that early out of memory?");
+
+	page = virt_to_page((void *)empty_zero_page);
+	split_page(page, order);
+	for (i = 0; i < (1 << order); i++, page++)
+		mark_page_reserved(page);
+
+	zero_page_mask = ((PAGE_SIZE << order) - 1) & PAGE_MASK;
+}
+
+void copy_user_highpage(struct page *to, struct page *from,
+	unsigned long vaddr, struct vm_area_struct *vma)
+{
+	void *vfrom, *vto;
+
+	vto = kmap_atomic(to);
+	vfrom = kmap_atomic(from);
+	copy_page(vto, vfrom);
+	kunmap_atomic(vfrom);
+	kunmap_atomic(vto);
+	/* Make sure this page is cleared on other CPU's too before using it */
+	smp_wmb();
+}
+
+void copy_to_user_page(struct vm_area_struct *vma,
+	struct page *page, unsigned long vaddr, void *dst, const void *src,
+	unsigned long len)
+{
+	memcpy(dst, src, len);
+}
+
+void copy_from_user_page(struct vm_area_struct *vma,
+	struct page *page, unsigned long vaddr, void *dst, const void *src,
+	unsigned long len)
+{
+	memcpy(dst, src, len);
+}
+EXPORT_SYMBOL_GPL(copy_from_user_page);
+
+void __init paging_init(void)
+{
+	unsigned long max_zone_pfns[MAX_NR_ZONES];
+
+	pagetable_init();
+
+#ifdef CONFIG_ZONE_DMA
+	max_zone_pfns[ZONE_DMA] = MAX_DMA_PFN;
+#endif
+#ifdef CONFIG_ZONE_DMA32
+	max_zone_pfns[ZONE_DMA32] = MAX_DMA32_PFN;
+#endif
+	max_zone_pfns[ZONE_NORMAL] = max_low_pfn;
+
+	free_area_init(max_zone_pfns);
+}
+
+void __init mem_init(void)
+{
+	max_mapnr = max_low_pfn;
+	high_memory = (void *) __va(max_low_pfn << PAGE_SHIFT);
+
+	memblock_free_all();
+	setup_zero_pages();	/* Setup zeroed pages.  */
+}
+
+void free_init_pages(const char *what, unsigned long begin, unsigned long end)
+{
+	unsigned long pfn;
+
+	for (pfn = PFN_UP(begin); pfn < PFN_DOWN(end); pfn++) {
+		struct page *page = pfn_to_page(pfn);
+		void *addr = phys_to_virt(PFN_PHYS(pfn));
+
+		memset(addr, POISON_FREE_INITMEM, PAGE_SIZE);
+		free_reserved_page(page);
+	}
+	pr_info("Freeing %s: %ldk freed\n", what, (end - begin) >> 10);
+}
+
+#ifdef CONFIG_BLK_DEV_INITRD
+void free_initrd_mem(unsigned long start, unsigned long end)
+{
+	free_reserved_area((void *)start, (void *)end, POISON_FREE_INITMEM,
+			   "initrd");
+}
+#endif
+
+void __ref free_initmem(void)
+{
+	free_initmem_default(POISON_FREE_INITMEM);
+}
+
+#ifdef CONFIG_MEMORY_HOTPLUG
+int arch_add_memory(int nid, u64 start, u64 size, struct mhp_params *params)
+{
+	unsigned long start_pfn = start >> PAGE_SHIFT;
+	unsigned long nr_pages = size >> PAGE_SHIFT;
+	int ret;
+
+	ret = __add_pages(nid, start_pfn, nr_pages, params);
+
+	if (ret)
+		pr_warn("%s: Problem encountered in __add_pages() as ret=%d\n",
+				__func__,  ret);
+
+	return ret;
+}
+
+#ifdef CONFIG_MEMORY_HOTREMOVE
+void arch_remove_memory(int nid, u64 start,
+		u64 size, struct vmem_altmap *altmap)
+{
+	unsigned long start_pfn = start >> PAGE_SHIFT;
+	unsigned long nr_pages = size >> PAGE_SHIFT;
+	struct page *page = pfn_to_page(start_pfn);
+
+	/* With altmap the first mapped page is offset from @start */
+	if (altmap)
+		page += vmem_altmap_offset(altmap);
+	__remove_pages(start_pfn, nr_pages, altmap);
+}
+#endif
+#endif
+
+/*
+ * Align swapper_pg_dir in to 64K, allows its address to be loaded
+ * with a single LUI instruction in the TLB handlers.  If we used
+ * __aligned(64K), its size would get rounded up to the alignment
+ * size, and waste space.  So we place it in its own section and align
+ * it in the linker script.
+ */
+pgd_t swapper_pg_dir[_PTRS_PER_PGD] __section(".bss..swapper_pg_dir");
+
+pgd_t invalid_pg_dir[_PTRS_PER_PGD] __page_aligned_bss;
+#ifndef __PAGETABLE_PUD_FOLDED
+pud_t invalid_pud_table[PTRS_PER_PUD] __page_aligned_bss;
+#endif
+#ifndef __PAGETABLE_PMD_FOLDED
+pmd_t invalid_pmd_table[PTRS_PER_PMD] __page_aligned_bss;
+EXPORT_SYMBOL_GPL(invalid_pmd_table);
+#endif
+pte_t invalid_pte_table[PTRS_PER_PTE] __page_aligned_bss;
+EXPORT_SYMBOL(invalid_pte_table);
diff --git a/arch/loongarch/mm/ioremap.c b/arch/loongarch/mm/ioremap.c
new file mode 100644
index 000000000000..515462cbbd8c
--- /dev/null
+++ b/arch/loongarch/mm/ioremap.c
@@ -0,0 +1,27 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <asm/io.h>
+
+void __init __iomem *early_ioremap(u64 phys_addr, unsigned long size)
+{
+	return ((void *)TO_CAC(phys_addr));
+}
+
+void __init early_iounmap(void __iomem *addr, unsigned long size)
+{
+
+}
+
+void *early_memremap_ro(resource_size_t phys_addr, unsigned long size)
+{
+	return early_memremap(phys_addr, size);
+}
+
+void *early_memremap_prot(resource_size_t phys_addr, unsigned long size,
+		    unsigned long prot_val)
+{
+	return early_memremap(phys_addr, size);
+}
diff --git a/arch/loongarch/mm/maccess.c b/arch/loongarch/mm/maccess.c
new file mode 100644
index 000000000000..58173842c6be
--- /dev/null
+++ b/arch/loongarch/mm/maccess.c
@@ -0,0 +1,10 @@
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/uaccess.h>
+#include <linux/kernel.h>
+
+bool copy_from_kernel_nofault_allowed(const void *unsafe_src, size_t size)
+{
+	/* highest bit set means kernel space */
+	return (unsigned long)unsafe_src >> (BITS_PER_LONG - 1);
+}
diff --git a/arch/loongarch/mm/mmap.c b/arch/loongarch/mm/mmap.c
new file mode 100644
index 000000000000..4c05e0dba649
--- /dev/null
+++ b/arch/loongarch/mm/mmap.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/compiler.h>
+#include <linux/elf-randomize.h>
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/mman.h>
+#include <linux/export.h>
+#include <linux/personality.h>
+#include <linux/random.h>
+#include <linux/sched/signal.h>
+#include <linux/sched/mm.h>
+
+unsigned long shm_align_mask = PAGE_SIZE - 1;	/* Sane caches */
+EXPORT_SYMBOL(shm_align_mask);
+
+/* gap between mmap and stack */
+#define MIN_GAP (128*1024*1024UL)
+#define MAX_GAP ((TASK_SIZE)/6*5)
+
+static int mmap_is_legacy(struct rlimit *rlim_stack)
+{
+	if (current->personality & ADDR_COMPAT_LAYOUT)
+		return 1;
+
+	if (rlim_stack->rlim_cur == RLIM_INFINITY)
+		return 1;
+
+	return sysctl_legacy_va_layout;
+}
+
+static unsigned long mmap_base(unsigned long rnd, struct rlimit *rlim_stack)
+{
+	unsigned long gap = rlim_stack->rlim_cur;
+
+	if (gap < MIN_GAP)
+		gap = MIN_GAP;
+	else if (gap > MAX_GAP)
+		gap = MAX_GAP;
+
+	return PAGE_ALIGN(TASK_SIZE - gap - rnd);
+}
+
+#define COLOUR_ALIGN(addr, pgoff)				\
+	((((addr) + shm_align_mask) & ~shm_align_mask) +	\
+	 (((pgoff) << PAGE_SHIFT) & shm_align_mask))
+
+enum mmap_allocation_direction {UP, DOWN};
+
+static unsigned long arch_get_unmapped_area_common(struct file *filp,
+	unsigned long addr0, unsigned long len, unsigned long pgoff,
+	unsigned long flags, enum mmap_allocation_direction dir)
+{
+	struct mm_struct *mm = current->mm;
+	struct vm_area_struct *vma;
+	unsigned long addr = addr0;
+	int do_color_align;
+	struct vm_unmapped_area_info info;
+
+	if (unlikely(len > TASK_SIZE))
+		return -ENOMEM;
+
+	if (flags & MAP_FIXED) {
+		/* Even MAP_FIXED mappings must reside within TASK_SIZE */
+		if (TASK_SIZE - len < addr)
+			return -EINVAL;
+
+		/*
+		 * We do not accept a shared mapping if it would violate
+		 * cache aliasing constraints.
+		 */
+		if ((flags & MAP_SHARED) &&
+		    ((addr - (pgoff << PAGE_SHIFT)) & shm_align_mask))
+			return -EINVAL;
+		return addr;
+	}
+
+	do_color_align = 0;
+	if (filp || (flags & MAP_SHARED))
+		do_color_align = 1;
+
+	/* requesting a specific address */
+	if (addr) {
+		if (do_color_align)
+			addr = COLOUR_ALIGN(addr, pgoff);
+		else
+			addr = PAGE_ALIGN(addr);
+
+		vma = find_vma(mm, addr);
+		if (TASK_SIZE - len >= addr &&
+		    (!vma || addr + len <= vm_start_gap(vma)))
+			return addr;
+	}
+
+	info.length = len;
+	info.align_mask = do_color_align ? (PAGE_MASK & shm_align_mask) : 0;
+	info.align_offset = pgoff << PAGE_SHIFT;
+
+	if (dir == DOWN) {
+		info.flags = VM_UNMAPPED_AREA_TOPDOWN;
+		info.low_limit = PAGE_SIZE;
+		info.high_limit = mm->mmap_base;
+		addr = vm_unmapped_area(&info);
+
+		if (!(addr & ~PAGE_MASK))
+			return addr;
+
+		/*
+		 * A failed mmap() very likely causes application failure,
+		 * so fall back to the bottom-up function here. This scenario
+		 * can happen with large stack limits and large mmap()
+		 * allocations.
+		 */
+	}
+
+	info.flags = 0;
+	info.low_limit = mm->mmap_base;
+	info.high_limit = TASK_SIZE;
+	return vm_unmapped_area(&info);
+}
+
+unsigned long arch_get_unmapped_area(struct file *filp, unsigned long addr0,
+	unsigned long len, unsigned long pgoff, unsigned long flags)
+{
+	return arch_get_unmapped_area_common(filp,
+			addr0, len, pgoff, flags, UP);
+}
+
+/*
+ * There is no need to export this but sched.h declares the function as
+ * extern so making it static here results in an error.
+ */
+unsigned long arch_get_unmapped_area_topdown(struct file *filp,
+	unsigned long addr0, unsigned long len, unsigned long pgoff,
+	unsigned long flags)
+{
+	return arch_get_unmapped_area_common(filp,
+			addr0, len, pgoff, flags, DOWN);
+}
+
+unsigned long arch_mmap_rnd(void)
+{
+	unsigned long rnd;
+
+	rnd = get_random_long() & ((1UL << mmap_rnd_bits) - 1);
+
+	return rnd << PAGE_SHIFT;
+}
+
+void arch_pick_mmap_layout(struct mm_struct *mm, struct rlimit *rlim_stack)
+{
+	unsigned long random_factor = 0UL;
+
+	if (current->flags & PF_RANDOMIZE)
+		random_factor = arch_mmap_rnd();
+
+	if (mmap_is_legacy(rlim_stack)) {
+		mm->mmap_base = TASK_UNMAPPED_BASE + random_factor;
+		mm->get_unmapped_area = arch_get_unmapped_area;
+	} else {
+		mm->mmap_base = mmap_base(random_factor, rlim_stack);
+		mm->get_unmapped_area = arch_get_unmapped_area_topdown;
+	}
+}
+
+static inline unsigned long brk_rnd(void)
+{
+	unsigned long rnd = get_random_long();
+
+	rnd = rnd << PAGE_SHIFT;
+	/* 8MB for 32bit, 256MB for 64bit */
+	if (TASK_IS_32BIT_ADDR)
+		rnd = rnd & 0x7ffffful;
+	else
+		rnd = rnd & 0xffffffful;
+
+	return rnd;
+}
+
+unsigned long arch_randomize_brk(struct mm_struct *mm)
+{
+	unsigned long base = mm->brk;
+	unsigned long ret;
+
+	ret = PAGE_ALIGN(base + brk_rnd());
+
+	if (ret < mm->brk)
+		return mm->brk;
+
+	return ret;
+}
+
+int __virt_addr_valid(const volatile void *kaddr)
+{
+	unsigned long vaddr = (unsigned long)kaddr;
+
+	if ((vaddr < PAGE_OFFSET) || (vaddr >= vm_map_base))
+		return 0;
+
+	return pfn_valid(PFN_DOWN(virt_to_phys(kaddr)));
+}
+EXPORT_SYMBOL_GPL(__virt_addr_valid);
diff --git a/arch/loongarch/mm/page.S b/arch/loongarch/mm/page.S
new file mode 100644
index 000000000000..548e3e325795
--- /dev/null
+++ b/arch/loongarch/mm/page.S
@@ -0,0 +1,93 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/linkage.h>
+#include <asm/asm.h>
+#include <asm/export.h>
+#include <asm/regdef.h>
+
+#ifdef CONFIG_PAGE_SIZE_4KB
+#define PAGE_SHIFT      12
+#endif
+#ifdef CONFIG_PAGE_SIZE_16KB
+#define PAGE_SHIFT      14
+#endif
+#ifdef CONFIG_PAGE_SIZE_64KB
+#define PAGE_SHIFT      16
+#endif
+
+	.align 5
+SYM_FUNC_START(clear_page)
+	lu12i.w  t0, 1 << (PAGE_SHIFT - 12)
+	add.d    t0, t0, a0
+1:
+	st.d     zero, a0, 0
+	st.d     zero, a0, 8
+	st.d     zero, a0, 16
+	st.d     zero, a0, 24
+	st.d     zero, a0, 32
+	st.d     zero, a0, 40
+	st.d     zero, a0, 48
+	st.d     zero, a0, 56
+	addi.d   a0,   a0, 128
+	st.d     zero, a0, -64
+	st.d     zero, a0, -56
+	st.d     zero, a0, -48
+	st.d     zero, a0, -40
+	st.d     zero, a0, -32
+	st.d     zero, a0, -24
+	st.d     zero, a0, -16
+	st.d     zero, a0, -8
+	bne      t0,   a0, 1b
+
+	jirl     $r0, ra, 0
+SYM_FUNC_END(clear_page)
+EXPORT_SYMBOL(clear_page)
+
+.align 5
+SYM_FUNC_START(copy_page)
+	lu12i.w  t8, 1 << (PAGE_SHIFT - 12)
+	add.d    t8, t8, a0
+1:
+	ld.d     t0, a1,  0
+	ld.d     t1, a1,  8
+	ld.d     t2, a1,  16
+	ld.d     t3, a1,  24
+	ld.d     t4, a1,  32
+	ld.d     t5, a1,  40
+	ld.d     t6, a1,  48
+	ld.d     t7, a1,  56
+
+	st.d     t0, a0,  0
+	st.d     t1, a0,  8
+	ld.d     t0, a1,  64
+	ld.d     t1, a1,  72
+	st.d     t2, a0,  16
+	st.d     t3, a0,  24
+	ld.d     t2, a1,  80
+	ld.d     t3, a1,  88
+	st.d     t4, a0,  32
+	st.d     t5, a0,  40
+	ld.d     t4, a1,  96
+	ld.d     t5, a1,  104
+	st.d     t6, a0,  48
+	st.d     t7, a0,  56
+	ld.d     t6, a1,  112
+	ld.d     t7, a1,  120
+	addi.d   a0, a0,  128
+	addi.d   a1, a1,  128
+
+	st.d     t0, a0,  -64
+	st.d     t1, a0,  -56
+	st.d     t2, a0,  -48
+	st.d     t3, a0,  -40
+	st.d     t4, a0,  -32
+	st.d     t5, a0,  -24
+	st.d     t6, a0,  -16
+	st.d     t7, a0,  -8
+
+	bne      t8, a0, 1b
+	jirl     $r0, ra, 0
+SYM_FUNC_END(copy_page)
+EXPORT_SYMBOL(copy_page)
diff --git a/arch/loongarch/mm/pgtable-64.c b/arch/loongarch/mm/pgtable-64.c
new file mode 100644
index 000000000000..caa6792429b9
--- /dev/null
+++ b/arch/loongarch/mm/pgtable-64.c
@@ -0,0 +1,116 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <asm/pgtable.h>
+#include <asm/pgalloc.h>
+#include <asm/tlbflush.h>
+
+void pgd_init(unsigned long page)
+{
+	unsigned long *p, *end;
+	unsigned long entry;
+
+#if !defined(__PAGETABLE_PUD_FOLDED)
+	entry = (unsigned long)invalid_pud_table;
+#elif !defined(__PAGETABLE_PMD_FOLDED)
+	entry = (unsigned long)invalid_pmd_table;
+#else
+	entry = (unsigned long)invalid_pte_table;
+#endif
+
+	p = (unsigned long *) page;
+	end = p + PTRS_PER_PGD;
+
+	do {
+		p[0] = entry;
+		p[1] = entry;
+		p[2] = entry;
+		p[3] = entry;
+		p[4] = entry;
+		p += 8;
+		p[-3] = entry;
+		p[-2] = entry;
+		p[-1] = entry;
+	} while (p != end);
+}
+
+#ifndef __PAGETABLE_PMD_FOLDED
+void pmd_init(unsigned long addr, unsigned long pagetable)
+{
+	unsigned long *p, *end;
+
+	p = (unsigned long *) addr;
+	end = p + PTRS_PER_PMD;
+
+	do {
+		p[0] = pagetable;
+		p[1] = pagetable;
+		p[2] = pagetable;
+		p[3] = pagetable;
+		p[4] = pagetable;
+		p += 8;
+		p[-3] = pagetable;
+		p[-2] = pagetable;
+		p[-1] = pagetable;
+	} while (p != end);
+}
+EXPORT_SYMBOL_GPL(pmd_init);
+#endif
+
+#ifndef __PAGETABLE_PUD_FOLDED
+void pud_init(unsigned long addr, unsigned long pagetable)
+{
+	unsigned long *p, *end;
+
+	p = (unsigned long *)addr;
+	end = p + PTRS_PER_PUD;
+
+	do {
+		p[0] = pagetable;
+		p[1] = pagetable;
+		p[2] = pagetable;
+		p[3] = pagetable;
+		p[4] = pagetable;
+		p += 8;
+		p[-3] = pagetable;
+		p[-2] = pagetable;
+		p[-1] = pagetable;
+	} while (p != end);
+}
+#endif
+
+pmd_t mk_pmd(struct page *page, pgprot_t prot)
+{
+	pmd_t pmd;
+
+	pmd_val(pmd) = (page_to_pfn(page) << _PFN_SHIFT) | pgprot_val(prot);
+
+	return pmd;
+}
+
+void set_pmd_at(struct mm_struct *mm, unsigned long addr,
+		pmd_t *pmdp, pmd_t pmd)
+{
+	*pmdp = pmd;
+	flush_tlb_all();
+}
+
+void __init pagetable_init(void)
+{
+	pgd_t *pgd_base;
+
+	/* Initialize the entire pgd.  */
+	pgd_init((unsigned long)swapper_pg_dir);
+	pgd_init((unsigned long)invalid_pg_dir);
+#ifndef __PAGETABLE_PUD_FOLDED
+	pud_init((unsigned long)invalid_pud_table, (unsigned long)invalid_pmd_table);
+#endif
+#ifndef __PAGETABLE_PMD_FOLDED
+	pmd_init((unsigned long)invalid_pmd_table, (unsigned long)invalid_pte_table);
+#endif
+	pgd_base = swapper_pg_dir;
+}
diff --git a/arch/loongarch/mm/pgtable.c b/arch/loongarch/mm/pgtable.c
new file mode 100644
index 000000000000..9f776f200f5c
--- /dev/null
+++ b/arch/loongarch/mm/pgtable.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/export.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <asm/pgalloc.h>
+
+pgd_t *pgd_alloc(struct mm_struct *mm)
+{
+	pgd_t *ret, *init;
+
+	ret = (pgd_t *) __get_free_pages(GFP_KERNEL, PGD_ORDER);
+	if (ret) {
+		init = pgd_offset(&init_mm, 0UL);
+		pgd_init((unsigned long)ret);
+		memcpy(ret + USER_PTRS_PER_PGD, init + USER_PTRS_PER_PGD,
+		       (PTRS_PER_PGD - USER_PTRS_PER_PGD) * sizeof(pgd_t));
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(pgd_alloc);
diff --git a/arch/loongarch/mm/tlb.c b/arch/loongarch/mm/tlb.c
new file mode 100644
index 000000000000..ec961f6a9688
--- /dev/null
+++ b/arch/loongarch/mm/tlb.c
@@ -0,0 +1,278 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/smp.h>
+#include <linux/mm.h>
+#include <linux/hugetlb.h>
+#include <linux/export.h>
+
+#include <asm/cpu.h>
+#include <asm/bootinfo.h>
+#include <asm/mmu_context.h>
+#include <asm/pgtable.h>
+#include <asm/tlb.h>
+
+void local_flush_tlb_all(void)
+{
+	invtlb_all(INVTLB_CURRENT_ALL, 0, 0);
+}
+EXPORT_SYMBOL(local_flush_tlb_all);
+
+/*
+ * All entries common to a mm share an asid. To effectively flush
+ * these entries, we just bump the asid.
+ */
+void local_flush_tlb_mm(struct mm_struct *mm)
+{
+	int cpu;
+
+	preempt_disable();
+
+	cpu = smp_processor_id();
+
+	if (asid_valid(mm, cpu))
+		drop_mmu_context(mm, cpu);
+	else
+		cpumask_clear_cpu(cpu, mm_cpumask(mm));
+
+	preempt_enable();
+}
+
+void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start,
+	unsigned long end)
+{
+	struct mm_struct *mm = vma->vm_mm;
+	int cpu = smp_processor_id();
+
+	if (asid_valid(mm, cpu)) {
+		unsigned long size, flags;
+
+		local_irq_save(flags);
+		start = round_down(start, PAGE_SIZE << 1);
+		end = round_up(end, PAGE_SIZE << 1);
+		size = (end - start) >> (PAGE_SHIFT + 1);
+		if (size <= (current_cpu_data.tlbsizestlbsets ?
+			     current_cpu_data.tlbsize / 8 :
+			     current_cpu_data.tlbsize / 2)) {
+			int asid = cpu_asid(cpu, mm);
+
+			while (start < end) {
+				invtlb(INVTLB_ADDR_GFALSE_AND_ASID, asid, start);
+				start += (PAGE_SIZE << 1);
+			}
+		} else {
+			drop_mmu_context(mm, cpu);
+		}
+		local_irq_restore(flags);
+	} else {
+		cpumask_clear_cpu(cpu, mm_cpumask(mm));
+	}
+}
+
+void local_flush_tlb_kernel_range(unsigned long start, unsigned long end)
+{
+	unsigned long size, flags;
+
+	local_irq_save(flags);
+	size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
+	size = (size + 1) >> 1;
+	if (size <= (current_cpu_data.tlbsizestlbsets ?
+		     current_cpu_data.tlbsize / 8 :
+		     current_cpu_data.tlbsize / 2)) {
+
+		start &= (PAGE_MASK << 1);
+		end += ((PAGE_SIZE << 1) - 1);
+		end &= (PAGE_MASK << 1);
+
+		while (start < end) {
+			invtlb_addr(INVTLB_ADDR_GTRUE_OR_ASID, 0, start);
+			start += (PAGE_SIZE << 1);
+		}
+	} else {
+		local_flush_tlb_all();
+	}
+	local_irq_restore(flags);
+}
+
+void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+	int cpu = smp_processor_id();
+
+	if (asid_valid(vma->vm_mm, cpu)) {
+		int newpid;
+
+		newpid = cpu_asid(cpu, vma->vm_mm);
+		page &= (PAGE_MASK << 1);
+		invtlb(INVTLB_ADDR_GFALSE_AND_ASID, newpid, page);
+	} else {
+		cpumask_clear_cpu(cpu, mm_cpumask(vma->vm_mm));
+	}
+}
+
+/*
+ * This one is only used for pages with the global bit set so we don't care
+ * much about the ASID.
+ */
+void local_flush_tlb_one(unsigned long page)
+{
+	page &= (PAGE_MASK << 1);
+	invtlb_addr(INVTLB_ADDR_GTRUE_OR_ASID, 0, page);
+}
+
+static void __update_hugetlb(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
+{
+	int idx;
+	unsigned long lo;
+	unsigned long flags;
+
+	local_irq_save(flags);
+
+	address &= (PAGE_MASK << 1);
+	write_csr_entryhi(address);
+	tlb_probe();
+	idx = read_csr_tlbidx();
+	write_csr_pagesize(PS_HUGE_SIZE);
+	lo = pmd_to_entrylo(pte_val(*ptep));
+	write_csr_entrylo0(lo);
+	write_csr_entrylo1(lo + (HPAGE_SIZE >> 1));
+
+	if (idx < 0)
+		tlb_write_random();
+	else
+		tlb_write_indexed();
+	write_csr_pagesize(PS_DEFAULT_SIZE);
+
+	local_irq_restore(flags);
+}
+
+void __update_tlb(struct vm_area_struct *vma, unsigned long address, pte_t *ptep)
+{
+	int idx;
+	unsigned long flags;
+
+	/*
+	 * Handle debugger faulting in for debugee.
+	 */
+	if (current->active_mm != vma->vm_mm)
+		return;
+
+	if (pte_val(*ptep) & _PAGE_HUGE)
+		return __update_hugetlb(vma, address, ptep);
+
+	local_irq_save(flags);
+
+	if ((unsigned long)ptep & sizeof(pte_t))
+		ptep--;
+
+	address &= (PAGE_MASK << 1);
+	write_csr_entryhi(address);
+	tlb_probe();
+	idx = read_csr_tlbidx();
+	write_csr_pagesize(PS_DEFAULT_SIZE);
+	write_csr_entrylo0(pte_val(*ptep++));
+	write_csr_entrylo1(pte_val(*ptep));
+	if (idx < 0)
+		tlb_write_random();
+	else
+		tlb_write_indexed();
+
+	local_irq_restore(flags);
+}
+
+#ifdef CONFIG_TRANSPARENT_HUGEPAGE
+
+int has_transparent_hugepage(void)
+{
+	static unsigned int size = -1;
+
+	if (size == -1) {	/* first call comes during __init */
+		unsigned long flags;
+
+		local_irq_save(flags);
+		write_csr_pagesize(PS_HUGE_SIZE);
+		size = read_csr_pagesize();
+		write_csr_pagesize(PS_DEFAULT_SIZE);
+		local_irq_restore(flags);
+	}
+	return size == PS_HUGE_SIZE;
+}
+
+#endif /* CONFIG_TRANSPARENT_HUGEPAGE  */
+
+static void setup_pw(void)
+{
+	unsigned long pgd_i, pgd_w;
+#ifndef __PAGETABLE_PMD_FOLDED
+	unsigned long pmd_i, pmd_w;
+#endif
+	unsigned long pte_i, pte_w;
+
+	pgd_i = PGDIR_SHIFT;  /* 1st level PGD */
+#ifndef __PAGETABLE_PMD_FOLDED
+	pgd_w = PGDIR_SHIFT - PMD_SHIFT + PGD_ORDER;
+	pmd_i = PMD_SHIFT;    /* 2nd level PMD */
+	pmd_w = PMD_SHIFT - PAGE_SHIFT;
+#else
+	pgd_w = PGDIR_SHIFT - PAGE_SHIFT + PGD_ORDER;
+#endif
+	pte_i  = PAGE_SHIFT;    /* 3rd level PTE */
+	pte_w  = PAGE_SHIFT - 3;
+
+#ifndef __PAGETABLE_PMD_FOLDED
+	csr_writeq(pte_i | pte_w << 5 | pmd_i << 10 | pmd_w << 15, LOONGARCH_CSR_PWCTL0);
+	csr_writeq(pgd_i | pgd_w << 6, LOONGARCH_CSR_PWCTL1);
+#else
+	csr_writeq(pte_i | pte_w << 5, LOONGARCH_CSR_PWCTL0);
+	csr_writeq(pgd_i | pgd_w << 6, LOONGARCH_CSR_PWCTL1);
+#endif
+	csr_writeq((long)swapper_pg_dir, LOONGARCH_CSR_PGDH);
+}
+
+static void output_pgtable_bits_defines(void)
+{
+#define pr_define(fmt, ...)					\
+	pr_debug("#define " fmt, ##__VA_ARGS__)
+
+	pr_debug("#include <asm/asm.h>\n");
+	pr_debug("#include <asm/regdef.h>\n");
+	pr_debug("\n");
+
+	pr_define("_PAGE_VALID_SHIFT %d\n", _PAGE_VALID_SHIFT);
+	pr_define("_PAGE_DIRTY_SHIFT %d\n", _PAGE_DIRTY_SHIFT);
+	pr_define("_PAGE_HUGE_SHIFT %d\n", _PAGE_HUGE_SHIFT);
+	pr_define("_PAGE_GLOBAL_SHIFT %d\n", _PAGE_GLOBAL_SHIFT);
+	pr_define("_PAGE_PRESENT_SHIFT %d\n", _PAGE_PRESENT_SHIFT);
+	pr_define("_PAGE_WRITE_SHIFT %d\n", _PAGE_WRITE_SHIFT);
+	pr_define("_PAGE_NO_READ_SHIFT %d\n", _PAGE_NO_READ_SHIFT);
+	pr_define("_PAGE_NO_EXEC_SHIFT %d\n", _PAGE_NO_EXEC_SHIFT);
+	pr_define("_PFN_SHIFT %d\n", _PFN_SHIFT);
+	pr_debug("\n");
+}
+
+void setup_tlb_handler(void)
+{
+	static int run_once = 0;
+
+	setup_pw();
+	output_pgtable_bits_defines();
+
+	/* The tlb handlers are generated only once */
+	if (!run_once) {
+		memcpy((void *)tlbrentry, handle_tlb_refill, 0x80);
+		local_flush_icache_range(tlbrentry, tlbrentry + 0x80);
+		run_once++;
+	}
+}
+void tlb_init(void)
+{
+	write_csr_pagesize(PS_DEFAULT_SIZE);
+
+	if (read_csr_pagesize() != PS_DEFAULT_SIZE)
+		panic("MMU doesn't support PAGE_SIZE=0x%lx", PAGE_SIZE);
+
+	setup_tlb_handler();
+	local_flush_tlb_all();
+}
diff --git a/arch/loongarch/mm/tlbex.S b/arch/loongarch/mm/tlbex.S
new file mode 100644
index 000000000000..4ba35d6d7a49
--- /dev/null
+++ b/arch/loongarch/mm/tlbex.S
@@ -0,0 +1,473 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <asm/asm.h>
+#include <asm/export.h>
+#include <asm/loongarchregs.h>
+#include <asm/page.h>
+#include <asm/pgtable-bits.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+#ifdef CONFIG_64BIT
+#include <asm/pgtable-64.h>
+#endif
+
+	.macro tlb_do_page_fault, write
+	SYM_FUNC_START(tlb_do_page_fault_\write)
+	SAVE_ALL
+	csrrd	a2, LOONGARCH_CSR_BADV
+	KMODE
+	move	a0, sp
+	REG_S	a2, sp, PT_BVADDR
+	li.w	a1, \write
+	la.abs	t0, do_page_fault
+	jirl    ra, t0, 0
+	la.abs	t0, ret_from_exception
+	jirl    zero, t0, 0
+	SYM_FUNC_END(tlb_do_page_fault_\write)
+	.endm
+
+SYM_FUNC_START(handle_tlb_rixi)
+	csrwr	t0, EXCEPTION_KS0
+	csrwr	t1, EXCEPTION_KS1
+SYM_FUNC_END(handle_tlb_rixi)
+	/* Go through */
+	tlb_do_page_fault 0
+	tlb_do_page_fault 1
+
+SYM_FUNC_START(handle_tlb_load)
+	csrwr	t0, EXCEPTION_KS0
+	csrwr	t1, EXCEPTION_KS1
+	csrwr	ra, EXCEPTION_KS2
+
+	/*
+	 * The vmalloc handling is not in the hotpath.
+	 */
+	csrrd	t0, LOONGARCH_CSR_BADV
+	blt	t0, $r0, vmalloc_load
+	csrrd	t1, LOONGARCH_CSR_PGDL
+
+vmalloc_done_load:
+	/* Get PGD offset in bytes */
+	srli.d	t0, t0, PGDIR_SHIFT
+	andi	t0, t0, (PTRS_PER_PGD - 1)
+	slli.d	t0, t0, 3
+	add.d	t1, t1, t0
+#if CONFIG_PGTABLE_LEVELS > 3
+	csrrd	t0, LOONGARCH_CSR_BADV
+	ld.d t1, t1, 0
+	srli.d	t0, t0, PUD_SHIFT
+	andi	t0, t0, (PTRS_PER_PUD - 1)
+	slli.d	t0, t0, 3
+	add.d	t1, t1, t0
+#endif
+#if CONFIG_PGTABLE_LEVELS > 2
+	csrrd	t0, LOONGARCH_CSR_BADV
+	ld.d	t1, t1, 0
+	srli.d	t0, t0, PMD_SHIFT
+	andi	t0, t0, (PTRS_PER_PMD - 1)
+	slli.d	t0, t0, 3
+	add.d	t1, t1, t0
+#endif
+	ld.d	ra, t1, 0
+
+	/*
+	 * For huge tlb entries, pmde doesn't contain an address but
+	 * instead contains the tlb pte. Check the PAGE_HUGE bit and
+	 * see if we need to jump to huge tlb processing.
+	 */
+	andi	t0, ra, _PAGE_HUGE
+	bne	t0, $r0, tlb_huge_update_load
+
+	csrrd	t0, LOONGARCH_CSR_BADV
+	srli.d	t0, t0, (PAGE_SHIFT + PTE_ORDER)
+	andi	t0, t0, (PTRS_PER_PTE - 1)
+	slli.d	t0, t0, _PTE_T_LOG2
+	add.d	t1, ra, t0
+
+	ld.d	t0, t1, 0
+	tlbsrch
+
+	srli.d	ra, t0, _PAGE_PRESENT_SHIFT
+	andi	ra, ra, 1
+	beq	ra, $r0, nopage_tlb_load
+
+	ori	t0, t0, _PAGE_VALID
+	st.d	t0, t1, 0
+	ori	t1, t1, 8
+	xori	t1, t1, 8
+	ld.d	t0, t1, 0
+	ld.d	t1, t1, 8
+	csrwr	t0, LOONGARCH_CSR_TLBELO0
+	csrwr	t1, LOONGARCH_CSR_TLBELO1
+	tlbwr
+leave_load:
+	csrrd	t0, EXCEPTION_KS0
+	csrrd	t1, EXCEPTION_KS1
+	csrrd	ra, EXCEPTION_KS2
+	ertn
+#ifdef CONFIG_64BIT
+vmalloc_load:
+	la.abs	t1, swapper_pg_dir
+	b	vmalloc_done_load
+#endif
+
+	/*
+	 * This is the entry point when build_tlbchange_handler_head
+	 * spots a huge page.
+	 */
+tlb_huge_update_load:
+	ld.d	t0, t1, 0
+	srli.d	ra, t0, _PAGE_PRESENT_SHIFT
+	andi	ra, ra, 1
+	beq	ra, $r0, nopage_tlb_load
+	tlbsrch
+
+	ori	t0, t0, _PAGE_VALID
+	st.d	t0, t1, 0
+	addu16i.d	t1, $r0, -(CSR_TLBIDX_EHINV >> 16)
+	addi.d	ra, t1, 0
+	csrxchg	ra, t1, LOONGARCH_CSR_TLBIDX
+	tlbwr
+
+	csrxchg	$r0, t1, LOONGARCH_CSR_TLBIDX
+
+	/*
+	 * A huge PTE describes an area the size of the
+	 * configured huge page size. This is twice the
+	 * of the large TLB entry size we intend to use.
+	 * A TLB entry half the size of the configured
+	 * huge page size is configured into entrylo0
+	 * and entrylo1 to cover the contiguous huge PTE
+	 * address space.
+	 */
+	/* Huge page: Move Global bit */
+	xori	t0, t0, _PAGE_HUGE
+	lu12i.w	t1, _PAGE_HGLOBAL >> 12
+	and	t1, t0, t1
+	srli.d	t1, t1, (_PAGE_HGLOBAL_SHIFT - _PAGE_GLOBAL_SHIFT)
+	or	t0, t0, t1
+
+	addi.d	ra, t0, 0
+	csrwr	t0, LOONGARCH_CSR_TLBELO0
+	addi.d	t0, ra, 0
+
+	/* Convert to entrylo1 */
+	addi.d	t1, $r0, 1
+	slli.d	t1, t1, (HPAGE_SHIFT - 1)
+	add.d	t0, t0, t1
+	csrwr	t0, LOONGARCH_CSR_TLBELO1
+
+	/* Set huge page tlb entry size */
+	addu16i.d	t0, $r0, (PS_MASK >> 16)
+	addu16i.d	t1, $r0, (PS_HUGE_SIZE << (PS_SHIFT - 16))
+	csrxchg		t1, t0, LOONGARCH_CSR_TLBIDX
+
+	tlbfill
+
+	addu16i.d	t0, $r0, (PS_MASK >> 16)
+	addu16i.d	t1, $r0, (PS_DEFAULT_SIZE << (PS_SHIFT - 16))
+	csrxchg		t1, t0, LOONGARCH_CSR_TLBIDX
+
+nopage_tlb_load:
+	csrrd	ra, EXCEPTION_KS2
+	la.abs	t0, tlb_do_page_fault_0
+	jirl	$r0, t0, 0
+SYM_FUNC_END(handle_tlb_load)
+
+SYM_FUNC_START(handle_tlb_store)
+	csrwr	t0, EXCEPTION_KS0
+	csrwr	t1, EXCEPTION_KS1
+	csrwr	ra, EXCEPTION_KS2
+
+	/*
+	 * The vmalloc handling is not in the hotpath.
+	 */
+	csrrd	t0, LOONGARCH_CSR_BADV
+	blt	t0, $r0, vmalloc_store
+	csrrd	t1, LOONGARCH_CSR_PGDL
+
+vmalloc_done_store:
+	/* Get PGD offset in bytes */
+	srli.d	t0, t0, PGDIR_SHIFT
+	andi	t0, t0, (PTRS_PER_PGD - 1)
+	slli.d	t0, t0, 3
+	add.d	t1, t1, t0
+
+#if CONFIG_PGTABLE_LEVELS > 3
+	csrrd	t0, LOONGARCH_CSR_BADV
+	ld.d t1, t1, 0
+	srli.d	t0, t0, PUD_SHIFT
+	andi	t0, t0, (PTRS_PER_PUD - 1)
+	slli.d	t0, t0, 3
+	add.d	t1, t1, t0
+#endif
+#if CONFIG_PGTABLE_LEVELS > 2
+	csrrd	t0, LOONGARCH_CSR_BADV
+	ld.d	t1, t1, 0
+	srli.d	t0, t0, PMD_SHIFT
+	andi	t0, t0, (PTRS_PER_PMD - 1)
+	slli.d	t0, t0, 3
+	add.d	t1, t1, t0
+#endif
+	ld.d	ra, t1, 0
+
+	/*
+	 * For huge tlb entries, pmde doesn't contain an address but
+	 * instead contains the tlb pte. Check the PAGE_HUGE bit and
+	 * see if we need to jump to huge tlb processing.
+	 */
+	andi	t0, ra, _PAGE_HUGE
+	bne	t0, $r0, tlb_huge_update_store
+
+	csrrd	t0, LOONGARCH_CSR_BADV
+	srli.d	t0, t0, (PAGE_SHIFT + PTE_ORDER)
+	andi	t0, t0, (PTRS_PER_PTE - 1)
+	slli.d	t0, t0, _PTE_T_LOG2
+	add.d	t1, ra, t0
+
+	ld.d	t0, t1, 0
+	tlbsrch
+
+	srli.d	ra, t0, _PAGE_PRESENT_SHIFT
+	andi	ra, ra, ((_PAGE_PRESENT | _PAGE_WRITE) >> _PAGE_PRESENT_SHIFT)
+	xori	ra, ra, ((_PAGE_PRESENT | _PAGE_WRITE) >> _PAGE_PRESENT_SHIFT)
+	bne	ra, $r0, nopage_tlb_store
+
+	ori	t0, t0, (_PAGE_VALID | _PAGE_DIRTY)
+	st.d	t0, t1, 0
+
+	ori	t1, t1, 8
+	xori	t1, t1, 8
+	ld.d	t0, t1, 0
+	ld.d	t1, t1, 8
+	csrwr	t0, LOONGARCH_CSR_TLBELO0
+	csrwr	t1, LOONGARCH_CSR_TLBELO1
+	tlbwr
+leave_store:
+	csrrd	t0, EXCEPTION_KS0
+	csrrd	t1, EXCEPTION_KS1
+	csrrd	ra, EXCEPTION_KS2
+	ertn
+#ifdef CONFIG_64BIT
+vmalloc_store:
+	la.abs	t1, swapper_pg_dir
+	b	vmalloc_done_store
+#endif
+
+	/*
+	 * This is the entry point when build_tlbchange_handler_head
+	 * spots a huge page.
+	 */
+tlb_huge_update_store:
+	ld.d	t0, t1, 0
+	srli.d	ra, t0, _PAGE_PRESENT_SHIFT
+	andi	ra, ra, ((_PAGE_PRESENT | _PAGE_WRITE) >> _PAGE_PRESENT_SHIFT)
+	xori	ra, ra, ((_PAGE_PRESENT | _PAGE_WRITE) >> _PAGE_PRESENT_SHIFT)
+	bne	ra, $r0, nopage_tlb_store
+
+	tlbsrch
+	ori	t0, t0, (_PAGE_VALID | _PAGE_DIRTY)
+
+	st.d	t0, t1, 0
+	addu16i.d	t1, $r0, -(CSR_TLBIDX_EHINV >> 16)
+	addi.d	ra, t1, 0
+	csrxchg	ra, t1, LOONGARCH_CSR_TLBIDX
+	tlbwr
+
+	csrxchg	$r0, t1, LOONGARCH_CSR_TLBIDX
+	/*
+	 * A huge PTE describes an area the size of the
+	 * configured huge page size. This is twice the
+	 * of the large TLB entry size we intend to use.
+	 * A TLB entry half the size of the configured
+	 * huge page size is configured into entrylo0
+	 * and entrylo1 to cover the contiguous huge PTE
+	 * address space.
+	 */
+	/* Huge page: Move Global bit */
+	xori	t0, t0, _PAGE_HUGE
+	lu12i.w	t1, _PAGE_HGLOBAL >> 12
+	and	t1, t0, t1
+	srli.d	t1, t1, (_PAGE_HGLOBAL_SHIFT - _PAGE_GLOBAL_SHIFT)
+	or	t0, t0, t1
+
+	addi.d	ra, t0, 0
+	csrwr	t0, LOONGARCH_CSR_TLBELO0
+	addi.d	t0, ra, 0
+
+	/* Convert to entrylo1 */
+	addi.d	t1, $r0, 1
+	slli.d	t1, t1, (HPAGE_SHIFT - 1)
+	add.d	t0, t0, t1
+	csrwr	t0, LOONGARCH_CSR_TLBELO1
+
+	/* Set huge page tlb entry size */
+	addu16i.d	t0, $r0, (PS_MASK >> 16)
+	addu16i.d	t1, $r0, (PS_HUGE_SIZE << (PS_SHIFT - 16))
+	csrxchg		t1, t0, LOONGARCH_CSR_TLBIDX
+
+	tlbfill
+
+	/* Reset default page size */
+	addu16i.d	t0, $r0, (PS_MASK >> 16)
+	addu16i.d	t1, $r0, (PS_DEFAULT_SIZE << (PS_SHIFT - 16))
+	csrxchg		t1, t0, LOONGARCH_CSR_TLBIDX
+
+nopage_tlb_store:
+	csrrd	ra, EXCEPTION_KS2
+	la.abs	t0, tlb_do_page_fault_1
+	jirl	$r0, t0, 0
+SYM_FUNC_END(handle_tlb_store)
+
+SYM_FUNC_START(handle_tlb_modify)
+	csrwr	t0, EXCEPTION_KS0
+	csrwr	t1, EXCEPTION_KS1
+	csrwr	ra, EXCEPTION_KS2
+
+	/*
+	 * The vmalloc handling is not in the hotpath.
+	 */
+	csrrd	t0, LOONGARCH_CSR_BADV
+	blt	t0, $r0, vmalloc_modify
+	csrrd	t1, LOONGARCH_CSR_PGDL
+
+vmalloc_done_modify:
+	/* Get PGD offset in bytes */
+	srli.d	t0, t0, PGDIR_SHIFT
+	andi	t0, t0, (PTRS_PER_PGD - 1)
+	slli.d	t0, t0, 3
+	add.d	t1, t1, t0
+#if CONFIG_PGTABLE_LEVELS > 3
+	csrrd	t0, LOONGARCH_CSR_BADV
+	ld.d t1, t1, 0
+	srli.d	t0, t0, PUD_SHIFT
+	andi	t0, t0, (PTRS_PER_PUD - 1)
+	slli.d	t0, t0, 3
+	add.d	t1, t1, t0
+#endif
+#if CONFIG_PGTABLE_LEVELS > 2
+	csrrd	t0, LOONGARCH_CSR_BADV
+	ld.d	t1, t1, 0
+	srli.d	t0, t0, PMD_SHIFT
+	andi	t0, t0, (PTRS_PER_PMD - 1)
+	slli.d	t0, t0, 3
+	add.d	t1, t1, t0
+#endif
+	ld.d	ra, t1, 0
+
+	/*
+	 * For huge tlb entries, pmde doesn't contain an address but
+	 * instead contains the tlb pte. Check the PAGE_HUGE bit and
+	 * see if we need to jump to huge tlb processing.
+	 */
+	andi	t0, ra, _PAGE_HUGE
+	bne	t0, $r0, tlb_huge_update_modify
+
+	csrrd	t0, LOONGARCH_CSR_BADV
+	srli.d	t0, t0, (PAGE_SHIFT + PTE_ORDER)
+	andi	t0, t0, (PTRS_PER_PTE - 1)
+	slli.d	t0, t0, _PTE_T_LOG2
+	add.d	t1, ra, t0
+
+	ld.d	t0, t1, 0
+	tlbsrch
+
+	srli.d	ra, t0, _PAGE_WRITE_SHIFT
+	andi	ra, ra, 1
+	beq	ra, $r0, nopage_tlb_modify
+
+	ori	t0, t0, (_PAGE_VALID | _PAGE_DIRTY)
+	st.d	t0, t1, 0
+	ori	t1, t1, 8
+	xori	t1, t1, 8
+	ld.d	t0, t1, 0
+	ld.d	t1, t1, 8
+	csrwr	t0, LOONGARCH_CSR_TLBELO0
+	csrwr	t1, LOONGARCH_CSR_TLBELO1
+	tlbwr
+leave_modify:
+	csrrd	t0, EXCEPTION_KS0
+	csrrd	t1, EXCEPTION_KS1
+	csrrd	ra, EXCEPTION_KS2
+	ertn
+#ifdef CONFIG_64BIT
+vmalloc_modify:
+	la.abs  t1, swapper_pg_dir
+	b	vmalloc_done_modify
+#endif
+
+	/*
+	 * This is the entry point when
+	 * build_tlbchange_handler_head spots a huge page.
+	 */
+tlb_huge_update_modify:
+	ld.d	t0, t1, 0
+
+	srli.d	ra, t0, _PAGE_WRITE_SHIFT
+	andi	ra, ra, 1
+	beq	ra, $r0, nopage_tlb_modify
+
+	tlbsrch
+	ori	t0, t0, (_PAGE_VALID | _PAGE_DIRTY)
+
+	st.d	t0, t1, 0
+	/*
+	 * A huge PTE describes an area the size of the
+	 * configured huge page size. This is twice the
+	 * of the large TLB entry size we intend to use.
+	 * A TLB entry half the size of the configured
+	 * huge page size is configured into entrylo0
+	 * and entrylo1 to cover the contiguous huge PTE
+	 * address space.
+	 */
+	/* Huge page: Move Global bit */
+	xori	t0, t0, _PAGE_HUGE
+	lu12i.w	t1, _PAGE_HGLOBAL >> 12
+	and	t1, t0, t1
+	srli.d	t1, t1, (_PAGE_HGLOBAL_SHIFT - _PAGE_GLOBAL_SHIFT)
+	or	t0, t0, t1
+
+	addi.d	ra, t0, 0
+	csrwr	t0, LOONGARCH_CSR_TLBELO0
+	addi.d	t0, ra, 0
+
+	/* Convert to entrylo1 */
+	addi.d	t1, $r0, 1
+	slli.d	t1, t1, (HPAGE_SHIFT - 1)
+	add.d	t0, t0, t1
+	csrwr	t0, LOONGARCH_CSR_TLBELO1
+
+	/* Set huge page tlb entry size */
+	addu16i.d	t0, $r0, (PS_MASK >> 16)
+	addu16i.d	t1, $r0, (PS_HUGE_SIZE << (PS_SHIFT - 16))
+	csrxchg	t1, t0, LOONGARCH_CSR_TLBIDX
+
+	tlbwr
+
+	/* Reset default page size */
+	addu16i.d	t0, $r0, (PS_MASK >> 16)
+	addu16i.d	t1, $r0, (PS_DEFAULT_SIZE << (PS_SHIFT - 16))
+	csrxchg	t1, t0, LOONGARCH_CSR_TLBIDX
+
+nopage_tlb_modify:
+	csrrd	ra, EXCEPTION_KS2
+	la.abs	t0, tlb_do_page_fault_1
+	jirl	$r0, t0, 0
+SYM_FUNC_END(handle_tlb_modify)
+
+SYM_FUNC_START(handle_tlb_refill)
+	csrwr	t0, LOONGARCH_CSR_TLBRSAVE
+	csrrd	t0, LOONGARCH_CSR_PGD
+	lddir	t0, t0, 3
+#if CONFIG_PGTABLE_LEVELS > 2
+	lddir	t0, t0, 1
+#endif
+	ldpte	t0, 0
+	ldpte	t0, 1
+	tlbfill
+	csrrd	t0, LOONGARCH_CSR_TLBRSAVE
+	ertn
+SYM_FUNC_END(handle_tlb_refill)
-- 
2.27.0


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

* [PATCH 09/19] LoongArch: Add system call support
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (6 preceding siblings ...)
  2021-07-06  4:18 ` [PATCH 08/19] LoongArch: Add memory management Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06 10:17   ` Arnd Bergmann
  2021-07-06 13:51   ` Thomas Gleixner
  2021-07-06  4:18 ` [PATCH 10/19] LoongArch: Add signal handling support Huacai Chen
                   ` (12 subsequent siblings)
  20 siblings, 2 replies; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

This patch adds system call support and related code (uaccess and
seccomp) for LoongArch.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 arch/loongarch/include/asm/seccomp.h     |   9 +
 arch/loongarch/include/asm/syscall.h     |  74 ++++
 arch/loongarch/include/asm/uaccess.h     | 453 +++++++++++++++++++++++
 arch/loongarch/include/asm/unistd.h      |  11 +
 arch/loongarch/include/uapi/asm/unistd.h |   7 +
 arch/loongarch/kernel/scall64.S          | 126 +++++++
 arch/loongarch/kernel/syscall.c          |  84 +++++
 7 files changed, 764 insertions(+)
 create mode 100644 arch/loongarch/include/asm/seccomp.h
 create mode 100644 arch/loongarch/include/asm/syscall.h
 create mode 100644 arch/loongarch/include/asm/uaccess.h
 create mode 100644 arch/loongarch/include/asm/unistd.h
 create mode 100644 arch/loongarch/include/uapi/asm/unistd.h
 create mode 100644 arch/loongarch/kernel/scall64.S
 create mode 100644 arch/loongarch/kernel/syscall.c

diff --git a/arch/loongarch/include/asm/seccomp.h b/arch/loongarch/include/asm/seccomp.h
new file mode 100644
index 000000000000..220c885f5478
--- /dev/null
+++ b/arch/loongarch/include/asm/seccomp.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_SECCOMP_H
+#define __ASM_SECCOMP_H
+
+#include <linux/unistd.h>
+
+#include <asm-generic/seccomp.h>
+
+#endif /* __ASM_SECCOMP_H */
diff --git a/arch/loongarch/include/asm/syscall.h b/arch/loongarch/include/asm/syscall.h
new file mode 100644
index 000000000000..7d877ebfda77
--- /dev/null
+++ b/arch/loongarch/include/asm/syscall.h
@@ -0,0 +1,74 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Author: Hanlu Li <lihanlu@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef __ASM_LOONGARCH_SYSCALL_H
+#define __ASM_LOONGARCH_SYSCALL_H
+
+#include <linux/compiler.h>
+#include <uapi/linux/audit.h>
+#include <linux/elf-em.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <asm/ptrace.h>
+#include <asm/unistd.h>
+
+extern void *sys_call_table[];
+
+static inline long syscall_get_nr(struct task_struct *task,
+				  struct pt_regs *regs)
+{
+	return regs->regs[11];
+}
+
+static inline void syscall_set_nr(struct task_struct *task,
+				  struct pt_regs *regs,
+				  int syscall)
+{
+	regs->regs[11] = syscall;
+}
+
+static inline long syscall_get_error(struct task_struct *task,
+				     struct pt_regs *regs)
+{
+	return regs->regs[7] ? -regs->regs[4] : 0;
+}
+
+static inline long syscall_get_return_value(struct task_struct *task,
+					    struct pt_regs *regs)
+{
+	return regs->regs[4];
+}
+
+static inline void syscall_rollback(struct task_struct *task,
+				    struct pt_regs *regs)
+{
+	/* Do nothing */
+}
+
+static inline void syscall_set_return_value(struct task_struct *task,
+					    struct pt_regs *regs,
+					    int error, long val)
+{
+	regs->regs[4] = (long) error ? error : val;
+}
+
+static inline void syscall_get_arguments(struct task_struct *task,
+					 struct pt_regs *regs,
+					 unsigned long *args)
+{
+	args[0] = regs->orig_a0;
+	memcpy(&args[1], &regs->regs[5], 5 * sizeof(long));
+}
+
+static inline int syscall_get_arch(struct task_struct *task)
+{
+	return AUDIT_ARCH_LOONGARCH64;
+}
+
+#endif	/* __ASM_LOONGARCH_SYSCALL_H */
diff --git a/arch/loongarch/include/asm/uaccess.h b/arch/loongarch/include/asm/uaccess.h
new file mode 100644
index 000000000000..b760aa0a3bc6
--- /dev/null
+++ b/arch/loongarch/include/asm/uaccess.h
@@ -0,0 +1,453 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_UACCESS_H
+#define _ASM_UACCESS_H
+
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/extable.h>
+#include <asm-generic/extable.h>
+
+/*
+ * The fs value determines whether argument validity checking should be
+ * performed or not.  If get_fs() == USER_DS, checking is performed, with
+ * get_fs() == KERNEL_DS, checking is bypassed.
+ *
+ * For historical reasons, these macros are grossly misnamed.
+ */
+
+#ifdef CONFIG_64BIT
+
+extern u64 __ua_limit;
+
+#define __UA_LIMIT	__ua_limit
+
+#define __UA_ADDR	".dword"
+#define __UA_ADDU	"add.d"
+#define __UA_LA		"la.abs"
+
+#endif /* CONFIG_64BIT */
+
+/*
+ * Is a address valid? This does a straightforward calculation rather
+ * than tests.
+ *
+ * Address valid if:
+ *  - "addr" doesn't have any high-bits set
+ *  - AND "size" doesn't have any high-bits set
+ *  - AND "addr+size" doesn't have any high-bits set
+ *  - OR we are in kernel mode.
+ *
+ * __ua_size() is a trick to avoid runtime checking of positive constant
+ * sizes; for those we already know at compile time that the size is ok.
+ */
+#define __ua_size(size)							\
+	((__builtin_constant_p(size) && (signed long) (size) > 0) ? 0 : (size))
+
+/*
+ * access_ok: - Checks if a user space pointer is valid
+ * @addr: User space pointer to start of block to check
+ * @size: Size of block to check
+ *
+ * Context: User context only. This function may sleep if pagefaults are
+ *          enabled.
+ *
+ * Checks if a pointer to a block of memory in user space is valid.
+ *
+ * Returns true (nonzero) if the memory block may be valid, false (zero)
+ * if it is definitely invalid.
+ *
+ * Note that, depending on architecture, this function probably just
+ * checks that the pointer is in the user space range - after calling
+ * this function, memory access functions may still return -EFAULT.
+ */
+static inline int __access_ok(const void __user *p, unsigned long size)
+{
+	unsigned long addr = (unsigned long)p;
+	unsigned long end = addr + size - !!size;
+
+	return (__UA_LIMIT & (addr | end | __ua_size(size))) == 0;
+}
+
+#define access_ok(addr, size)					\
+	likely(__access_ok((addr), (size)))
+
+/*
+ * get_user: - Get a simple variable from user space.
+ * @x:	 Variable to store result.
+ * @ptr: Source address, in user space.
+ *
+ * Context: User context only. This function may sleep if pagefaults are
+ *          enabled.
+ *
+ * This macro copies a single simple variable from user space to kernel
+ * space.  It supports simple types like char and int, but not larger
+ * data types like structures or arrays.
+ *
+ * @ptr must have pointer-to-simple-variable type, and the result of
+ * dereferencing @ptr must be assignable to @x without a cast.
+ *
+ * Returns zero on success, or -EFAULT on error.
+ * On error, the variable @x is set to zero.
+ */
+#define get_user(x, ptr) \
+({									\
+	int __gu_err = -EFAULT;						\
+	const __typeof__(*(ptr)) __user *__gu_ptr = (ptr);		\
+									\
+	might_fault();							\
+	if (likely(access_ok(__gu_ptr, sizeof(*(ptr))))) {		\
+		__get_user_common((x), sizeof(*(ptr)), __gu_ptr);	\
+	} else								\
+		(x) = 0;						\
+									\
+	__gu_err;							\
+})
+
+/*
+ * put_user: - Write a simple value into user space.
+ * @x:	 Value to copy to user space.
+ * @ptr: Destination address, in user space.
+ *
+ * Context: User context only. This function may sleep if pagefaults are
+ *          enabled.
+ *
+ * This macro copies a single simple value from kernel space to user
+ * space.  It supports simple types like char and int, but not larger
+ * data types like structures or arrays.
+ *
+ * @ptr must have pointer-to-simple-variable type, and @x must be assignable
+ * to the result of dereferencing @ptr.
+ *
+ * Returns zero on success, or -EFAULT on error.
+ */
+#define put_user(x, ptr) \
+({									\
+	__typeof__(*(ptr)) __user *__pu_addr = (ptr);			\
+	__typeof__(*(ptr)) __pu_val = (x);				\
+	int __pu_err = -EFAULT;						\
+									\
+	might_fault();							\
+	if (likely(access_ok(__pu_addr, sizeof(*(ptr))))) {		\
+		__put_user_common(__pu_addr, sizeof(*(ptr)));		\
+	}								\
+									\
+	__pu_err;							\
+})
+
+/*
+ * __get_user: - Get a simple variable from user space, with less checking.
+ * @x:	 Variable to store result.
+ * @ptr: Source address, in user space.
+ *
+ * Context: User context only. This function may sleep if pagefaults are
+ *          enabled.
+ *
+ * This macro copies a single simple variable from user space to kernel
+ * space.  It supports simple types like char and int, but not larger
+ * data types like structures or arrays.
+ *
+ * @ptr must have pointer-to-simple-variable type, and the result of
+ * dereferencing @ptr must be assignable to @x without a cast.
+ *
+ * Caller must check the pointer with access_ok() before calling this
+ * function.
+ *
+ * Returns zero on success, or -EFAULT on error.
+ * On error, the variable @x is set to zero.
+ */
+#define __get_user(x, ptr) \
+({									\
+	int __gu_err;							\
+									\
+	__chk_user_ptr(ptr);						\
+	__get_user_common((x), sizeof(*(ptr)), ptr);			\
+	__gu_err;							\
+})
+
+/*
+ * __put_user: - Write a simple value into user space, with less checking.
+ * @x:	 Value to copy to user space.
+ * @ptr: Destination address, in user space.
+ *
+ * Context: User context only. This function may sleep if pagefaults are
+ *          enabled.
+ *
+ * This macro copies a single simple value from kernel space to user
+ * space.  It supports simple types like char and int, but not larger
+ * data types like structures or arrays.
+ *
+ * @ptr must have pointer-to-simple-variable type, and @x must be assignable
+ * to the result of dereferencing @ptr.
+ *
+ * Caller must check the pointer with access_ok() before calling this
+ * function.
+ *
+ * Returns zero on success, or -EFAULT on error.
+ */
+#define __put_user(x, ptr) \
+({									\
+	__typeof__(*(ptr)) __pu_val;					\
+	int __pu_err = 0;						\
+									\
+	__pu_val = (x);							\
+	__chk_user_ptr(ptr);						\
+	__put_user_common(ptr, sizeof(*(ptr)));					\
+	__pu_err;							\
+})
+
+struct __large_struct { unsigned long buf[100]; };
+#define __m(x) (*(struct __large_struct __user *)(x))
+
+#ifdef CONFIG_32BIT
+#define __GET_DW(val, insn, ptr) __get_data_asm_ll32(val, insn, ptr)
+#endif
+#ifdef CONFIG_64BIT
+#define __GET_DW(val, insn, ptr) __get_data_asm(val, insn, ptr)
+#endif
+
+#define __get_user_common(val, size, ptr)				\
+do {									\
+	switch (size) {							\
+	case 1: __get_data_asm(val, "ld.b", ptr); break;		\
+	case 2: __get_data_asm(val, "ld.h", ptr); break;		\
+	case 4: __get_data_asm(val, "ld.w", ptr); break;		\
+	case 8: __GET_DW(val, "ld.d", ptr); break;			\
+	default: BUILD_BUG(); break;					\
+	}								\
+} while (0)
+
+#define __get_kernel_common(val, size, ptr) __get_user_common(val, size, ptr)
+
+#define __get_data_asm(val, insn, addr)					\
+{									\
+	long __gu_tmp;							\
+									\
+	__asm__ __volatile__(						\
+	"1:	" insn "	%1, %3				\n"	\
+	"2:							\n"	\
+	"	.section .fixup,\"ax\"				\n"	\
+	"3:	li.w	%0, %4					\n"	\
+	"	or	%1, $r0, $r0				\n"	\
+	"	b	2b					\n"	\
+	"	.previous					\n"	\
+	"	.section __ex_table,\"a\"			\n"	\
+	"	"__UA_ADDR "\t1b, 3b				\n"	\
+	"	.previous					\n"	\
+	: "=r" (__gu_err), "=r" (__gu_tmp)				\
+	: "0" (0), "o" (__m(addr)), "i" (-EFAULT));			\
+									\
+	(val) = (__typeof__(*(addr))) __gu_tmp;				\
+}
+
+/*
+ * Get a long long 64 using 32 bit registers.
+ */
+#define __get_data_asm_ll32(val, insn, addr)				\
+{									\
+	union {								\
+		unsigned long long	l;				\
+		__typeof__(*(addr))	t;				\
+	} __gu_tmp;							\
+									\
+	__asm__ __volatile__(						\
+	"1:	ld.w	%1, (%3)				\n"	\
+	"2:	ld.w	%D1, 4(%3)				\n"	\
+	"3:							\n"	\
+	"	.section	.fixup,\"ax\"			\n"	\
+	"4:	li.w	%0, %4					\n"	\
+	"	slli.d	%1, $r0, 0				\n"	\
+	"	slli.d	%D1, $r0, 0				\n"	\
+	"	b	3b					\n"	\
+	"	.previous					\n"	\
+	"	.section	__ex_table,\"a\"		\n"	\
+	"	" __UA_ADDR "	1b, 4b				\n"	\
+	"	" __UA_ADDR "	2b, 4b				\n"	\
+	"	.previous					\n"	\
+	: "=r" (__gu_err), "=&r" (__gu_tmp.l)				\
+	: "0" (0), "r" (addr), "i" (-EFAULT));				\
+									\
+	(val) = __gu_tmp.t;						\
+}
+
+#ifdef CONFIG_32BIT
+#define __PUT_DW(insn, ptr) __put_data_asm_ll32(insn, ptr)
+#endif
+#ifdef CONFIG_64BIT
+#define __PUT_DW(insn, ptr) __put_data_asm(insn, ptr)
+#endif
+
+#define __put_user_common(ptr, size)					\
+do {									\
+	switch (size) {							\
+	case 1: __put_data_asm("st.b", ptr); break;			\
+	case 2: __put_data_asm("st.h", ptr); break;			\
+	case 4: __put_data_asm("st.w", ptr); break;			\
+	case 8: __PUT_DW("st.d", ptr); break;				\
+	default: BUILD_BUG(); break;					\
+	}								\
+} while (0)
+
+#define __put_kernel_common(ptr, size) __put_user_common(ptr, size)
+
+#define __put_data_asm(insn, ptr)					\
+{									\
+	__asm__ __volatile__(						\
+	"1:	" insn "	%z2, %3		# __put_user_asm\n"	\
+	"2:							\n"	\
+	"	.section	.fixup,\"ax\"			\n"	\
+	"3:	li.w	%0, %4					\n"	\
+	"	b	2b					\n"	\
+	"	.previous					\n"	\
+	"	.section	__ex_table,\"a\"		\n"	\
+	"	" __UA_ADDR "	1b, 3b				\n"	\
+	"	.previous					\n"	\
+	: "=r" (__pu_err)						\
+	: "0" (0), "Jr" (__pu_val), "o" (__m(ptr)),			\
+	  "i" (-EFAULT));						\
+}
+
+#define __put_data_asm_ll32(insn, ptr)					\
+{									\
+	__asm__ __volatile__(						\
+	"1:	st.w	%2, (%3)	# __put_user_asm_ll32	\n"	\
+	"2:	st.w	%D2, 4(%3)				\n"	\
+	"3:							\n"	\
+	"	.section	.fixup,\"ax\"			\n"	\
+	"4:	li.w	%0, %4					\n"	\
+	"	b	3b					\n"	\
+	"	.previous					\n"	\
+	"	.section	__ex_table,\"a\"		\n"	\
+	"	" __UA_ADDR "	1b, 4b				\n"	\
+	"	" __UA_ADDR "	2b, 4b				\n"	\
+	"	.previous"						\
+	: "=r" (__pu_err)						\
+	: "0" (0), "r" (__pu_val), "r" (ptr),				\
+	  "i" (-EFAULT));						\
+}
+
+#define HAVE_GET_KERNEL_NOFAULT
+
+#define __get_kernel_nofault(dst, src, type, err_label)			\
+do {									\
+	int __gu_err;							\
+									\
+	__get_kernel_common(*((type *)(dst)), sizeof(type),		\
+			    (__force type *)(src));			\
+	if (unlikely(__gu_err))						\
+		goto err_label;						\
+} while (0)
+
+#define __put_kernel_nofault(dst, src, type, err_label)			\
+do {									\
+	type __pu_val;					\
+	int __pu_err = 0;						\
+									\
+	__pu_val = *(__force type *)(src);				\
+	__put_kernel_common(((type *)(dst)), sizeof(type));		\
+	if (unlikely(__pu_err))						\
+		goto err_label;						\
+} while (0)
+
+extern __kernel_size_t __copy_user(void *to, const void *from, __kernel_size_t n);
+
+static inline unsigned long
+raw_copy_from_user(void *to, const void __user *from, unsigned long n)
+{
+	return __copy_user(to, from, n);
+}
+
+static inline unsigned long
+raw_copy_to_user(void __user *to, const void *from, unsigned long n)
+{
+	return __copy_user(to, from, n);
+}
+
+#define INLINE_COPY_FROM_USER
+#define INLINE_COPY_TO_USER
+
+static inline unsigned long
+raw_copy_in_user(void __user *to, const void __user *from, unsigned long n)
+{
+	return __copy_user(to, from, n);
+}
+
+/*
+ * __clear_user: - Zero a block of memory in user space, with less checking.
+ * @addr: Destination address, in user space.
+ * @size: Number of bytes to zero.
+ *
+ * Zero a block of memory in user space.  Caller must check
+ * the specified block with access_ok() before calling this function.
+ *
+ * Returns number of bytes that could not be cleared.
+ * On success, this will be zero.
+ */
+extern __kernel_size_t __clear_user(void __user *addr, __kernel_size_t size);
+
+#define clear_user(addr, n)						\
+({									\
+	void __user *__cl_addr = (addr);				\
+	unsigned long __cl_size = (n);					\
+	if (__cl_size && access_ok(__cl_addr, __cl_size))		\
+		__cl_size = __clear_user(__cl_addr, __cl_size);		\
+	__cl_size;							\
+})
+
+extern long __strncpy_from_user(char *to, const char __user *from, long len);
+
+/*
+ * strncpy_from_user: - Copy a NUL terminated string from userspace.
+ * @to:   Destination address, in kernel space.  This buffer must be at
+ *	  least @len bytes long.
+ * @from: Source address, in user space.
+ * @len:  Maximum number of bytes to copy, including the trailing NUL.
+ *
+ * Copies a NUL-terminated string from userspace to kernel space.
+ *
+ * On success, returns the length of the string (not including the trailing
+ * NUL).
+ *
+ * If access to userspace fails, returns -EFAULT (some data may have been
+ * copied).
+ *
+ * If @len is smaller than the length of the string, copies @len bytes
+ * and returns @len.
+ */
+static inline long
+strncpy_from_user(char *to, const char __user *from, long len)
+{
+	if (!access_ok(from, len))
+		return -EFAULT;
+
+	might_fault();
+	return __strncpy_from_user(to, from, len);
+}
+
+extern long __strnlen_user(const char __user *s, long n);
+
+/*
+ * strnlen_user: - Get the size of a string in user space.
+ * @s: The string to measure.
+ *
+ * Context: User context only. This function may sleep if pagefaults are
+ *          enabled.
+ *
+ * Get the size of a NUL-terminated string in user space.
+ *
+ * Returns the size of the string INCLUDING the terminating NUL.
+ * On exception, returns 0.
+ * If the string is too long, returns a value greater than @n.
+ */
+static inline long strnlen_user(const char __user *s, long n)
+{
+	if (!access_ok(s, 1))
+		return 0;
+
+	might_fault();
+	return __strnlen_user(s, n);
+}
+
+#endif /* _ASM_UACCESS_H */
diff --git a/arch/loongarch/include/asm/unistd.h b/arch/loongarch/include/asm/unistd.h
new file mode 100644
index 000000000000..25b6f2d0b4e5
--- /dev/null
+++ b/arch/loongarch/include/asm/unistd.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Author: Hanlu Li <lihanlu@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <uapi/asm/unistd.h>
+
+#define NR_syscalls (__NR_syscalls)
diff --git a/arch/loongarch/include/uapi/asm/unistd.h b/arch/loongarch/include/uapi/asm/unistd.h
new file mode 100644
index 000000000000..6c194d740ed0
--- /dev/null
+++ b/arch/loongarch/include/uapi/asm/unistd.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#define __ARCH_WANT_NEW_STAT
+#define __ARCH_WANT_SYS_CLONE
+#define __ARCH_WANT_SYS_CLONE3
+#define __ARCH_WANT_SET_GET_RLIMIT
+
+#include <asm-generic/unistd.h>
diff --git a/arch/loongarch/kernel/scall64.S b/arch/loongarch/kernel/scall64.S
new file mode 100644
index 000000000000..ec4cf68d7feb
--- /dev/null
+++ b/arch/loongarch/kernel/scall64.S
@@ -0,0 +1,126 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/errno.h>
+#include <asm/asm.h>
+#include <asm/asmmacro.h>
+#include <asm/irqflags.h>
+#include <asm/loongarchregs.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+#include <asm/asm-offsets.h>
+#include <asm/thread_info.h>
+#include <asm/unistd.h>
+
+	.text
+	.cfi_sections	.debug_frame
+	.align	5
+SYM_FUNC_START(handle_sys)
+	csrrd	t0, PERCPU_BASE_KS
+	la.abs	t1, kernelsp
+	add.d	t1, t1, t0
+	or	t2, sp, zero
+	ld.d	sp, t1, 0
+
+	addi.d	sp, sp, -PT_SIZE
+	cfi_st	t2, PT_R3
+	cfi_rel_offset  sp, PT_R3
+	csrrd	t2, LOONGARCH_CSR_PRMD
+	st.d	t2, sp, PT_PRMD
+	csrrd	t2, LOONGARCH_CSR_CRMD
+	st.d	t2, sp, PT_CRMD
+	csrrd	t2, LOONGARCH_CSR_ECFG
+	st.d	t2, sp, PT_ECFG
+	csrrd	t2, LOONGARCH_CSR_EUEN
+	st.d	t2, sp, PT_EUEN
+	cfi_st	ra, PT_R1
+	cfi_st	a0, PT_R4
+	cfi_st	a1, PT_R5
+	cfi_st	a2, PT_R6
+	cfi_st	a3, PT_R7
+	cfi_st	a4, PT_R8
+	cfi_st	a5, PT_R9
+	cfi_st	a6, PT_R10
+	cfi_st	a7, PT_R11
+	csrrd	ra, LOONGARCH_CSR_EPC
+	st.d	ra, sp, PT_EPC
+
+	cfi_rel_offset ra, PT_EPC
+
+	cfi_st	tp, PT_R2
+	cfi_st	x0, PT_R21
+	cfi_st	fp, PT_R22
+
+	li.d	tp, ~_THREAD_MASK
+	and	tp, tp, sp
+
+	STI
+
+	/* save the initial A0 value (needed in signal handlers) */
+	st.d	a0, sp, PT_ORIG_A0
+	ld.d	t1, sp, PT_EPC		# skip syscall on return
+	addi.d	t1, t1, 4		# skip to next instruction
+	st.d	t1, sp, PT_EPC
+
+	li.d	t1, _TIF_WORK_SYSCALL_ENTRY
+	LONG_L	t0, tp, TI_FLAGS	# syscall tracing enabled?
+	and	t0, t1, t0
+	bnez	t0, syscall_trace_entry
+
+syscall_common:
+	/* Check to make sure we don't jump to a bogus syscall number. */
+	li.w	t0, __NR_syscalls
+	sub.d	t2, a7, t0
+	bgez	t2, illegal_syscall
+
+	/* Syscall number held in a7 */
+	slli.d	t0, a7, 3		# offset into table
+	la	t2, sys_call_table
+	add.d	t0, t2, t0
+	ld.d	t2, t0, 0		#syscall routine
+	beqz    t2, illegal_syscall
+
+	jalr	t2			# Do The Real Thing (TM)
+
+	ld.d	t1, sp, PT_R11		# syscall number
+	addi.d	t1, t1, 1		# +1 for handle_signal
+	st.d	t1, sp, PT_R0		# save it for syscall restarting
+	st.d	v0, sp, PT_R4		# result
+
+la64_syscall_exit:
+	b	syscall_exit_partial
+
+/* ------------------------------------------------------------------------ */
+
+syscall_trace_entry:
+	SAVE_STATIC
+	move	a0, sp
+	move	a1, a7
+	bl	syscall_trace_enter
+
+	blt	v0, zero, 1f			# seccomp failed? Skip syscall
+
+	RESTORE_STATIC
+	ld.d	a0, sp, PT_R4		# Restore argument registers
+	ld.d	a1, sp, PT_R5
+	ld.d	a2, sp, PT_R6
+	ld.d	a3, sp, PT_R7
+	ld.d	a4, sp, PT_R8
+	ld.d	a5, sp, PT_R9
+	ld.d	a6, sp, PT_R10
+	ld.d	a7, sp, PT_R11		# Restore syscall (maybe modified)
+	b	syscall_common
+
+1:	b	syscall_exit
+
+	/*
+	 * The system call does not exist in this kernel
+	 */
+
+illegal_syscall:
+	/* This also isn't a valid syscall, throw an error.  */
+	li.w	v0, -ENOSYS			# error
+	st.d	v0, sp, PT_R4
+	b	la64_syscall_exit
+SYM_FUNC_END(handle_sys)
diff --git a/arch/loongarch/kernel/syscall.c b/arch/loongarch/kernel/syscall.c
new file mode 100644
index 000000000000..71dd110bf31c
--- /dev/null
+++ b/arch/loongarch/kernel/syscall.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Author: Hanlu Li <lihanlu@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/capability.h>
+#include <linux/errno.h>
+#include <linux/linkage.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/syscalls.h>
+#include <linux/file.h>
+#include <linux/utsname.h>
+#include <linux/unistd.h>
+#include <linux/compiler.h>
+#include <linux/sched/task_stack.h>
+
+#include <asm/asm.h>
+#include <asm/cacheflush.h>
+#include <asm/asm-offsets.h>
+#include <asm/signal.h>
+#include <asm/shmparam.h>
+#include <asm/switch_to.h>
+#include <asm-generic/syscalls.h>
+
+#undef __SYSCALL
+#define __SYSCALL(nr, call)	[nr] = (call),
+
+#define __str2(x) #x
+#define __str(x) __str2(x)
+#define save_static(symbol)                            \
+__asm__(                                               \
+       ".text\n\t"                                     \
+       ".globl\t__" #symbol "\n\t"                     \
+       ".align\t2\n\t"                                 \
+       ".type\t__" #symbol ", @function\n\t"           \
+	"__"#symbol":\n\t"                              \
+       "st.d\t$r23,$r3,"__str(PT_R23)"\n\t"            \
+       "st.d\t$r24,$r3,"__str(PT_R24)"\n\t"            \
+       "st.d\t$r25,$r3,"__str(PT_R25)"\n\t"            \
+       "st.d\t$r26,$r3,"__str(PT_R26)"\n\t"            \
+       "st.d\t$r27,$r3,"__str(PT_R27)"\n\t"            \
+       "st.d\t$r28,$r3,"__str(PT_R28)"\n\t"            \
+       "st.d\t$r29,$r3,"__str(PT_R29)"\n\t"            \
+       "st.d\t$r30,$r3,"__str(PT_R30)"\n\t"            \
+       "st.d\t$r31,$r3,"__str(PT_R31)"\n\t"            \
+       "b\t" #symbol "\n\t"                            \
+       ".size\t__" #symbol",. - __" #symbol)
+
+save_static(sys_clone);
+save_static(sys_clone3);
+
+asmlinkage void __sys_clone(void);
+asmlinkage void __sys_clone3(void);
+
+SYSCALL_DEFINE6(mmap, unsigned long, addr, unsigned long, len,
+	unsigned long, prot, unsigned long, flags, unsigned long,
+	fd, off_t, offset)
+{
+	if (offset & ~PAGE_MASK)
+		return -EINVAL;
+	return ksys_mmap_pgoff(addr, len, prot, flags, fd,
+			       offset >> PAGE_SHIFT);
+}
+
+SYSCALL_DEFINE6(mmap2, unsigned long, addr, unsigned long, len,
+	unsigned long, prot, unsigned long, flags, unsigned long, fd,
+	unsigned long, pgoff)
+{
+	if (pgoff & (~PAGE_MASK >> 12))
+		return -EINVAL;
+
+	return ksys_mmap_pgoff(addr, len, prot, flags, fd,
+			       pgoff >> (PAGE_SHIFT - 12));
+}
+
+void *sys_call_table[__NR_syscalls] = {
+	[0 ... __NR_syscalls - 1] = sys_ni_syscall,
+#include <asm/unistd.h>
+	__SYSCALL(__NR_clone, __sys_clone)
+	__SYSCALL(__NR_clone3, __sys_clone3)
+};
-- 
2.27.0


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

* [PATCH 10/19] LoongArch: Add signal handling support
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (7 preceding siblings ...)
  2021-07-06  4:18 ` [PATCH 09/19] LoongArch: Add system call support Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06 10:17   ` Arnd Bergmann
  2021-08-26 16:43   ` Xi Ruoyao
  2021-07-06  4:18 ` [PATCH 11/19] LoongArch: Add elf and module support Huacai Chen
                   ` (11 subsequent siblings)
  20 siblings, 2 replies; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

This patch adds signal handling support for LoongArch.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 arch/loongarch/include/asm/sigcontext.h      |  10 +
 arch/loongarch/include/asm/signal.h          |  16 +
 arch/loongarch/include/uapi/asm/sigcontext.h |  40 ++
 arch/loongarch/include/uapi/asm/siginfo.h    |  19 +
 arch/loongarch/include/uapi/asm/signal.h     | 115 ++++
 arch/loongarch/include/uapi/asm/ucontext.h   |  46 ++
 arch/loongarch/kernel/signal-common.h        |  52 ++
 arch/loongarch/kernel/signal.c               | 583 +++++++++++++++++++
 8 files changed, 881 insertions(+)
 create mode 100644 arch/loongarch/include/asm/sigcontext.h
 create mode 100644 arch/loongarch/include/asm/signal.h
 create mode 100644 arch/loongarch/include/uapi/asm/sigcontext.h
 create mode 100644 arch/loongarch/include/uapi/asm/siginfo.h
 create mode 100644 arch/loongarch/include/uapi/asm/signal.h
 create mode 100644 arch/loongarch/include/uapi/asm/ucontext.h
 create mode 100644 arch/loongarch/kernel/signal-common.h
 create mode 100644 arch/loongarch/kernel/signal.c

diff --git a/arch/loongarch/include/asm/sigcontext.h b/arch/loongarch/include/asm/sigcontext.h
new file mode 100644
index 000000000000..c8d46213ad98
--- /dev/null
+++ b/arch/loongarch/include/asm/sigcontext.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_SIGCONTEXT_H
+#define _ASM_SIGCONTEXT_H
+
+#include <uapi/asm/sigcontext.h>
+
+#endif /* _ASM_SIGCONTEXT_H */
diff --git a/arch/loongarch/include/asm/signal.h b/arch/loongarch/include/asm/signal.h
new file mode 100644
index 000000000000..530805184ce5
--- /dev/null
+++ b/arch/loongarch/include/asm/signal.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_SIGNAL_H
+#define _ASM_SIGNAL_H
+
+#include <uapi/asm/signal.h>
+
+#ifndef __ASSEMBLY__
+
+#include <asm/sigcontext.h>
+#undef __HAVE_ARCH_SIG_BITOPS
+
+#endif /* __ASSEMBLY__ */
+#endif /* _ASM_SIGNAL_H */
diff --git a/arch/loongarch/include/uapi/asm/sigcontext.h b/arch/loongarch/include/uapi/asm/sigcontext.h
new file mode 100644
index 000000000000..da0e5bac2d80
--- /dev/null
+++ b/arch/loongarch/include/uapi/asm/sigcontext.h
@@ -0,0 +1,40 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Author: Hanlu Li <lihanlu@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _UAPI_ASM_SIGCONTEXT_H
+#define _UAPI_ASM_SIGCONTEXT_H
+
+#include <linux/types.h>
+#include <asm/processor.h>
+
+/* scalar FP context was used */
+#define USED_FP			(1 << 0)
+
+/* extended context was used, see struct extcontext for details */
+#define USED_EXTCONTEXT		(1 << 1)
+
+#include <linux/posix_types.h>
+/*
+ * Keep this struct definition in sync with the sigcontext fragment
+ * in arch/loongarch/kernel/asm-offsets.c
+ *
+ */
+struct sigcontext {
+	__u64	sc_pc;
+	__u64	sc_regs[32];
+	__u32	sc_flags;
+
+	__u32	sc_fcsr;
+	__u32	sc_vcsr;
+	__u64	sc_fcc;
+	__u64	sc_scr[4];
+
+	union fpureg sc_fpregs[32] FPU_ALIGN;
+	__u8	sc_reserved[4096] __attribute__((__aligned__(16)));
+};
+
+#endif /* _UAPI_ASM_SIGCONTEXT_H */
diff --git a/arch/loongarch/include/uapi/asm/siginfo.h b/arch/loongarch/include/uapi/asm/siginfo.h
new file mode 100644
index 000000000000..11287ad78138
--- /dev/null
+++ b/arch/loongarch/include/uapi/asm/siginfo.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Author: Hanlu Li <lihanlu@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _UAPI_ASM_SIGINFO_H
+#define _UAPI_ASM_SIGINFO_H
+
+#if _LOONGARCH_SZLONG == 32
+#define __ARCH_SI_PREAMBLE_SIZE (3 * sizeof(int))
+#else
+#define __ARCH_SI_PREAMBLE_SIZE (4 * sizeof(int))
+#endif
+
+#include <asm-generic/siginfo.h>
+
+#endif /* _UAPI_ASM_SIGINFO_H */
diff --git a/arch/loongarch/include/uapi/asm/signal.h b/arch/loongarch/include/uapi/asm/signal.h
new file mode 100644
index 000000000000..2254aef35402
--- /dev/null
+++ b/arch/loongarch/include/uapi/asm/signal.h
@@ -0,0 +1,115 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Author: Hanlu Li <lihanlu@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _UAPI_ASM_SIGNAL_H
+#define _UAPI_ASM_SIGNAL_H
+
+#include <linux/types.h>
+
+#ifndef _NSIG
+#define _NSIG		128
+#endif
+#define _NSIG_BPW	__BITS_PER_LONG
+#define _NSIG_WORDS	(_NSIG / _NSIG_BPW)
+
+#define SIGHUP		 1
+#define SIGINT		 2
+#define SIGQUIT		 3
+#define SIGILL		 4
+#define SIGTRAP		 5
+#define SIGABRT		 6
+#define SIGIOT		 6
+#define SIGBUS		 7
+#define SIGFPE		 8
+#define SIGKILL		 9
+#define SIGUSR1		10
+#define SIGSEGV		11
+#define SIGUSR2		12
+#define SIGPIPE		13
+#define SIGALRM		14
+#define SIGTERM		15
+#define SIGSTKFLT	16
+#define SIGCHLD		17
+#define SIGCONT		18
+#define SIGSTOP		19
+#define SIGTSTP		20
+#define SIGTTIN		21
+#define SIGTTOU		22
+#define SIGURG		23
+#define SIGXCPU		24
+#define SIGXFSZ		25
+#define SIGVTALRM	26
+#define SIGPROF		27
+#define SIGWINCH	28
+#define SIGIO		29
+#define SIGPOLL		SIGIO
+#define SIGPWR		30
+#define SIGSYS		31
+#define	SIGUNUSED	31
+
+/* These should not be considered constants from userland.  */
+#define SIGRTMIN	32
+#ifndef SIGRTMAX
+#define SIGRTMAX	_NSIG
+#endif
+
+/*
+ * SA_FLAGS values:
+ *
+ * SA_ONSTACK indicates that a registered stack_t will be used.
+ * SA_RESTART flag to get restarting signals (which were the default long ago)
+ * SA_NOCLDSTOP flag to turn off SIGCHLD when children stop.
+ * SA_RESETHAND clears the handler when the signal is delivered.
+ * SA_NOCLDWAIT flag on SIGCHLD to inhibit zombies.
+ * SA_NODEFER prevents the current signal from being masked in the handler.
+ *
+ * SA_ONESHOT and SA_NOMASK are the historical Linux names for the Single
+ * Unix names RESETHAND and NODEFER respectively.
+ */
+#define SA_NOCLDSTOP	0x00000001
+#define SA_NOCLDWAIT	0x00000002
+#define SA_SIGINFO	0x00000004
+#define SA_ONSTACK	0x08000000
+#define SA_RESTART	0x10000000
+#define SA_NODEFER	0x40000000
+#define SA_RESETHAND	0x80000000
+
+#define SA_NOMASK	SA_NODEFER
+#define SA_ONESHOT	SA_RESETHAND
+
+#if !defined MINSIGSTKSZ || !defined SIGSTKSZ
+#define MINSIGSTKSZ	2048
+#define SIGSTKSZ	8192
+#endif
+
+#ifndef __ASSEMBLY__
+typedef struct {
+	unsigned long sig[_NSIG_WORDS];
+} sigset_t;
+
+/* not actually used, but required for linux/syscalls.h */
+typedef unsigned long old_sigset_t;
+
+#include <asm-generic/signal-defs.h>
+
+#ifndef __KERNEL__
+struct sigaction {
+	__sighandler_t sa_handler;
+	unsigned long sa_flags;
+	sigset_t sa_mask;		/* mask last for extensibility */
+};
+#endif
+
+typedef struct sigaltstack {
+	void __user *ss_sp;
+	int ss_flags;
+	size_t ss_size;
+} stack_t;
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _UAPI_ASM_SIGNAL_H */
diff --git a/arch/loongarch/include/uapi/asm/ucontext.h b/arch/loongarch/include/uapi/asm/ucontext.h
new file mode 100644
index 000000000000..e3814b5fbce8
--- /dev/null
+++ b/arch/loongarch/include/uapi/asm/ucontext.h
@@ -0,0 +1,46 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef __LOONGARCH_UAPI_ASM_UCONTEXT_H
+#define __LOONGARCH_UAPI_ASM_UCONTEXT_H
+
+/**
+ * struct extcontext - extended context header structure
+ * @magic:	magic value identifying the type of extended context
+ * @size:	the size in bytes of the enclosing structure
+ *
+ * Extended context structures provide context which does not fit within struct
+ * sigcontext. They are placed sequentially in memory at the end of struct
+ * ucontext and struct sigframe, with each extended context structure beginning
+ * with a header defined by this struct. The type of context represented is
+ * indicated by the magic field. Userland may check each extended context
+ * structure against magic values that it recognises. The size field allows any
+ * unrecognised context to be skipped, allowing for future expansion. The end
+ * of the extended context data is indicated by the magic value
+ * END_EXTCONTEXT_MAGIC.
+ */
+struct extcontext {
+	unsigned int		magic;
+	unsigned int		size;
+};
+
+/**
+ * struct ucontext - user context structure
+ * @uc_flags:
+ * @uc_link:
+ * @uc_stack:
+ * @uc_mcontext:	holds basic processor state
+ * @uc_sigmask:
+ * @uc_extcontext:	holds extended processor state
+ */
+struct ucontext {
+	/* Historic fields matching asm-generic */
+	unsigned long		uc_flags;
+	struct ucontext		*uc_link;
+	stack_t			uc_stack;
+	struct sigcontext	uc_mcontext;
+	sigset_t		uc_sigmask;
+
+	/* Extended context structures may follow ucontext */
+	unsigned long long	uc_extcontext[0];
+};
+
+#endif /* __LOONGARCH_UAPI_ASM_UCONTEXT_H */
diff --git a/arch/loongarch/kernel/signal-common.h b/arch/loongarch/kernel/signal-common.h
new file mode 100644
index 000000000000..c0ce90f988e5
--- /dev/null
+++ b/arch/loongarch/kernel/signal-common.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Author: Hanlu Li <lihanlu@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef __SIGNAL_COMMON_H
+#define __SIGNAL_COMMON_H
+
+/* #define DEBUG_SIG */
+
+#ifdef DEBUG_SIG
+#  define DEBUGP(fmt, args...) printk("%s: " fmt, __func__, ##args)
+#else
+#  define DEBUGP(fmt, args...)
+#endif
+
+/*
+ * Determine which stack to use..
+ */
+extern void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs,
+				 size_t frame_size);
+/* Check and clear pending FPU exceptions in saved CSR */
+extern int fpcsr_pending(unsigned int __user *fpcsr);
+
+/* Make sure we will not lose FPU ownership */
+#define lock_fpu_owner()	({ preempt_disable(); pagefault_disable(); })
+#define unlock_fpu_owner()	({ pagefault_enable(); preempt_enable(); })
+
+/* Assembly functions to move context to/from the FPU */
+extern asmlinkage int
+_save_fp_context(void __user *fpregs, void __user *fcc, void __user *csr);
+extern asmlinkage int
+_restore_fp_context(void __user *fpregs, void __user *fcc, void __user *csr);
+extern asmlinkage int
+_save_lsx_context(void __user *fpregs, void __user *fcc, void __user *fcsr,
+	void __user *vcsr);
+extern asmlinkage int
+_restore_lsx_context(void __user *fpregs, void __user *fcc, void __user *fcsr,
+	void __user *vcsr);
+extern asmlinkage int
+_save_lasx_context(void __user *fpregs, void __user *fcc, void __user *fcsr,
+	void __user *vcsr);
+extern asmlinkage int
+_restore_lasx_context(void __user *fpregs, void __user *fcc, void __user *fcsr,
+	void __user *vcsr);
+extern asmlinkage int _save_lsx_all_upper(void __user *buf);
+extern asmlinkage int _restore_lsx_all_upper(void __user *buf);
+
+#endif	/* __SIGNAL_COMMON_H */
diff --git a/arch/loongarch/kernel/signal.c b/arch/loongarch/kernel/signal.c
new file mode 100644
index 000000000000..b722b9e7c380
--- /dev/null
+++ b/arch/loongarch/kernel/signal.c
@@ -0,0 +1,583 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Author: Hanlu Li <lihanlu@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/audit.h>
+#include <linux/cache.h>
+#include <linux/context_tracking.h>
+#include <linux/irqflags.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/personality.h>
+#include <linux/smp.h>
+#include <linux/kernel.h>
+#include <linux/signal.h>
+#include <linux/errno.h>
+#include <linux/wait.h>
+#include <linux/ptrace.h>
+#include <linux/unistd.h>
+#include <linux/uprobes.h>
+#include <linux/compiler.h>
+#include <linux/syscalls.h>
+#include <linux/uaccess.h>
+#include <linux/tracehook.h>
+
+#include <asm/abi.h>
+#include <asm/asm.h>
+#include <asm/cacheflush.h>
+#include <asm/fpu.h>
+#include <asm/ucontext.h>
+#include <asm/cpu-features.h>
+#include <asm/inst.h>
+
+#include "signal-common.h"
+
+static int (*save_fp_context)(void __user *sc);
+static int (*restore_fp_context)(void __user *sc);
+
+struct sigframe {
+	u32 sf_ass[4];		/* argument save space for o32 */
+	u32 sf_pad[2];		/* Was: signal trampoline */
+
+	/* Matches struct ucontext from its uc_mcontext field onwards */
+	struct sigcontext sf_sc;
+	sigset_t sf_mask;
+	unsigned long long sf_extcontext[0];
+};
+
+struct rt_sigframe {
+	u32 rs_ass[4];		/* argument save space for o32 */
+	u32 rs_pad[2];		/* Was: signal trampoline */
+	struct siginfo rs_info;
+	struct ucontext rs_uc;
+};
+
+/*
+ * Thread saved context copy to/from a signal context presumed to be on the
+ * user stack, and therefore accessed with appropriate macros from uaccess.h.
+ */
+static int copy_fp_to_sigcontext(void __user *sc)
+{
+	struct loongarch_abi *abi = current->thread.abi;
+	uint64_t __user *fpregs = sc + abi->off_sc_fpregs;
+	uint64_t __user *fcc = sc + abi->off_sc_fcc;
+	uint32_t __user *csr = sc + abi->off_sc_fcsr;
+	int i;
+	int err = 0;
+	int inc = 1;
+
+	for (i = 0; i < NUM_FPU_REGS; i += inc) {
+		err |=
+		    __put_user(get_fpr64(&current->thread.fpu.fpr[i], 0),
+			       &fpregs[4*i]);
+	}
+	err |= __put_user(current->thread.fpu.fcsr, csr);
+	err |= __put_user(current->thread.fpu.fcc, fcc);
+
+	return err;
+}
+
+static int copy_fp_from_sigcontext(void __user *sc)
+{
+	struct loongarch_abi *abi = current->thread.abi;
+	uint64_t __user *fpregs = sc + abi->off_sc_fpregs;
+	uint64_t __user *fcc = sc + abi->off_sc_fcc;
+	uint32_t __user *csr = sc + abi->off_sc_fcsr;
+	int i;
+	int err = 0;
+	int inc = 1;
+	u64 fpr_val;
+
+	for (i = 0; i < NUM_FPU_REGS; i += inc) {
+		err |= __get_user(fpr_val, &fpregs[4*i]);
+		set_fpr64(&current->thread.fpu.fpr[i], 0, fpr_val);
+	}
+	err |= __get_user(current->thread.fpu.fcsr, csr);
+	err |= __get_user(current->thread.fpu.fcc, fcc);
+
+	return err;
+}
+
+/*
+ * Wrappers for the assembly _{save,restore}_fp_context functions.
+ */
+static int save_hw_fp_context(void __user *sc)
+{
+	struct loongarch_abi *abi = current->thread.abi;
+	uint64_t __user *fpregs = sc + abi->off_sc_fpregs;
+	uint64_t __user *fcc = sc + abi->off_sc_fcc;
+	uint32_t __user *fcsr = sc + abi->off_sc_fcsr;
+
+	return _save_fp_context(fpregs, fcc, fcsr);
+}
+
+static int restore_hw_fp_context(void __user *sc)
+{
+	struct loongarch_abi *abi = current->thread.abi;
+	uint64_t __user *fpregs = sc + abi->off_sc_fpregs;
+	uint64_t __user *fcc = sc + abi->off_sc_fcc;
+	uint32_t __user *csr = sc + abi->off_sc_fcsr;
+
+	return _restore_fp_context(fpregs, fcc, csr);
+}
+
+/*
+ * Extended context handling.
+ */
+
+static inline void __user *sc_to_extcontext(void __user *sc)
+{
+	struct ucontext __user *uc;
+
+	/*
+	 * We can just pretend the sigcontext is always embedded in a struct
+	 * ucontext here, because the offset from sigcontext to extended
+	 * context is the same in the struct sigframe case.
+	 */
+	uc = container_of(sc, struct ucontext, uc_mcontext);
+	return &uc->uc_extcontext;
+}
+
+static int save_extcontext(void __user *buf)
+{
+	return 0;
+}
+
+static int restore_extcontext(void __user *buf)
+{
+	return 0;
+}
+
+/*
+ * Helper routines
+ */
+int protected_save_fp_context(void __user *sc)
+{
+	int err = 0;
+	unsigned int used, ext_sz;
+	struct loongarch_abi *abi = current->thread.abi;
+	uint64_t __user *fpregs = sc + abi->off_sc_fpregs;
+	uint32_t __user *fcc = sc + abi->off_sc_fcsr;
+	uint32_t __user *fcsr = sc + abi->off_sc_fcsr;
+	uint32_t __user *vcsr = sc + abi->off_sc_vcsr;
+	uint32_t __user *flags = sc + abi->off_sc_flags;
+
+	used = used_math() ? USED_FP : 0;
+	if (!used)
+		goto fp_done;
+
+	while (1) {
+		lock_fpu_owner();
+		if (is_fpu_owner())
+			err = save_fp_context(sc);
+		else
+			err |= copy_fp_to_sigcontext(sc);
+		unlock_fpu_owner();
+		if (likely(!err))
+			break;
+		/* touch the sigcontext and try again */
+		err = __put_user(0, &fpregs[0]) |
+			__put_user(0, &fpregs[31*4]) |
+			__put_user(0, fcc) |
+			__put_user(0, fcsr) |
+			__put_user(0, vcsr);
+		if (err)
+			return err;	/* really bad sigcontext */
+	}
+
+fp_done:
+	ext_sz = err = save_extcontext(sc_to_extcontext(sc));
+	if (err < 0)
+		return err;
+	used |= ext_sz ? USED_EXTCONTEXT : 0;
+
+	return __put_user(used, flags);
+}
+
+int protected_restore_fp_context(void __user *sc)
+{
+	unsigned int used;
+	int err = 0, sig = 0, tmp __maybe_unused;
+	struct loongarch_abi *abi = current->thread.abi;
+	uint64_t __user *fpregs = sc + abi->off_sc_fpregs;
+	uint32_t __user *fcsr = sc + abi->off_sc_fcsr;
+	uint32_t __user *fcc = sc + abi->off_sc_fcsr;
+	uint32_t __user *vcsr = sc + abi->off_sc_vcsr;
+	uint32_t __user *flags = sc + abi->off_sc_flags;
+
+	err = __get_user(used, flags);
+	conditional_used_math(used & USED_FP);
+
+	/*
+	 * The signal handler may have used FPU; give it up if the program
+	 * doesn't want it following sigreturn.
+	 */
+	if (err || !(used & USED_FP))
+		lose_fpu(0);
+
+	if (err)
+		return err;
+
+	if (!(used & USED_FP))
+		goto fp_done;
+
+	err = sig = fpcsr_pending(fcsr);
+	if (err < 0)
+		return err;
+
+	while (1) {
+		lock_fpu_owner();
+		if (is_fpu_owner())
+			err = restore_fp_context(sc);
+		else
+			err |= copy_fp_from_sigcontext(sc);
+		unlock_fpu_owner();
+		if (likely(!err))
+			break;
+		/* touch the sigcontext and try again */
+		err = __get_user(tmp, &fpregs[0]) |
+			__get_user(tmp, &fpregs[31*4]) |
+			__get_user(tmp, fcc) |
+			__get_user(tmp, fcsr) |
+			__get_user(tmp, vcsr);
+		if (err)
+			break;	/* really bad sigcontext */
+	}
+
+fp_done:
+	if (!err && (used & USED_EXTCONTEXT))
+		err = restore_extcontext(sc_to_extcontext(sc));
+
+	return err ?: sig;
+}
+
+int setup_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
+{
+	int err = 0;
+	int i;
+
+	err |= __put_user(regs->csr_epc, &sc->sc_pc);
+
+	err |= __put_user(0, &sc->sc_regs[0]);
+	for (i = 1; i < 32; i++)
+		err |= __put_user(regs->regs[i], &sc->sc_regs[i]);
+
+	/*
+	 * Save FPU state to signal context. Signal handler
+	 * will "inherit" current FPU state.
+	 */
+	err |= protected_save_fp_context(sc);
+
+	return err;
+}
+
+static size_t extcontext_max_size(void)
+{
+	size_t sz = 0;
+
+	/*
+	 * The assumption here is that between this point & the point at which
+	 * the extended context is saved the size of the context should only
+	 * ever be able to shrink (if the task is preempted), but never grow.
+	 * That is, what this function returns is an upper bound on the size of
+	 * the extended context for the current task at the current time.
+	 */
+
+	/* If any context is saved then we'll append the end marker */
+	if (sz)
+		sz += sizeof(((struct extcontext *)NULL)->magic);
+
+	return sz;
+}
+
+int fpcsr_pending(unsigned int __user *fpcsr)
+{
+	int err, sig = 0;
+	unsigned int csr, enabled;
+
+	err = __get_user(csr, fpcsr);
+	enabled = ((csr & FPU_CSR_ALL_E) << 24);
+	/*
+	 * If the signal handler set some FPU exceptions, clear it and
+	 * send SIGFPE.
+	 */
+	if (csr & enabled) {
+		csr &= ~enabled;
+		err |= __put_user(csr, fpcsr);
+		sig = SIGFPE;
+	}
+	return err ?: sig;
+}
+
+int restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *sc)
+{
+	int err = 0;
+	int i;
+
+	/* Always make any pending restarted system calls return -EINTR */
+	current->restart_block.fn = do_no_restart_syscall;
+
+	err |= __get_user(regs->csr_epc, &sc->sc_pc);
+
+	for (i = 1; i < 32; i++)
+		err |= __get_user(regs->regs[i], &sc->sc_regs[i]);
+
+	return err ?: protected_restore_fp_context(sc);
+}
+
+void __user *get_sigframe(struct ksignal *ksig, struct pt_regs *regs,
+			  size_t frame_size)
+{
+	unsigned long sp;
+
+	/* Leave space for potential extended context */
+	frame_size += extcontext_max_size();
+
+	/* Default to using normal stack */
+	sp = regs->regs[3];
+
+	/*
+	 * FPU emulator may have it's own trampoline active just
+	 * above the user stack, 16-bytes before the next lowest
+	 * 16 byte boundary.  Try to avoid trashing it.
+	 */
+	sp -= 32;
+
+	sp = sigsp(sp, ksig);
+
+	return (void __user *)((sp - frame_size) & ALMASK);
+}
+
+/*
+ * Atomically swap in the new signal mask, and wait for a signal.
+ */
+
+asmlinkage void sys_rt_sigreturn(void)
+{
+	struct rt_sigframe __user *frame;
+	struct pt_regs *regs;
+	sigset_t set;
+	int sig;
+
+	regs = current_pt_regs();
+	frame = (struct rt_sigframe __user *)regs->regs[3];
+	if (!access_ok(frame, sizeof(*frame)))
+		goto badframe;
+	if (__copy_from_user(&set, &frame->rs_uc.uc_sigmask, sizeof(set)))
+		goto badframe;
+
+	set_current_blocked(&set);
+
+	sig = restore_sigcontext(regs, &frame->rs_uc.uc_mcontext);
+	if (sig < 0)
+		goto badframe;
+	else if (sig)
+		force_sig(sig);
+
+	if (restore_altstack(&frame->rs_uc.uc_stack))
+		goto badframe;
+
+	/*
+	 * Don't let your children do this ...
+	 */
+	__asm__ __volatile__(
+		"or\t$sp, $zero, %0\n\t"
+		"b\tsyscall_exit"
+		: /* no outputs */
+		: "r" (regs));
+	/* Unreached */
+
+badframe:
+	force_sig(SIGSEGV);
+}
+
+static int setup_rt_frame(void *sig_return, struct ksignal *ksig,
+			  struct pt_regs *regs, sigset_t *set)
+{
+	struct rt_sigframe __user *frame;
+	int err = 0;
+
+	frame = get_sigframe(ksig, regs, sizeof(*frame));
+	if (!access_ok(frame, sizeof(*frame)))
+		return -EFAULT;
+
+	/* Create siginfo.  */
+	err |= copy_siginfo_to_user(&frame->rs_info, &ksig->info);
+
+	/* Create the ucontext.	 */
+	err |= __put_user(0, &frame->rs_uc.uc_flags);
+	err |= __put_user(NULL, &frame->rs_uc.uc_link);
+	err |= __save_altstack(&frame->rs_uc.uc_stack, regs->regs[3]);
+	err |= setup_sigcontext(regs, &frame->rs_uc.uc_mcontext);
+	err |= __copy_to_user(&frame->rs_uc.uc_sigmask, set, sizeof(*set));
+
+	if (err)
+		return -EFAULT;
+
+	/*
+	 * Arguments to signal handler:
+	 *
+	 *   a0 = signal number
+	 *   a1 = 0 (should be cause)
+	 *   a2 = pointer to ucontext
+	 *
+	 * $25 and c0_epc point to the signal handler, $29 points to
+	 * the struct rt_sigframe.
+	 */
+	regs->regs[4] = ksig->sig;
+	regs->regs[5] = (unsigned long) &frame->rs_info;
+	regs->regs[6] = (unsigned long) &frame->rs_uc;
+	regs->regs[3] = (unsigned long) frame;
+	regs->regs[1] = (unsigned long) sig_return;
+	regs->csr_epc = (unsigned long) ksig->ka.sa.sa_handler;
+
+	DEBUGP("SIG deliver (%s:%d): sp=0x%p pc=0x%lx ra=0x%lx\n",
+	       current->comm, current->pid,
+	       frame, regs->csr_epc, regs->regs[1]);
+
+	return 0;
+}
+
+struct loongarch_abi loongarch_abi = {
+	.setup_rt_frame = setup_rt_frame,
+	.restart	= __NR_restart_syscall,
+#ifdef CONFIG_32BIT
+	.audit_arch	= AUDIT_ARCH_LOONGARCH32,
+#else
+	.audit_arch	= AUDIT_ARCH_LOONGARCH64,
+#endif
+
+	.off_sc_fpregs = offsetof(struct sigcontext, sc_fpregs),
+	.off_sc_fcc = offsetof(struct sigcontext, sc_fcc),
+	.off_sc_fcsr = offsetof(struct sigcontext, sc_fcsr),
+	.off_sc_flags = offsetof(struct sigcontext, sc_flags),
+	.off_sc_vcsr = offsetof(struct sigcontext, sc_vcsr),
+
+	.vdso		= &vdso_image,
+};
+
+static void handle_signal(struct ksignal *ksig, struct pt_regs *regs)
+{
+	sigset_t *oldset = sigmask_to_save();
+	int ret;
+	struct loongarch_abi *abi = current->thread.abi;
+	void *vdso = current->mm->context.vdso;
+
+	/* Are we from a system call? */
+	if (regs->regs[0]) {
+		switch (regs->regs[4]) {
+		case -ERESTART_RESTARTBLOCK:
+		case -ERESTARTNOHAND:
+			regs->regs[4] = -EINTR;
+			break;
+		case -ERESTARTSYS:
+			if (!(ksig->ka.sa.sa_flags & SA_RESTART)) {
+				regs->regs[4] = -EINTR;
+				break;
+			}
+			fallthrough;
+		case -ERESTARTNOINTR:
+			regs->regs[4] = regs->orig_a0;
+			regs->csr_epc -= 4;
+		}
+
+		regs->regs[0] = 0;		/* Don't deal with this again.	*/
+	}
+
+	rseq_signal_deliver(ksig, regs);
+
+	ret = abi->setup_rt_frame(vdso + abi->vdso->off_rt_sigreturn,
+				  ksig, regs, oldset);
+
+	signal_setup_done(ret, ksig, 0);
+}
+
+static void do_signal(struct pt_regs *regs)
+{
+	struct ksignal ksig;
+
+	if (get_signal(&ksig)) {
+		/* Whee!  Actually deliver the signal.	*/
+		handle_signal(&ksig, regs);
+		return;
+	}
+
+	/* Are we from a system call? */
+	if (regs->regs[0]) {
+		switch (regs->regs[4]) {
+		case -ERESTARTNOHAND:
+		case -ERESTARTSYS:
+		case -ERESTARTNOINTR:
+			regs->regs[4] = regs->orig_a0;
+			regs->csr_epc -= 4;
+			break;
+
+		case -ERESTART_RESTARTBLOCK:
+			regs->regs[4] = regs->orig_a0;
+			regs->regs[11] = current->thread.abi->restart;
+			regs->csr_epc -= 4;
+			break;
+		}
+		regs->regs[0] = 0;	/* Don't deal with this again.	*/
+	}
+
+	/*
+	 * If there's no signal to deliver, we just put the saved sigmask
+	 * back
+	 */
+	restore_saved_sigmask();
+}
+
+/*
+ * notification of userspace execution resumption
+ * - triggered by the TIF_WORK_MASK flags
+ */
+asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
+	__u32 thread_info_flags)
+{
+	local_irq_enable();
+
+	user_exit();
+
+	if (thread_info_flags & _TIF_UPROBE)
+		uprobe_notify_resume(regs);
+
+	/* deal with pending signal delivery */
+	if (thread_info_flags & (_TIF_SIGPENDING | _TIF_NOTIFY_SIGNAL))
+		do_signal(regs);
+
+	if (thread_info_flags & _TIF_NOTIFY_RESUME) {
+		clear_thread_flag(TIF_NOTIFY_RESUME);
+		tracehook_notify_resume(regs);
+		rseq_handle_notify_resume(NULL, regs);
+	}
+
+	user_enter();
+}
+
+static int signal_setup(void)
+{
+	/*
+	 * The offset from sigcontext to extended context should be the same
+	 * regardless of the type of signal, such that userland can always know
+	 * where to look if it wishes to find the extended context structures.
+	 */
+	BUILD_BUG_ON((offsetof(struct sigframe, sf_extcontext) -
+		      offsetof(struct sigframe, sf_sc)) !=
+		     (offsetof(struct rt_sigframe, rs_uc.uc_extcontext) -
+		      offsetof(struct rt_sigframe, rs_uc.uc_mcontext)));
+
+	if (cpu_has_fpu) {
+		save_fp_context = save_hw_fp_context;
+		restore_fp_context = restore_hw_fp_context;
+	} else {
+		save_fp_context = copy_fp_to_sigcontext;
+		restore_fp_context = copy_fp_from_sigcontext;
+	}
+
+	return 0;
+}
+
+arch_initcall(signal_setup);
-- 
2.27.0


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

* [PATCH 11/19] LoongArch: Add elf and module support
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (8 preceding siblings ...)
  2021-07-06  4:18 ` [PATCH 10/19] LoongArch: Add signal handling support Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06  4:18 ` [PATCH 12/19] LoongArch: Add misc common routines Huacai Chen
                   ` (10 subsequent siblings)
  20 siblings, 0 replies; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

This patch adds elf definition and module relocate codes.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 arch/loongarch/include/asm/cpufeature.h  |  24 +
 arch/loongarch/include/asm/elf.h         | 311 +++++++++++
 arch/loongarch/include/asm/exec.h        |  10 +
 arch/loongarch/include/asm/module.h      |  15 +
 arch/loongarch/include/asm/vermagic.h    |  19 +
 arch/loongarch/include/uapi/asm/auxvec.h |  17 +
 arch/loongarch/include/uapi/asm/hwcap.h  |  20 +
 arch/loongarch/kernel/elf.c              |  41 ++
 arch/loongarch/kernel/module.c           | 652 +++++++++++++++++++++++
 9 files changed, 1109 insertions(+)
 create mode 100644 arch/loongarch/include/asm/cpufeature.h
 create mode 100644 arch/loongarch/include/asm/elf.h
 create mode 100644 arch/loongarch/include/asm/exec.h
 create mode 100644 arch/loongarch/include/asm/module.h
 create mode 100644 arch/loongarch/include/asm/vermagic.h
 create mode 100644 arch/loongarch/include/uapi/asm/auxvec.h
 create mode 100644 arch/loongarch/include/uapi/asm/hwcap.h
 create mode 100644 arch/loongarch/kernel/elf.c
 create mode 100644 arch/loongarch/kernel/module.c

diff --git a/arch/loongarch/include/asm/cpufeature.h b/arch/loongarch/include/asm/cpufeature.h
new file mode 100644
index 000000000000..7e4c6a769830
--- /dev/null
+++ b/arch/loongarch/include/asm/cpufeature.h
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * CPU feature definitions for module loading, used by
+ * module_cpu_feature_match(), see uapi/asm/hwcap.h for LoongArch CPU features.
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef __ASM_CPUFEATURE_H
+#define __ASM_CPUFEATURE_H
+
+#include <uapi/asm/hwcap.h>
+#include <asm/elf.h>
+
+#define MAX_CPU_FEATURES (8 * sizeof(elf_hwcap))
+
+#define cpu_feature(x)		ilog2(HWCAP_ ## x)
+
+static inline bool cpu_have_feature(unsigned int num)
+{
+	return elf_hwcap & (1UL << num);
+}
+
+#endif /* __ASM_CPUFEATURE_H */
diff --git a/arch/loongarch/include/asm/elf.h b/arch/loongarch/include/asm/elf.h
new file mode 100644
index 000000000000..b74e55823c29
--- /dev/null
+++ b/arch/loongarch/include/asm/elf.h
@@ -0,0 +1,311 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_ELF_H
+#define _ASM_ELF_H
+
+#include <linux/auxvec.h>
+#include <linux/fs.h>
+#include <linux/mm_types.h>
+
+#include <uapi/linux/elf.h>
+
+#include <asm/current.h>
+
+/* ELF header e_flags defines. */
+
+/* The ABI of a file. */
+#define EF_LARCH_ABI_LP32		0x00000001	/* LP32 ABI.  */
+#define EF_LARCH_ABI_LPX32		0x00000002	/* LPX32 ABI  */
+#define EF_LARCH_ABI_LP64		0x00000003	/* LP64 ABI  */
+#define EF_LARCH_ABI			0x00000003
+
+/* LoongArch relocation types used by the dynamic linker */
+#define R_LARCH_NONE				0
+#define R_LARCH_32				1
+#define R_LARCH_64				2
+#define R_LARCH_RELATIVE			3
+#define R_LARCH_COPY				4
+#define R_LARCH_JUMP_SLOT			5
+#define R_LARCH_TLS_DTPMOD32			6
+#define R_LARCH_TLS_DTPMOD64			7
+#define R_LARCH_TLS_DTPREL32			8
+#define R_LARCH_TLS_DTPREL64			9
+#define R_LARCH_TLS_TPREL32			10
+#define R_LARCH_TLS_TPREL64			11
+#define R_LARCH_IRELATIVE			12
+#define R_LARCH_MARK_LA				20
+#define R_LARCH_MARK_PCREL			21
+#define R_LARCH_SOP_PUSH_PCREL			22
+#define R_LARCH_SOP_PUSH_ABSOLUTE		23
+#define R_LARCH_SOP_PUSH_DUP			24
+#define R_LARCH_SOP_PUSH_GPREL			25
+#define R_LARCH_SOP_PUSH_TLS_TPREL		26
+#define R_LARCH_SOP_PUSH_TLS_GOT		27
+#define R_LARCH_SOP_PUSH_TLS_GD			28
+#define R_LARCH_SOP_PUSH_PLT_PCREL		29
+#define R_LARCH_SOP_ASSERT			30
+#define R_LARCH_SOP_NOT				31
+#define R_LARCH_SOP_SUB				32
+#define R_LARCH_SOP_SL				33
+#define R_LARCH_SOP_SR				34
+#define R_LARCH_SOP_ADD				35
+#define R_LARCH_SOP_AND				36
+#define R_LARCH_SOP_IF_ELSE			37
+#define R_LARCH_SOP_POP_32_S_10_5		38
+#define R_LARCH_SOP_POP_32_U_10_12		39
+#define R_LARCH_SOP_POP_32_S_10_12		40
+#define R_LARCH_SOP_POP_32_S_10_16		41
+#define R_LARCH_SOP_POP_32_S_10_16_S2		42
+#define R_LARCH_SOP_POP_32_S_5_20		43
+#define R_LARCH_SOP_POP_32_S_0_5_10_16_S2	44
+#define R_LARCH_SOP_POP_32_S_0_10_10_16_S2	45
+#define R_LARCH_SOP_POP_32_U			46
+#define R_LARCH_ADD8				47
+#define R_LARCH_ADD16				48
+#define R_LARCH_ADD24				49
+#define R_LARCH_ADD32				50
+#define R_LARCH_ADD64				51
+#define R_LARCH_SUB8				52
+#define R_LARCH_SUB16				53
+#define R_LARCH_SUB24				54
+#define R_LARCH_SUB32				55
+#define R_LARCH_SUB64				56
+#define R_LARCH_GNU_VTINHERIT			57
+#define R_LARCH_GNU_VTENTRY			58
+#define R_LARCH_PC16				64
+#define R_LARCH_PC21				65
+#define R_LARCH_PC26				66
+
+#ifndef ELF_ARCH
+
+/* ELF register definitions */
+
+/*
+ * General purpose have the following registers:
+ *	Register	Number
+ *	GPRs		32
+ *	EPC		1
+ *	BADVADDR	1
+ *	CRMD		1
+ *	PRMD		1
+ *	EUEN		1
+ *	ECFG		1
+ *	ESTAT		1
+ *	Reserved	6
+ */
+#define ELF_NGREG	45
+
+/*
+ * Floating point have the following registers:
+ *	Register	Number
+ *	FPR		32
+ *	FCC		1
+ *	FCSR		1
+ */
+#define ELF_NFPREG	34
+
+typedef unsigned long elf_greg_t;
+typedef elf_greg_t elf_gregset_t[ELF_NGREG];
+
+typedef double elf_fpreg_t;
+typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
+
+void loongarch_dump_regs64(u64 *uregs, const struct pt_regs *regs);
+
+#ifdef CONFIG_32BIT
+/*
+ * This is used to ensure we don't load something for the wrong architecture.
+ */
+#define elf_check_arch elf32_check_arch
+
+/*
+ * These are used to set parameters in the core dumps.
+ */
+#define ELF_CLASS	ELFCLASS32
+
+#define ELF_CORE_COPY_REGS(dest, regs) \
+	loongarch_dump_regs32((u32 *)&(dest), (regs));
+
+#endif /* CONFIG_32BIT */
+
+#ifdef CONFIG_64BIT
+/*
+ * This is used to ensure we don't load something for the wrong architecture.
+ */
+#define elf_check_arch elf64_check_arch
+
+/*
+ * These are used to set parameters in the core dumps.
+ */
+#define ELF_CLASS	ELFCLASS64
+
+#define ELF_CORE_COPY_REGS(dest, regs) \
+	loongarch_dump_regs64((u64 *)&(dest), (regs));
+
+#endif /* CONFIG_64BIT */
+
+/*
+ * These are used to set parameters in the core dumps.
+ */
+#define ELF_DATA	ELFDATA2LSB
+#define ELF_ARCH	EM_LOONGARCH
+
+#endif /* !defined(ELF_ARCH) */
+
+#define loongarch_elf_check_machine(x) ((x)->e_machine == EM_LOONGARCH)
+
+#define vmcore_elf32_check_arch loongarch_elf_check_machine
+#define vmcore_elf64_check_arch loongarch_elf_check_machine
+
+/*
+ * Return non-zero if HDR identifies an 32bit ELF binary.
+ */
+#define elf32_check_arch(hdr)						\
+({									\
+	int __res = 1;							\
+	struct elfhdr *__h = (hdr);					\
+									\
+	if (!loongarch_elf_check_machine(__h))				\
+		__res = 0;						\
+	if (__h->e_ident[EI_CLASS] != ELFCLASS32)			\
+		__res = 0;						\
+									\
+	__res;								\
+})
+
+/*
+ * Return non-zero if HDR identifies an 64bit ELF binary.
+ */
+#define elf64_check_arch(hdr)						\
+({									\
+	int __res = 1;							\
+	struct elfhdr *__h = (hdr);					\
+									\
+	if (!loongarch_elf_check_machine(__h))				\
+		__res = 0;						\
+	if (__h->e_ident[EI_CLASS] != ELFCLASS64)			\
+		__res = 0;						\
+									\
+	__res;								\
+})
+
+struct loongarch_abi;
+extern struct loongarch_abi loongarch_abi;
+
+#ifdef CONFIG_32BIT
+
+#define SET_PERSONALITY2(ex, state)					\
+do {									\
+	current->thread.abi = &loongarch_abi;				\
+									\
+	loongarch_set_personality_fcsr(state);				\
+									\
+	if (personality(current->personality) != PER_LINUX)		\
+		set_personality(PER_LINUX);				\
+} while (0)
+
+#endif /* CONFIG_32BIT */
+
+#ifdef CONFIG_64BIT
+
+#define SET_PERSONALITY2(ex, state)					\
+do {									\
+	unsigned int p;							\
+									\
+	clear_thread_flag(TIF_32BIT_REGS);				\
+	clear_thread_flag(TIF_32BIT_ADDR);				\
+									\
+	current->thread.abi = &loongarch_abi;			\
+	loongarch_set_personality_fcsr(state);				\
+									\
+	p = personality(current->personality);				\
+	if (p != PER_LINUX32 && p != PER_LINUX)				\
+		set_personality(PER_LINUX);				\
+} while (0)
+
+#endif /* CONFIG_64BIT */
+
+#define CORE_DUMP_USE_REGSET
+#define ELF_EXEC_PAGESIZE	PAGE_SIZE
+
+/*
+ * This yields a mask that user programs can use to figure out what
+ * instruction set this cpu supports. This could be done in userspace,
+ * but it's not easy, and we've already done it here.
+ */
+
+#define ELF_HWCAP	(elf_hwcap)
+extern unsigned int elf_hwcap;
+#include <asm/hwcap.h>
+
+/*
+ * This yields a string that ld.so will use to load implementation
+ * specific libraries for optimization.	 This is more specific in
+ * intent than poking at uname or /proc/cpuinfo.
+ */
+
+#define ELF_PLATFORM  __elf_platform
+extern const char *__elf_platform;
+
+/* The regs[11] (a7) holds the syscall number and should not cleared */
+#define ELF_PLAT_INIT(_r, load_addr)	do { \
+	_r->regs[1] = _r->regs[2] = _r->regs[3] = _r->regs[4] = 0;	\
+	_r->regs[5] = _r->regs[6] = _r->regs[7] = _r->regs[8] = 0;	\
+	_r->regs[9] = _r->regs[10] = _r->regs[12] = _r->regs[13] = 0;	\
+	_r->regs[14] = _r->regs[15] = _r->regs[16] = _r->regs[17] = 0;	\
+	_r->regs[18] = _r->regs[19] = _r->regs[20] = _r->regs[21] = 0;	\
+	_r->regs[22] = _r->regs[23] = _r->regs[24] = _r->regs[25] = 0;	\
+	_r->regs[26] = _r->regs[27] = _r->regs[28] = _r->regs[29] = 0;	\
+	_r->regs[30] = _r->regs[31] = 0;				\
+} while (0)
+
+/*
+ * This is the location that an ET_DYN program is loaded if exec'ed. Typical
+ * use of this is to invoke "./ld.so someprog" to test out a new version of
+ * the loader. We need to make sure that it is out of the way of the program
+ * that it will "exec", and that there is sufficient room for the brk.
+ */
+
+#ifndef ELF_ET_DYN_BASE
+#define ELF_ET_DYN_BASE		(TASK_SIZE / 3 * 2)
+#endif
+
+/* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT entries changes */
+#define ARCH_DLINFO							\
+do {									\
+	NEW_AUX_ENT(AT_SYSINFO_EHDR,					\
+		    (unsigned long)current->mm->context.vdso);		\
+} while (0)
+
+#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
+struct linux_binprm;
+extern int arch_setup_additional_pages(struct linux_binprm *bprm,
+				       int uses_interp);
+
+struct arch_elf_state {
+	int fp_abi;
+	int interp_fp_abi;
+};
+
+#define LOONGARCH_ABI_FP_ANY	(0)
+
+#define INIT_ARCH_ELF_STATE {			\
+	.fp_abi = LOONGARCH_ABI_FP_ANY,		\
+	.interp_fp_abi = LOONGARCH_ABI_FP_ANY,	\
+}
+
+
+extern int arch_elf_pt_proc(void *ehdr, void *phdr, struct file *elf,
+			    bool is_interp, struct arch_elf_state *state);
+
+extern int arch_check_elf(void *ehdr, bool has_interpreter, void *interp_ehdr,
+			  struct arch_elf_state *state);
+
+extern void loongarch_set_personality_fcsr(struct arch_elf_state *state);
+
+#define elf_read_implies_exec(ex, stk) loongarch_elf_read_implies_exec(&(ex), stk)
+extern int loongarch_elf_read_implies_exec(void *elf_ex, int exstack);
+
+#endif /* _ASM_ELF_H */
diff --git a/arch/loongarch/include/asm/exec.h b/arch/loongarch/include/asm/exec.h
new file mode 100644
index 000000000000..00df07ce3529
--- /dev/null
+++ b/arch/loongarch/include/asm/exec.h
@@ -0,0 +1,10 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_EXEC_H
+#define _ASM_EXEC_H
+
+extern unsigned long arch_align_stack(unsigned long sp);
+
+#endif /* _ASM_EXEC_H */
diff --git a/arch/loongarch/include/asm/module.h b/arch/loongarch/include/asm/module.h
new file mode 100644
index 000000000000..6df72c6228e0
--- /dev/null
+++ b/arch/loongarch/include/asm/module.h
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_MODULE_H
+#define _ASM_MODULE_H
+
+#include <asm-generic/module.h>
+
+#define RELA_STACK_DEPTH 16
+
+struct mod_arch_specific {
+};
+
+#endif /* _ASM_MODULE_H */
diff --git a/arch/loongarch/include/asm/vermagic.h b/arch/loongarch/include/asm/vermagic.h
new file mode 100644
index 000000000000..9882dfd4702a
--- /dev/null
+++ b/arch/loongarch/include/asm/vermagic.h
@@ -0,0 +1,19 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_VERMAGIC_H
+#define _ASM_VERMAGIC_H
+
+#define MODULE_PROC_FAMILY "LOONGARCH "
+
+#ifdef CONFIG_32BIT
+#define MODULE_KERNEL_TYPE "32BIT "
+#elif defined CONFIG_64BIT
+#define MODULE_KERNEL_TYPE "64BIT "
+#endif
+
+#define MODULE_ARCH_VERMAGIC \
+	MODULE_PROC_FAMILY MODULE_KERNEL_TYPE
+
+#endif /* _ASM_VERMAGIC_H */
diff --git a/arch/loongarch/include/uapi/asm/auxvec.h b/arch/loongarch/include/uapi/asm/auxvec.h
new file mode 100644
index 000000000000..5a2200fd75bf
--- /dev/null
+++ b/arch/loongarch/include/uapi/asm/auxvec.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0+ WITH Linux-syscall-note */
+/*
+ * Author: Hanlu Li <lihanlu@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef __ASM_AUXVEC_H
+#define __ASM_AUXVEC_H
+
+/* Location of VDSO image. */
+#define AT_SYSINFO_EHDR		33
+
+#define AT_VECTOR_SIZE_ARCH 1 /* entries in ARCH_DLINFO */
+
+#endif /* __ASM_AUXVEC_H */
diff --git a/arch/loongarch/include/uapi/asm/hwcap.h b/arch/loongarch/include/uapi/asm/hwcap.h
new file mode 100644
index 000000000000..8840b72fa8e8
--- /dev/null
+++ b/arch/loongarch/include/uapi/asm/hwcap.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI_ASM_HWCAP_H
+#define _UAPI_ASM_HWCAP_H
+
+/* HWCAP flags */
+#define HWCAP_LOONGARCH_CPUCFG		(1 << 0)
+#define HWCAP_LOONGARCH_LAM		(1 << 1)
+#define HWCAP_LOONGARCH_UAL		(1 << 2)
+#define HWCAP_LOONGARCH_FPU		(1 << 3)
+#define HWCAP_LOONGARCH_LSX		(1 << 4)
+#define HWCAP_LOONGARCH_LASX		(1 << 5)
+#define HWCAP_LOONGARCH_CRC32		(1 << 6)
+#define HWCAP_LOONGARCH_COMPLEX		(1 << 7)
+#define HWCAP_LOONGARCH_CRYPTO		(1 << 8)
+#define HWCAP_LOONGARCH_LVZ		(1 << 9)
+#define HWCAP_LOONGARCH_LBT_X86		(1 << 10)
+#define HWCAP_LOONGARCH_LBT_ARM		(1 << 11)
+#define HWCAP_LOONGARCH_LBT_MIPS	(1 << 12)
+
+#endif /* _UAPI_ASM_HWCAP_H */
diff --git a/arch/loongarch/kernel/elf.c b/arch/loongarch/kernel/elf.c
new file mode 100644
index 000000000000..072dfd40f0b5
--- /dev/null
+++ b/arch/loongarch/kernel/elf.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <linux/binfmts.h>
+#include <linux/elf.h>
+#include <linux/export.h>
+#include <linux/sched.h>
+
+#include <asm/cpu-features.h>
+#include <asm/cpu-info.h>
+
+int arch_elf_pt_proc(void *_ehdr, void *_phdr, struct file *elf,
+		     bool is_interp, struct arch_elf_state *state)
+{
+	return 0;
+}
+
+int arch_check_elf(void *_ehdr, bool has_interpreter, void *_interp_ehdr,
+		   struct arch_elf_state *state)
+{
+	return 0;
+}
+
+void loongarch_set_personality_fcsr(struct arch_elf_state *state)
+{
+	current->thread.fpu.fcsr = boot_cpu_data.fpu_csr0;
+}
+
+int loongarch_elf_read_implies_exec(void *elf_ex, int exstack)
+{
+	if (exstack != EXSTACK_DISABLE_X) {
+		/* The binary doesn't request a non-executable stack */
+		return 1;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL(loongarch_elf_read_implies_exec);
diff --git a/arch/loongarch/kernel/module.c b/arch/loongarch/kernel/module.c
new file mode 100644
index 000000000000..af7c403b032b
--- /dev/null
+++ b/arch/loongarch/kernel/module.c
@@ -0,0 +1,652 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Author: Hanlu Li <lihanlu@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#undef DEBUG
+
+#include <linux/moduleloader.h>
+#include <linux/elf.h>
+#include <linux/mm.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+static int rela_stack_push(s64 stack_value, s64 *rela_stack, size_t *rela_stack_top)
+{
+	if (*rela_stack_top >= RELA_STACK_DEPTH)
+		return -ENOEXEC;
+
+	rela_stack[(*rela_stack_top)++] = stack_value;
+	pr_debug("%s stack_value = 0x%llx\n", __func__, stack_value);
+
+	return 0;
+}
+
+static int rela_stack_pop(s64 *stack_value, s64 *rela_stack, size_t *rela_stack_top)
+{
+	if (*rela_stack_top == 0)
+		return -ENOEXEC;
+
+	*stack_value = rela_stack[--(*rela_stack_top)];
+	pr_debug("%s stack_value = 0x%llx\n", __func__, *stack_value);
+
+	return 0;
+}
+
+static int apply_r_larch_none(struct module *me, u32 *location, Elf_Addr v,
+				s64 *rela_stack, size_t *rela_stack_top)
+{
+	return 0;
+}
+
+static int apply_r_larch_32(struct module *me, u32 *location, Elf_Addr v,
+				s64 *rela_stack, size_t *rela_stack_top)
+{
+	*location = v;
+	return 0;
+}
+
+static int apply_r_larch_64(struct module *me, u32 *location, Elf_Addr v,
+				s64 *rela_stack, size_t *rela_stack_top)
+{
+	*(Elf_Addr *)location = v;
+	return 0;
+}
+
+static int apply_r_larch_mark_la(struct module *me, u32 *location, Elf_Addr v,
+					s64 *rela_stack, size_t *rela_stack_top)
+{
+	return 0;
+}
+
+static int apply_r_larch_mark_pcrel(struct module *me, u32 *location, Elf_Addr v,
+					s64 *rela_stack, size_t *rela_stack_top)
+{
+	return 0;
+}
+
+static int apply_r_larch_sop_push_pcrel(struct module *me, u32 *location, Elf_Addr v,
+						s64 *rela_stack, size_t *rela_stack_top)
+{
+	return rela_stack_push(v - (u64)location, rela_stack, rela_stack_top);
+}
+
+static int apply_r_larch_sop_push_absolute(struct module *me, u32 *location, Elf_Addr v,
+						s64 *rela_stack, size_t *rela_stack_top)
+{
+	return rela_stack_push(v, rela_stack, rela_stack_top);
+}
+
+static int apply_r_larch_sop_push_dup(struct module *me, u32 *location, Elf_Addr v,
+						s64 *rela_stack, size_t *rela_stack_top)
+{
+	int err = 0;
+	s64 opr1;
+
+	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+	err = rela_stack_push(opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+	err = rela_stack_push(opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int apply_r_larch_sop_push_plt_pcrel(struct module *me, u32 *location, Elf_Addr v,
+						s64 *rela_stack, size_t *rela_stack_top)
+{
+	return apply_r_larch_sop_push_pcrel(me, location, v, rela_stack, rela_stack_top);
+}
+
+static int apply_r_larch_sop_sub(struct module *me, u32 *location, Elf_Addr v,
+					s64 *rela_stack, size_t *rela_stack_top)
+{
+	int err = 0;
+	s64 opr1, opr2;
+
+	err = rela_stack_pop(&opr2, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+	err = rela_stack_push(opr1 - opr2, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int apply_r_larch_sop_sl(struct module *me, u32 *location, Elf_Addr v,
+					s64 *rela_stack, size_t *rela_stack_top)
+{
+	int err = 0;
+	s64 opr1, opr2;
+
+	err = rela_stack_pop(&opr2, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+	err = rela_stack_push(opr1 << opr2, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int apply_r_larch_sop_sr(struct module *me, u32 *location, Elf_Addr v,
+					s64 *rela_stack, size_t *rela_stack_top)
+{
+	int err = 0;
+	s64 opr1, opr2;
+
+	err = rela_stack_pop(&opr2, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+	err = rela_stack_push(opr1 >> opr2, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int apply_r_larch_sop_add(struct module *me, u32 *location, Elf_Addr v,
+					s64 *rela_stack, size_t *rela_stack_top)
+{
+	int err = 0;
+	s64 opr1, opr2;
+
+	err = rela_stack_pop(&opr2, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+	err = rela_stack_push(opr1 + opr2, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+
+	return 0;
+}
+static int apply_r_larch_sop_and(struct module *me, u32 *location, Elf_Addr v,
+					s64 *rela_stack, size_t *rela_stack_top)
+{
+	int err = 0;
+	s64 opr1, opr2;
+
+	err = rela_stack_pop(&opr2, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+	err = rela_stack_push(opr1 & opr2, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int apply_r_larch_sop_if_else(struct module *me, u32 *location, Elf_Addr v,
+					s64 *rela_stack, size_t *rela_stack_top)
+{
+	int err = 0;
+	s64 opr1, opr2, opr3;
+
+	err = rela_stack_pop(&opr3, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+	err = rela_stack_pop(&opr2, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+	err = rela_stack_push(opr1 ? opr2 : opr3, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+
+	return 0;
+}
+
+static int apply_r_larch_sop_pop_32_s_10_5(struct module *me, u32 *location, Elf_Addr v,
+						s64 *rela_stack, size_t *rela_stack_top)
+{
+	int err = 0;
+	s64 opr1;
+
+	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+
+	/* check 5-bit signed */
+	if ((opr1 & ~(u64)0xf) &&
+	    (opr1 & ~(u64)0xf) != ~(u64)0xf) {
+		pr_err("module %s: opr1 = 0x%llx overflow! dangerous %s relocation\n",
+			me->name, opr1, __func__);
+		return -ENOEXEC;
+	}
+
+	/* (*(uint32_t *) PC) [14 ... 10] = opr [4 ... 0] */
+	*location = (*location & (~(u32)0x7c00)) | ((opr1 & 0x1f) << 10);
+
+	return 0;
+}
+
+static int apply_r_larch_sop_pop_32_u_10_12(struct module *me, u32 *location, Elf_Addr v,
+						s64 *rela_stack, size_t *rela_stack_top)
+{
+	int err = 0;
+	s64 opr1;
+
+	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+
+	/* check 12-bit unsigned */
+	if (opr1 & ~(u64)0xfff) {
+		pr_err("module %s: opr1 = 0x%llx overflow! dangerous %s relocation\n",
+			me->name, opr1, __func__);
+		return -ENOEXEC;
+	}
+
+	/* (*(uint32_t *) PC) [21 ... 10] = opr [11 ... 0] */
+	*location = (*location & (~(u32)0x3ffc00)) | ((opr1 & 0xfff) << 10);
+
+	return 0;
+}
+
+static int apply_r_larch_sop_pop_32_s_10_12(struct module *me, u32 *location, Elf_Addr v,
+						s64 *rela_stack, size_t *rela_stack_top)
+{
+	int err = 0;
+	s64 opr1;
+
+	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+
+	/* check 12-bit signed */
+	if ((opr1 & ~(u64)0x7ff) &&
+	    (opr1 & ~(u64)0x7ff) != ~(u64)0x7ff) {
+		pr_err("module %s: opr1 = 0x%llx overflow! dangerous %s relocation\n",
+			me->name, opr1, __func__);
+		return -ENOEXEC;
+	}
+
+	/* (*(uint32_t *) PC) [21 ... 10] = opr [11 ... 0] */
+	*location = (*location & (~(u32)0x3ffc00)) | ((opr1 & 0xfff) << 10);
+
+	return 0;
+}
+
+static int apply_r_larch_sop_pop_32_s_10_16(struct module *me, u32 *location, Elf_Addr v,
+						s64 *rela_stack, size_t *rela_stack_top)
+{
+	int err = 0;
+	s64 opr1;
+
+	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+
+	/* check 16-bit signed */
+	if ((opr1 & ~(u64)0x7fff) &&
+	    (opr1 & ~(u64)0x7fff) != ~(u64)0x7fff) {
+		pr_err("module %s: opr1 = 0x%llx overflow! dangerous %s relocation\n",
+			me->name, opr1, __func__);
+		return -ENOEXEC;
+	}
+
+	/* (*(uint32_t *) PC) [25 ... 10] = opr [15 ... 0] */
+	*location = (*location & 0xfc0003ff) | ((opr1 & 0xffff) << 10);
+
+	return 0;
+}
+
+static int apply_r_larch_sop_pop_32_s_10_16_s2(struct module *me, u32 *location, Elf_Addr v,
+						s64 *rela_stack, size_t *rela_stack_top)
+{
+	int err = 0;
+	s64 opr1;
+
+	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+
+	/* check 4-aligned */
+	if (opr1 % 4) {
+		pr_err("module %s: opr1 = 0x%llx unaligned! dangerous %s relocation\n",
+			me->name, opr1, __func__);
+		return -ENOEXEC;
+	}
+
+	opr1 >>= 2;
+	/* check 18-bit signed */
+	if ((opr1 & ~(u64)0x7fff) &&
+	    (opr1 & ~(u64)0x7fff) != ~(u64)0x7fff) {
+		pr_err("module %s: opr1 = 0x%llx overflow! dangerous %s relocation\n",
+			me->name, opr1, __func__);
+		return -ENOEXEC;
+	}
+
+	/* (*(uint32_t *) PC) [25 ... 10] = opr [17 ... 2] */
+	*location = (*location & 0xfc0003ff) | ((opr1 & 0xffff) << 10);
+
+	return 0;
+}
+
+static int apply_r_larch_sop_pop_32_s_5_20(struct module *me, u32 *location, Elf_Addr v,
+						s64 *rela_stack, size_t *rela_stack_top)
+{
+	int err = 0;
+	s64 opr1;
+
+	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+
+	/* check 20-bit signed */
+	if ((opr1 & ~(u64)0x7ffff) &&
+	    (opr1 & ~(u64)0x7ffff) != ~(u64)0x7ffff) {
+		pr_err("module %s: opr1 = 0x%llx overflow! dangerous %s relocation\n",
+			me->name, opr1, __func__);
+		return -ENOEXEC;
+	}
+
+	/* (*(uint32_t *) PC) [24 ... 5] = opr [19 ... 0] */
+	*location = (*location & (~(u32)0x1ffffe0)) | ((opr1 & 0xfffff) << 5);
+
+	return 0;
+}
+
+static int apply_r_larch_sop_pop_32_s_0_5_10_16_s2(struct module *me, u32 *location, Elf_Addr v,
+							s64 *rela_stack, size_t *rela_stack_top)
+{
+	int err = 0;
+	s64 opr1;
+
+	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+
+	/* check 4-aligned */
+	if (opr1 % 4) {
+		pr_err("module %s: opr1 = 0x%llx unaligned! dangerous %s relocation\n",
+			me->name, opr1, __func__);
+		return -ENOEXEC;
+	}
+
+	opr1 >>= 2;
+	/* check 23-bit signed */
+	if ((opr1 & ~(u64)0xfffff) &&
+	    (opr1 & ~(u64)0xfffff) != ~(u64)0xfffff) {
+		pr_err("module %s: opr1 = 0x%llx overflow! dangerous %s relocation\n",
+			me->name, opr1, __func__);
+		return -ENOEXEC;
+	}
+
+	/*
+	 * (*(uint32_t *) PC) [4 ... 0] = opr [22 ... 18]
+	 * (*(uint32_t *) PC) [25 ... 10] = opr [17 ... 2]
+	 */
+	*location = (*location & 0xfc0003e0)
+		| ((opr1 & 0x1f0000) >> 16) | ((opr1 & 0xffff) << 10);
+
+	return 0;
+}
+
+static int apply_r_larch_sop_pop_32_s_0_10_10_16_s2(struct module *me, u32 *location, Elf_Addr v,
+							s64 *rela_stack, size_t *rela_stack_top)
+{
+	int err = 0;
+	s64 opr1;
+
+	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+
+	/* check 4-aligned */
+	if (opr1 % 4) {
+		pr_err("module %s: opr1 = 0x%llx unaligned! dangerous %s relocation\n",
+			me->name, opr1, __func__);
+		return -ENOEXEC;
+	}
+
+	opr1 >>= 2;
+	/* check 28-bit signed */
+	if ((opr1 & ~(u64)0x1ffffff) &&
+	    (opr1 & ~(u64)0x1ffffff) != ~(u64)0x1ffffff) {
+		pr_err("module %s: opr1 = 0x%llx overflow! dangerous %s relocation\n",
+			me->name, opr1, __func__);
+		return -ENOEXEC;
+	}
+
+	/*
+	 * (*(uint32_t *) PC) [9 ... 0] = opr [27 ... 18]
+	 * (*(uint32_t *) PC) [25 ... 10] = opr [17 ... 2]
+	 */
+	*location = (*location & 0xfc000000)
+		| ((opr1 & 0x3ff0000) >> 16) | ((opr1 & 0xffff) << 10);
+
+	return 0;
+}
+
+static int apply_r_larch_sop_pop_32_u(struct module *me, u32 *location, Elf_Addr v,
+					s64 *rela_stack, size_t *rela_stack_top)
+{
+	int err = 0;
+	s64 opr1;
+
+	err = rela_stack_pop(&opr1, rela_stack, rela_stack_top);
+	if (err)
+		return err;
+
+	/* check 32-bit unsigned */
+	if (opr1 & ~(u64)0xffffffff) {
+		pr_err("module %s: opr1 = 0x%llx overflow! dangerous %s relocation\n",
+			me->name, opr1, __func__);
+		return -ENOEXEC;
+	}
+
+	/* (*(uint32_t *) PC) = opr */
+	*location = (u32)opr1;
+
+	return 0;
+}
+
+static int apply_r_larch_add32(struct module *me, u32 *location, Elf_Addr v,
+				s64 *rela_stack, size_t *rela_stack_top)
+{
+	*(s32 *)location += v;
+	return 0;
+}
+
+static int apply_r_larch_add64(struct module *me, u32 *location, Elf_Addr v,
+				s64 *rela_stack, size_t *rela_stack_top)
+{
+	*(s64 *)location += v;
+	return 0;
+}
+
+static int apply_r_larch_sub32(struct module *me, u32 *location, Elf_Addr v,
+				s64 *rela_stack, size_t *rela_stack_top)
+{
+	*(s32 *)location -= v;
+	return 0;
+}
+
+static int apply_r_larch_sub64(struct module *me, u32 *location, Elf_Addr v,
+				s64 *rela_stack, size_t *rela_stack_top)
+{
+	*(s64 *)location -= v;
+	return 0;
+}
+
+/*
+ * reloc_handlers_rela() - Apply a particular relocation to a module
+ * @me: the module to apply the reloc to
+ * @location: the address at which the reloc is to be applied
+ * @v: the value of the reloc, with addend for RELA-style
+ * @rela_stack: the stack used for store relocation info, LOCAL to THIS module
+ * @rela_stac_top: where the stack operation(pop/push) applies to
+ *
+ * Return: 0 upon success, else -ERRNO
+ */
+typedef int (*reloc_rela_handler)(struct module *me, u32 *location, Elf_Addr v,
+				s64 *rela_stack, size_t *rela_stack_top);
+
+/* The handlers for known reloc types */
+static reloc_rela_handler reloc_rela_handlers[] = {
+	[R_LARCH_NONE]				= apply_r_larch_none,
+	[R_LARCH_32]				= apply_r_larch_32,
+	[R_LARCH_64]				= apply_r_larch_64,
+	[R_LARCH_MARK_LA]			= apply_r_larch_mark_la,
+	[R_LARCH_MARK_PCREL]			= apply_r_larch_mark_pcrel,
+	[R_LARCH_SOP_PUSH_PCREL]		= apply_r_larch_sop_push_pcrel,
+	[R_LARCH_SOP_PUSH_ABSOLUTE]		= apply_r_larch_sop_push_absolute,
+	[R_LARCH_SOP_PUSH_DUP]			= apply_r_larch_sop_push_dup,
+	[R_LARCH_SOP_PUSH_PLT_PCREL]		= apply_r_larch_sop_push_plt_pcrel,
+	[R_LARCH_SOP_SUB]			= apply_r_larch_sop_sub,
+	[R_LARCH_SOP_SL]			= apply_r_larch_sop_sl,
+	[R_LARCH_SOP_SR]			= apply_r_larch_sop_sr,
+	[R_LARCH_SOP_ADD]			= apply_r_larch_sop_add,
+	[R_LARCH_SOP_AND]			= apply_r_larch_sop_and,
+	[R_LARCH_SOP_IF_ELSE]			= apply_r_larch_sop_if_else,
+	[R_LARCH_SOP_POP_32_S_10_5]		= apply_r_larch_sop_pop_32_s_10_5,
+	[R_LARCH_SOP_POP_32_U_10_12]		= apply_r_larch_sop_pop_32_u_10_12,
+	[R_LARCH_SOP_POP_32_S_10_12]		= apply_r_larch_sop_pop_32_s_10_12,
+	[R_LARCH_SOP_POP_32_S_10_16]		= apply_r_larch_sop_pop_32_s_10_16,
+	[R_LARCH_SOP_POP_32_S_10_16_S2]		= apply_r_larch_sop_pop_32_s_10_16_s2,
+	[R_LARCH_SOP_POP_32_S_5_20]		= apply_r_larch_sop_pop_32_s_5_20,
+	[R_LARCH_SOP_POP_32_S_0_5_10_16_S2]	= apply_r_larch_sop_pop_32_s_0_5_10_16_s2,
+	[R_LARCH_SOP_POP_32_S_0_10_10_16_S2]	= apply_r_larch_sop_pop_32_s_0_10_10_16_s2,
+	[R_LARCH_SOP_POP_32_U]			= apply_r_larch_sop_pop_32_u,
+	[R_LARCH_ADD32]				= apply_r_larch_add32,
+	[R_LARCH_ADD64]				= apply_r_larch_add64,
+	[R_LARCH_SUB32]				= apply_r_larch_sub32,
+	[R_LARCH_SUB64]				= apply_r_larch_sub64,
+};
+
+int apply_relocate(Elf_Shdr *sechdrs, const char *strtab,
+		   unsigned int symindex, unsigned int relsec,
+		   struct module *me)
+{
+	int i, err;
+	unsigned int type;
+	s64 rela_stack[RELA_STACK_DEPTH];
+	size_t rela_stack_top = 0;
+	reloc_rela_handler handler;
+	void *location;
+	Elf_Addr v;
+	Elf_Sym *sym;
+	Elf_Rel *rel = (void *) sechdrs[relsec].sh_addr;
+
+	pr_debug("%s: Applying relocate section %u to %u\n", __func__, relsec,
+	       sechdrs[relsec].sh_info);
+
+	rela_stack_top = 0;
+	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
+		/* This is where to make the change */
+		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + rel[i].r_offset;
+		/* This is the symbol it is referring to */
+		sym = (Elf_Sym *)sechdrs[symindex].sh_addr + ELF_R_SYM(rel[i].r_info);
+		if (IS_ERR_VALUE(sym->st_value)) {
+			/* Ignore unresolved weak symbol */
+			if (ELF_ST_BIND(sym->st_info) == STB_WEAK)
+				continue;
+			pr_warn("%s: Unknown symbol %s\n", me->name, strtab + sym->st_name);
+			return -ENOENT;
+		}
+
+		type = ELF_R_TYPE(rel[i].r_info);
+
+		if (type < ARRAY_SIZE(reloc_rela_handlers))
+			handler = reloc_rela_handlers[type];
+		else
+			handler = NULL;
+
+		if (!handler) {
+			pr_err("%s: Unknown relocation type %u\n", me->name, type);
+			return -EINVAL;
+		}
+
+		v = sym->st_value;
+		err = handler(me, location, v, rela_stack, &rela_stack_top);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
+
+int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
+		       unsigned int symindex, unsigned int relsec,
+		       struct module *me)
+{
+	int i, err;
+	unsigned int type;
+	s64 rela_stack[RELA_STACK_DEPTH];
+	size_t rela_stack_top = 0;
+	reloc_rela_handler handler;
+	void *location;
+	Elf_Addr v;
+	Elf_Sym *sym;
+	Elf_Rela *rel = (void *) sechdrs[relsec].sh_addr;
+
+	pr_debug("%s: Applying relocate section %u to %u\n", __func__, relsec,
+	       sechdrs[relsec].sh_info);
+
+	rela_stack_top = 0;
+	for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) {
+		/* This is where to make the change */
+		location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr + rel[i].r_offset;
+		/* This is the symbol it is referring to */
+		sym = (Elf_Sym *)sechdrs[symindex].sh_addr + ELF_R_SYM(rel[i].r_info);
+		if (IS_ERR_VALUE(sym->st_value)) {
+			/* Ignore unresolved weak symbol */
+			if (ELF_ST_BIND(sym->st_info) == STB_WEAK)
+				continue;
+			pr_warn("%s: Unknown symbol %s\n", me->name, strtab + sym->st_name);
+			return -ENOENT;
+		}
+
+		type = ELF_R_TYPE(rel[i].r_info);
+
+		if (type < ARRAY_SIZE(reloc_rela_handlers))
+			handler = reloc_rela_handlers[type];
+		else
+			handler = NULL;
+
+		if (!handler) {
+			pr_err("%s: Unknown relocation type %u\n", me->name, type);
+			return -EINVAL;
+		}
+
+		pr_debug("type %d st_value %llx r_addend %llx loc %llx\n",
+		       (int)ELF64_R_TYPE(rel[i].r_info),
+		       sym->st_value, rel[i].r_addend, (u64)location);
+
+		v = sym->st_value + rel[i].r_addend;
+		err = handler(me, location, v, rela_stack, &rela_stack_top);
+		if (err)
+			return err;
+	}
+
+	return 0;
+}
-- 
2.27.0


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

* [PATCH 12/19] LoongArch: Add misc common routines
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (9 preceding siblings ...)
  2021-07-06  4:18 ` [PATCH 11/19] LoongArch: Add elf and module support Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06 10:17   ` Arnd Bergmann
  2021-07-06  4:18 ` [PATCH 13/19] LoongArch: Add some library functions Huacai Chen
                   ` (9 subsequent siblings)
  20 siblings, 1 reply; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

This patch adds some misc common routines for LoongArch, including: asm-
offsets routines, arch_hweight, cmpxchg and futex functions, i/o memory
access functions, vga/frame-buffer functions, /proc/cpuinfo display, etc.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 arch/loongarch/include/asm/arch_hweight.h |  36 ++
 arch/loongarch/include/asm/asm-offsets.h  |   5 +
 arch/loongarch/include/asm/cmpxchg.h      | 154 +++++++++
 arch/loongarch/include/asm/fb.h           |  23 ++
 arch/loongarch/include/asm/futex.h        | 107 ++++++
 arch/loongarch/include/asm/io.h           | 385 ++++++++++++++++++++++
 arch/loongarch/include/asm/vga.h          |  56 ++++
 arch/loongarch/include/uapi/asm/swab.h    |  52 +++
 arch/loongarch/kernel/asm-offsets.c       | 260 +++++++++++++++
 arch/loongarch/kernel/cmpxchg.c           | 100 ++++++
 arch/loongarch/kernel/io.c                |  94 ++++++
 arch/loongarch/kernel/proc.c              | 122 +++++++
 12 files changed, 1394 insertions(+)
 create mode 100644 arch/loongarch/include/asm/arch_hweight.h
 create mode 100644 arch/loongarch/include/asm/asm-offsets.h
 create mode 100644 arch/loongarch/include/asm/cmpxchg.h
 create mode 100644 arch/loongarch/include/asm/fb.h
 create mode 100644 arch/loongarch/include/asm/futex.h
 create mode 100644 arch/loongarch/include/asm/io.h
 create mode 100644 arch/loongarch/include/asm/vga.h
 create mode 100644 arch/loongarch/include/uapi/asm/swab.h
 create mode 100644 arch/loongarch/kernel/asm-offsets.c
 create mode 100644 arch/loongarch/kernel/cmpxchg.c
 create mode 100644 arch/loongarch/kernel/io.c
 create mode 100644 arch/loongarch/kernel/proc.c

diff --git a/arch/loongarch/include/asm/arch_hweight.h b/arch/loongarch/include/asm/arch_hweight.h
new file mode 100644
index 000000000000..e790af4e6ee8
--- /dev/null
+++ b/arch/loongarch/include/asm/arch_hweight.h
@@ -0,0 +1,36 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_ARCH_HWEIGHT_H
+#define _ASM_ARCH_HWEIGHT_H
+
+#ifdef ARCH_HAS_USABLE_BUILTIN_POPCOUNT
+
+#include <asm/types.h>
+
+static inline unsigned int __arch_hweight32(unsigned int w)
+{
+	return __builtin_popcount(w);
+}
+
+static inline unsigned int __arch_hweight16(unsigned int w)
+{
+	return __builtin_popcount(w & 0xffff);
+}
+
+static inline unsigned int __arch_hweight8(unsigned int w)
+{
+	return __builtin_popcount(w & 0xff);
+}
+
+static inline unsigned long __arch_hweight64(__u64 w)
+{
+	return __builtin_popcountll(w);
+}
+
+#else
+#include <asm-generic/bitops/arch_hweight.h>
+#endif
+
+#endif /* _ASM_ARCH_HWEIGHT_H */
diff --git a/arch/loongarch/include/asm/asm-offsets.h b/arch/loongarch/include/asm/asm-offsets.h
new file mode 100644
index 000000000000..cf9ab9d12183
--- /dev/null
+++ b/arch/loongarch/include/asm/asm-offsets.h
@@ -0,0 +1,5 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <generated/asm-offsets.h>
diff --git a/arch/loongarch/include/asm/cmpxchg.h b/arch/loongarch/include/asm/cmpxchg.h
new file mode 100644
index 000000000000..a8adaacc73cc
--- /dev/null
+++ b/arch/loongarch/include/asm/cmpxchg.h
@@ -0,0 +1,154 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef __ASM_CMPXCHG_H
+#define __ASM_CMPXCHG_H
+
+#include <linux/bug.h>
+#include <asm/compiler.h>
+
+/*
+ * These functions doesn't exist, so if they are called you'll either:
+ *
+ * - Get an error at compile-time due to __compiletime_error, if supported by
+ *   your compiler.
+ *
+ * or:
+ *
+ * - Get an error at link-time due to the call to the missing function.
+ */
+extern unsigned long __cmpxchg_called_with_bad_pointer(void)
+	__compiletime_error("Bad argument size for cmpxchg");
+extern unsigned long __xchg_called_with_bad_pointer(void)
+	__compiletime_error("Bad argument size for xchg");
+
+#define __xchg_asm(amswap_db, m, val)		\
+({						\
+		__typeof(val) __ret;		\
+						\
+		__asm__ __volatile__ (		\
+		" "amswap_db" %1, %z2, %0 \n"	\
+		: "+ZB" (*m), "=&r" (__ret)	\
+		: "Jr" (val)			\
+		: "memory");			\
+						\
+		__ret;				\
+})
+
+extern unsigned long __xchg_small(volatile void *ptr, unsigned long val,
+				  unsigned int size);
+
+static inline unsigned long __xchg(volatile void *ptr, unsigned long x,
+				   int size)
+{
+	switch (size) {
+	case 1:
+	case 2:
+		return __xchg_small(ptr, x, size);
+
+	case 4:
+		return __xchg_asm("amswap_db.w", (volatile u32 *)ptr, (u32)x);
+
+	case 8:
+		if (!IS_ENABLED(CONFIG_64BIT))
+			return __xchg_called_with_bad_pointer();
+
+		return __xchg_asm("amswap_db.d", (volatile u64 *)ptr, (u64)x);
+
+	default:
+		return __xchg_called_with_bad_pointer();
+	}
+}
+
+#define arch_xchg(ptr, x)							\
+({									\
+	__typeof__(*(ptr)) __res;					\
+									\
+	__res = (__typeof__(*(ptr)))					\
+		__xchg((ptr), (unsigned long)(x), sizeof(*(ptr)));	\
+									\
+	__res;								\
+})
+
+#define __cmpxchg_asm(ld, st, m, old, new)				\
+({									\
+	__typeof(old) __ret;						\
+									\
+	__asm__ __volatile__(						\
+	"1:	" ld "	%0, %2		# __cmpxchg_asm \n"		\
+	"	bne	%0, %z3, 2f			\n"		\
+	"	or	$t0, %z4, $zero			\n"		\
+	"	" st "	$t0, %1				\n"		\
+	"	beq	$zero, $t0, 1b			\n"		\
+	"2:						\n"		\
+	: "=&r" (__ret), "=ZB"(*m)					\
+	: "ZB"(*m), "Jr" (old), "Jr" (new)				\
+	: "t0", "memory");						\
+									\
+	__ret;								\
+})
+
+extern unsigned long __cmpxchg_small(volatile void *ptr, unsigned long old,
+				     unsigned long new, unsigned int size);
+
+static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
+				      unsigned long new, unsigned int size)
+{
+	switch (size) {
+	case 1:
+	case 2:
+		return __cmpxchg_small(ptr, old, new, size);
+
+	case 4:
+		return __cmpxchg_asm("ll.w", "sc.w", (volatile u32 *)ptr,
+				     (u32)old, new);
+
+	case 8:
+		/* lld/scd are only available for LoongArch64 */
+		if (!IS_ENABLED(CONFIG_64BIT))
+			return __cmpxchg_called_with_bad_pointer();
+
+		return __cmpxchg_asm("ll.d", "sc.d", (volatile u64 *)ptr,
+				     (u64)old, new);
+
+	default:
+		return __cmpxchg_called_with_bad_pointer();
+	}
+}
+
+#define arch_cmpxchg_local(ptr, old, new)					\
+	((__typeof__(*(ptr)))						\
+		__cmpxchg((ptr),					\
+			  (unsigned long)(__typeof__(*(ptr)))(old),	\
+			  (unsigned long)(__typeof__(*(ptr)))(new),	\
+			  sizeof(*(ptr))))
+
+#define arch_cmpxchg(ptr, old, new)						\
+({									\
+	__typeof__(*(ptr)) __res;					\
+									\
+	__res = arch_cmpxchg_local((ptr), (old), (new));			\
+									\
+	__res;								\
+})
+
+#ifdef CONFIG_64BIT
+#define arch_cmpxchg64_local(ptr, o, n)					\
+  ({									\
+	BUILD_BUG_ON(sizeof(*(ptr)) != 8);				\
+	arch_cmpxchg_local((ptr), (o), (n));					\
+  })
+
+#define arch_cmpxchg64(ptr, o, n)						\
+  ({									\
+	BUILD_BUG_ON(sizeof(*(ptr)) != 8);				\
+	arch_cmpxchg((ptr), (o), (n));					\
+  })
+#else
+#include <asm-generic/cmpxchg-local.h>
+#define arch_cmpxchg64_local(ptr, o, n) __generic_cmpxchg64_local((ptr), (o), (n))
+#define arch_cmpxchg64(ptr, o, n) arch_cmpxchg64_local((ptr), (o), (n))
+#endif
+
+#endif /* __ASM_CMPXCHG_H */
diff --git a/arch/loongarch/include/asm/fb.h b/arch/loongarch/include/asm/fb.h
new file mode 100644
index 000000000000..b29566fb2b1b
--- /dev/null
+++ b/arch/loongarch/include/asm/fb.h
@@ -0,0 +1,23 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_FB_H_
+#define _ASM_FB_H_
+
+#include <linux/fb.h>
+#include <linux/fs.h>
+#include <asm/page.h>
+
+static inline void fb_pgprotect(struct file *file, struct vm_area_struct *vma,
+				unsigned long off)
+{
+	vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
+}
+
+static inline int fb_is_primary_device(struct fb_info *info)
+{
+	return 0;
+}
+
+#endif /* _ASM_FB_H_ */
diff --git a/arch/loongarch/include/asm/futex.h b/arch/loongarch/include/asm/futex.h
new file mode 100644
index 000000000000..6512feb041d1
--- /dev/null
+++ b/arch/loongarch/include/asm/futex.h
@@ -0,0 +1,107 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_FUTEX_H
+#define _ASM_FUTEX_H
+
+#include <linux/futex.h>
+#include <linux/uaccess.h>
+#include <asm/barrier.h>
+#include <asm/compiler.h>
+#include <asm/errno.h>
+
+#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg)		\
+{									\
+	__asm__ __volatile__(						\
+	"1:	ll.w	%1, %4 # __futex_atomic_op\n"		\
+	"	" insn	"				\n"	\
+	"2:	sc.w	$t0, %2				\n"	\
+	"	beq	$t0, $zero, 1b			\n"	\
+	"3:						\n"	\
+	"	.section .fixup,\"ax\"			\n"	\
+	"4:	li.w	%0, %6				\n"	\
+	"	b	3b				\n"	\
+	"	.previous				\n"	\
+	"	.section __ex_table,\"a\"		\n"	\
+	"	"__UA_ADDR "\t1b, 4b			\n"	\
+	"	"__UA_ADDR "\t2b, 4b			\n"	\
+	"	.previous				\n"	\
+	: "=r" (ret), "=&r" (oldval),				\
+	  "=ZC" (*uaddr)					\
+	: "0" (0), "ZC" (*uaddr), "Jr" (oparg),			\
+	  "i" (-EFAULT)						\
+	: "memory", "t0");					\
+}
+
+static inline int
+arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
+{
+	int oldval = 0, ret = 0;
+
+	pagefault_disable();
+
+	switch (op) {
+	case FUTEX_OP_SET:
+		__futex_atomic_op("move $t0, %z5", ret, oldval, uaddr, oparg);
+		break;
+	case FUTEX_OP_ADD:
+		__futex_atomic_op("add.w $t0, %1, %z5", ret, oldval, uaddr, oparg);
+		break;
+	case FUTEX_OP_OR:
+		__futex_atomic_op("or	$t0, %1, %z5", ret, oldval, uaddr, oparg);
+		break;
+	case FUTEX_OP_ANDN:
+		__futex_atomic_op("and	$t0, %1, %z5", ret, oldval, uaddr, ~oparg);
+		break;
+	case FUTEX_OP_XOR:
+		__futex_atomic_op("xor	$t0, %1, %z5", ret, oldval, uaddr, oparg);
+		break;
+	default:
+		ret = -ENOSYS;
+	}
+
+	pagefault_enable();
+
+	if (!ret)
+		*oval = oldval;
+
+	return ret;
+}
+
+static inline int
+futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32 newval)
+{
+	int ret = 0;
+	u32 val = 0;
+
+	if (!access_ok(uaddr, sizeof(u32)))
+		return -EFAULT;
+
+	__asm__ __volatile__(
+	"# futex_atomic_cmpxchg_inatomic			\n"
+	"1:	ll.w	%1, %3					\n"
+	"	bne	%1, %z4, 3f				\n"
+	"	or	$t0, %z5, $zero				\n"
+	"2:	sc.w	$t0, %2					\n"
+	"	beq	$zero, $t0, 1b				\n"
+	"3:							\n"
+	"	.section .fixup,\"ax\"				\n"
+	"4:	li.d	%0, %6					\n"
+	"	b	3b					\n"
+	"	.previous					\n"
+	"	.section __ex_table,\"a\"			\n"
+	"	"__UA_ADDR "\t1b, 4b				\n"
+	"	"__UA_ADDR "\t2b, 4b				\n"
+	"	.previous					\n"
+	: "+r" (ret), "=&r" (val), "=" GCC_OFF_SMALL_ASM() (*uaddr)
+	: GCC_OFF_SMALL_ASM() (*uaddr), "Jr" (oldval), "Jr" (newval),
+	  "i" (-EFAULT)
+	: "memory", "t0");
+
+	*uval = val;
+
+	return ret;
+}
+
+#endif /* _ASM_FUTEX_H */
diff --git a/arch/loongarch/include/asm/io.h b/arch/loongarch/include/asm/io.h
new file mode 100644
index 000000000000..8fa7c99db370
--- /dev/null
+++ b/arch/loongarch/include/asm/io.h
@@ -0,0 +1,385 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_IO_H
+#define _ASM_IO_H
+
+#define ARCH_HAS_IOREMAP_WC
+
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/types.h>
+
+#include <asm/addrspace.h>
+#include <asm/bug.h>
+#include <asm/byteorder.h>
+#include <asm/cpu.h>
+#include <asm-generic/iomap.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/string.h>
+
+#define ioswabb(a, x)		(x)
+#define ioswabw(a, x)		(x)
+#define ioswabl(a, x)		(x)
+#define ioswabq(a, x)		(x)
+#define __mem_ioswabb(a, x)	(x)
+#define __mem_ioswabw(a, x)	(x)
+#define __mem_ioswabl(a, x)	(x)
+#define __mem_ioswabq(a, x)	(x)
+#define __raw_ioswabb(a, x)	(x)
+#define __raw_ioswabw(a, x)	(x)
+#define __raw_ioswabl(a, x)	(x)
+#define __raw_ioswabq(a, x)	(x)
+
+#define IO_SPACE_LIMIT 0xffff
+
+/*
+ * On LoongArch I/O ports are memory mapped, so we access them using normal
+ * load/store instructions. loongarch_io_port_base is the virtual address to
+ * which all ports are being mapped.  For sake of efficiency some code
+ * assumes that this is an address that can be loaded with a single lui
+ * instruction, so the lower 16 bits must be zero. Should be true on any
+ * sane architecture; generic code does not use this assumption.
+ */
+extern unsigned long loongarch_io_port_base;
+
+static inline void set_io_port_base(unsigned long base)
+{
+	loongarch_io_port_base = base;
+}
+
+/*
+ * Provide the necessary definitions for generic iomap. We make use of
+ * loongarch_io_port_base for iomap(), but we don't reserve any low addresses
+ * for use with I/O ports.
+ */
+
+#define HAVE_ARCH_PIO_SIZE
+#define PIO_OFFSET	loongarch_io_port_base
+#define PIO_MASK	IO_SPACE_LIMIT
+#define PIO_RESERVED	0x0UL
+
+/*
+ * ISA I/O bus memory addresses are 1:1 with the physical address.
+ */
+static inline unsigned long isa_virt_to_bus(volatile void *address)
+{
+	return virt_to_phys(address);
+}
+
+static inline void *isa_bus_to_virt(unsigned long address)
+{
+	return phys_to_virt(address);
+}
+
+/*
+ * However PCI ones are not necessarily 1:1 and therefore these interfaces
+ * are forbidden in portable PCI drivers.
+ *
+ * Allow them for x86 for legacy drivers, though.
+ */
+#define virt_to_bus virt_to_phys
+#define bus_to_virt phys_to_virt
+
+/*
+ * Change "struct page" to physical address.
+ */
+#define page_to_phys(page)	((dma_addr_t)page_to_pfn(page) << PAGE_SHIFT)
+
+extern void __init __iomem *early_ioremap(u64 phys_addr, unsigned long size);
+extern void __init early_iounmap(void __iomem *addr, unsigned long size);
+
+#define early_memremap early_ioremap
+#define early_memunmap early_iounmap
+
+static inline void __iomem *ioremap_prot(phys_addr_t offset, unsigned long size,
+	unsigned long prot_val)
+{
+	/* This only works for !HIGHMEM currently */
+	return (void __iomem *)(unsigned long)(IO_BASE + offset);
+}
+
+/*
+ * ioremap     -   map bus memory into CPU space
+ * @offset:    bus address of the memory
+ * @size:      size of the resource to map
+ *
+ * ioremap performs a platform specific sequence of operations to
+ * make bus memory CPU accessible via the readb/readw/readl/writeb/
+ * writew/writel functions and the other mmio helpers. The returned
+ * address is not guaranteed to be usable directly as a virtual
+ * address.
+ */
+#define ioremap(offset, size)					\
+	ioremap_prot((offset), (size), _CACHE_SUC)
+#define ioremap_uc ioremap
+
+/*
+ * ioremap_cache -	map bus memory into CPU space
+ * @offset:	    bus address of the memory
+ * @size:	    size of the resource to map
+ *
+ * ioremap_cache performs a platform specific sequence of operations to
+ * make bus memory CPU accessible via the readb/readw/readl/writeb/
+ * writew/writel functions and the other mmio helpers. The returned
+ * address is not guaranteed to be usable directly as a virtual
+ * address.
+ *
+ * This version of ioremap ensures that the memory is marked cachable by
+ * the CPU.  Also enables full write-combining.	 Useful for some
+ * memory-like regions on I/O busses.
+ */
+#define ioremap_cache(offset, size)				\
+	ioremap_prot((offset), (size), _CACHE_CC)
+
+/*
+ * ioremap_wc     -   map bus memory into CPU space
+ * @offset:    bus address of the memory
+ * @size:      size of the resource to map
+ *
+ * ioremap_wc performs a platform specific sequence of operations to
+ * make bus memory CPU accessible via the readb/readw/readl/writeb/
+ * writew/writel functions and the other mmio helpers. The returned
+ * address is not guaranteed to be usable directly as a virtual
+ * address.
+ *
+ * This version of ioremap ensures that the memory is marked uncachable
+ * but accelerated by means of write-combining feature. It is specifically
+ * useful for PCIe prefetchable windows, which may vastly improve a
+ * communications performance. If it was determined on boot stage, what
+ * CPU CCA doesn't support WUC, the method shall fall-back to the
+ * _CACHE_SUC option (see cpu_probe() method).
+ */
+#define ioremap_wc(offset, size)				\
+	ioremap_prot((offset), (size), boot_cpu_data.writecombine)
+
+static inline void iounmap(const volatile void __iomem *addr)
+{
+}
+
+#define io_reorder_wmb()		wmb()
+
+#define __BUILD_MEMORY_SINGLE(pfx, bwlq, type)				\
+									\
+static inline void pfx##write##bwlq(type val,				\
+				    volatile void __iomem *mem)		\
+{									\
+	volatile type *__mem;						\
+	type __val;							\
+									\
+	io_reorder_wmb();						\
+									\
+	__mem = (void *)(unsigned long)(mem);				\
+									\
+	__val = pfx##ioswab##bwlq(__mem, val);				\
+									\
+	if (sizeof(type) != sizeof(u64) || sizeof(u64) == sizeof(long)) \
+		*__mem = __val;						\
+	else								\
+		BUG();							\
+}									\
+									\
+static inline type pfx##read##bwlq(const volatile void __iomem *mem)	\
+{									\
+	volatile type *__mem;						\
+	type __val;							\
+									\
+	__mem = (void *)(unsigned long)(mem);				\
+									\
+	if (sizeof(type) != sizeof(u64) || sizeof(u64) == sizeof(long)) \
+		__val = *__mem;						\
+	else {								\
+		__val = 0;						\
+		BUG();							\
+	}								\
+									\
+	/* prevent prefetching of coherent DMA data prematurely */	\
+	rmb();								\
+	return pfx##ioswab##bwlq(__mem, __val);				\
+}
+
+#define __BUILD_IOPORT_SINGLE(pfx, bwlq, type, p)			\
+									\
+static inline void pfx##out##bwlq##p(type val, unsigned long port)	\
+{									\
+	volatile type *__addr;						\
+	type __val;							\
+									\
+	io_reorder_wmb();						\
+									\
+	__addr = (void *)(loongarch_io_port_base + port);		\
+									\
+	__val = pfx##ioswab##bwlq(__addr, val);				\
+									\
+	/* Really, we want this to be atomic */				\
+	BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long));		\
+									\
+	*__addr = __val;						\
+}									\
+									\
+static inline type pfx##in##bwlq##p(unsigned long port)			\
+{									\
+	volatile type *__addr;						\
+	type __val;							\
+									\
+	__addr = (void *)(loongarch_io_port_base + port);		\
+									\
+	BUILD_BUG_ON(sizeof(type) > sizeof(unsigned long));		\
+									\
+	__val = *__addr;						\
+									\
+	/* prevent prefetching of coherent DMA data prematurely */	\
+	rmb();								\
+	return pfx##ioswab##bwlq(__addr, __val);			\
+}
+
+#define __BUILD_MEMORY_PFX(bus, bwlq, type)				\
+									\
+__BUILD_MEMORY_SINGLE(bus, bwlq, type)
+
+#define BUILDIO_MEM(bwlq, type)						\
+									\
+__BUILD_MEMORY_PFX(__raw_, bwlq, type)					\
+__BUILD_MEMORY_PFX(, bwlq, type)					\
+__BUILD_MEMORY_PFX(__mem_, bwlq, type)					\
+
+BUILDIO_MEM(b, u8)
+BUILDIO_MEM(w, u16)
+BUILDIO_MEM(l, u32)
+BUILDIO_MEM(q, u64)
+
+#define __BUILD_IOPORT_PFX(bus, bwlq, type)				\
+	__BUILD_IOPORT_SINGLE(bus, bwlq, type,)				\
+	__BUILD_IOPORT_SINGLE(bus, bwlq, type, _p)
+
+#define BUILDIO_IOPORT(bwlq, type)					\
+	__BUILD_IOPORT_PFX(, bwlq, type)				\
+	__BUILD_IOPORT_PFX(__mem_, bwlq, type)
+
+BUILDIO_IOPORT(b, u8)
+BUILDIO_IOPORT(w, u16)
+BUILDIO_IOPORT(l, u32)
+#ifdef CONFIG_64BIT
+BUILDIO_IOPORT(q, u64)
+#endif
+
+#define readb_relaxed			readb
+#define readw_relaxed			readw
+#define readl_relaxed			readl
+#define readq_relaxed			readq
+
+#define writeb_relaxed			writeb
+#define writew_relaxed			writew
+#define writel_relaxed			writel
+#define writeq_relaxed			writeq
+
+#define readb_be(addr)							\
+	__raw_readb((__force unsigned *)(addr))
+#define readw_be(addr)							\
+	be16_to_cpu(__raw_readw((__force unsigned *)(addr)))
+#define readl_be(addr)							\
+	be32_to_cpu(__raw_readl((__force unsigned *)(addr)))
+#define readq_be(addr)							\
+	be64_to_cpu(__raw_readq((__force unsigned *)(addr)))
+
+#define writeb_be(val, addr)						\
+	__raw_writeb((val), (__force unsigned *)(addr))
+#define writew_be(val, addr)						\
+	__raw_writew(cpu_to_be16((val)), (__force unsigned *)(addr))
+#define writel_be(val, addr)						\
+	__raw_writel(cpu_to_be32((val)), (__force unsigned *)(addr))
+#define writeq_be(val, addr)						\
+	__raw_writeq(cpu_to_be64((val)), (__force unsigned *)(addr))
+
+/*
+ * Some code tests for these symbols
+ */
+#define readq				readq
+#define writeq				writeq
+
+#define __BUILD_MEMORY_STRING(bwlq, type)				\
+									\
+static inline void writes##bwlq(volatile void __iomem *mem,		\
+				const void *addr, unsigned int count)	\
+{									\
+	const volatile type *__addr = addr;				\
+									\
+	while (count--) {						\
+		__mem_write##bwlq(*__addr, mem);			\
+		__addr++;						\
+	}								\
+}									\
+									\
+static inline void reads##bwlq(volatile void __iomem *mem, void *addr,	\
+			       unsigned int count)			\
+{									\
+	volatile type *__addr = addr;					\
+									\
+	while (count--) {						\
+		*__addr = __mem_read##bwlq(mem);			\
+		__addr++;						\
+	}								\
+}
+
+#define __BUILD_IOPORT_STRING(bwlq, type)				\
+									\
+static inline void outs##bwlq(unsigned long port, const void *addr,	\
+			      unsigned int count)			\
+{									\
+	const volatile type *__addr = addr;				\
+									\
+	while (count--) {						\
+		__mem_out##bwlq(*__addr, port);				\
+		__addr++;						\
+	}								\
+}									\
+									\
+static inline void ins##bwlq(unsigned long port, void *addr,		\
+			     unsigned int count)			\
+{									\
+	volatile type *__addr = addr;					\
+									\
+	while (count--) {						\
+		*__addr = __mem_in##bwlq(port);				\
+		__addr++;						\
+	}								\
+}
+
+#define BUILDSTRING(bwlq, type)						\
+									\
+__BUILD_MEMORY_STRING(bwlq, type)					\
+__BUILD_IOPORT_STRING(bwlq, type)
+
+BUILDSTRING(b, u8)
+BUILDSTRING(w, u16)
+BUILDSTRING(l, u32)
+#ifdef CONFIG_64BIT
+BUILDSTRING(q, u64)
+#endif
+
+#define mmiowb() asm volatile ("dbar 0" ::: "memory")
+
+/*
+ * String version of I/O memory access operations.
+ */
+extern void __memset_io(volatile void __iomem *dst, int c, size_t count);
+extern void __memcpy_toio(volatile void __iomem *to, const void *from, size_t count);
+extern void __memcpy_fromio(void *to, const volatile void __iomem *from, size_t count);
+#define memset_io(c, v, l)     __memset_io((c), (v), (l))
+#define memcpy_fromio(a, c, l) __memcpy_fromio((a), (c), (l))
+#define memcpy_toio(c, a, l)   __memcpy_toio((c), (a), (l))
+
+/*
+ * Convert a physical pointer to a virtual kernel pointer for /dev/mem
+ * access
+ */
+#define xlate_dev_mem_ptr(p)	__va(p)
+
+/*
+ * Convert a virtual cached pointer to an uncached pointer
+ */
+#define xlate_dev_kmem_ptr(p)	p
+
+#endif /* _ASM_IO_H */
diff --git a/arch/loongarch/include/asm/vga.h b/arch/loongarch/include/asm/vga.h
new file mode 100644
index 000000000000..eef95f2f837a
--- /dev/null
+++ b/arch/loongarch/include/asm/vga.h
@@ -0,0 +1,56 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Access to VGA videoram
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_VGA_H
+#define _ASM_VGA_H
+
+#include <linux/string.h>
+#include <asm/addrspace.h>
+#include <asm/byteorder.h>
+
+/*
+ *	On the PC, we can just recalculate addresses and then
+ *	access the videoram directly without any black magic.
+ */
+
+#define VGA_MAP_MEM(x, s)	TO_UNCAC(0x10000000L + (unsigned long)(x))
+
+#define vga_readb(x)	(*(x))
+#define vga_writeb(x, y)	(*(y) = (x))
+
+#define VT_BUF_HAVE_RW
+/*
+ *  These are only needed for supporting VGA or MDA text mode, which use little
+ *  endian byte ordering.
+ *  In other cases, we can optimize by using native byte ordering and
+ *  <linux/vt_buffer.h> has already done the right job for us.
+ */
+
+#undef scr_writew
+#undef scr_readw
+
+static inline void scr_writew(u16 val, volatile u16 *addr)
+{
+	*addr = cpu_to_le16(val);
+}
+
+static inline u16 scr_readw(volatile const u16 *addr)
+{
+	return le16_to_cpu(*addr);
+}
+
+static inline void scr_memsetw(u16 *s, u16 v, unsigned int count)
+{
+	memset16(s, cpu_to_le16(v), count / 2);
+}
+
+#define scr_memcpyw(d, s, c) memcpy(d, s, c)
+#define scr_memmovew(d, s, c) memmove(d, s, c)
+#define VT_BUF_HAVE_MEMCPYW
+#define VT_BUF_HAVE_MEMMOVEW
+#define VT_BUF_HAVE_MEMSETW
+
+#endif /* _ASM_VGA_H */
diff --git a/arch/loongarch/include/uapi/asm/swab.h b/arch/loongarch/include/uapi/asm/swab.h
new file mode 100644
index 000000000000..48db78a5f474
--- /dev/null
+++ b/arch/loongarch/include/uapi/asm/swab.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+/*
+ * Authors: Jun Yi <yijun@loongson.cn>
+ *          Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_SWAB_H
+#define _ASM_SWAB_H
+
+#include <linux/compiler.h>
+#include <linux/types.h>
+
+#define __SWAB_64_THRU_32__
+
+static inline __attribute_const__ __u16 __arch_swab16(__u16 x)
+{
+	__asm__(
+	"	revb.2h	%0, %1			\n"
+	: "=r" (x)
+	: "r" (x));
+
+	return x;
+}
+#define __arch_swab16 __arch_swab16
+
+static inline __attribute_const__ __u32 __arch_swab32(__u32 x)
+{
+	__asm__(
+	"	revb.2h	%0, %1			\n"
+	"	rotri.w	%0, %0, 16		\n"
+	: "=r" (x)
+	: "r" (x));
+
+	return x;
+}
+#define __arch_swab32 __arch_swab32
+
+#ifdef __loongarch64
+static inline __attribute_const__ __u64 __arch_swab64(__u64 x)
+{
+	__asm__(
+	"	revb.4h	%0, %1			\n"
+	"	revh.d	%0, %0			\n"
+	: "=r" (x)
+	: "r" (x));
+
+	return x;
+}
+#define __arch_swab64 __arch_swab64
+#endif /* __loongarch64 */
+#endif /* _ASM_SWAB_H */
diff --git a/arch/loongarch/kernel/asm-offsets.c b/arch/loongarch/kernel/asm-offsets.c
new file mode 100644
index 000000000000..b4efe336616b
--- /dev/null
+++ b/arch/loongarch/kernel/asm-offsets.c
@@ -0,0 +1,260 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * asm-offsets.c: Calculate pt_regs and task_struct offsets.
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/compat.h>
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/kbuild.h>
+#include <linux/suspend.h>
+#include <asm/cpu-info.h>
+#include <asm/ptrace.h>
+#include <asm/processor.h>
+
+void output_ptreg_defines(void)
+{
+	COMMENT("LoongArch pt_regs offsets.");
+	OFFSET(PT_R0, pt_regs, regs[0]);
+	OFFSET(PT_R1, pt_regs, regs[1]);
+	OFFSET(PT_R2, pt_regs, regs[2]);
+	OFFSET(PT_R3, pt_regs, regs[3]);
+	OFFSET(PT_R4, pt_regs, regs[4]);
+	OFFSET(PT_R5, pt_regs, regs[5]);
+	OFFSET(PT_R6, pt_regs, regs[6]);
+	OFFSET(PT_R7, pt_regs, regs[7]);
+	OFFSET(PT_R8, pt_regs, regs[8]);
+	OFFSET(PT_R9, pt_regs, regs[9]);
+	OFFSET(PT_R10, pt_regs, regs[10]);
+	OFFSET(PT_R11, pt_regs, regs[11]);
+	OFFSET(PT_R12, pt_regs, regs[12]);
+	OFFSET(PT_R13, pt_regs, regs[13]);
+	OFFSET(PT_R14, pt_regs, regs[14]);
+	OFFSET(PT_R15, pt_regs, regs[15]);
+	OFFSET(PT_R16, pt_regs, regs[16]);
+	OFFSET(PT_R17, pt_regs, regs[17]);
+	OFFSET(PT_R18, pt_regs, regs[18]);
+	OFFSET(PT_R19, pt_regs, regs[19]);
+	OFFSET(PT_R20, pt_regs, regs[20]);
+	OFFSET(PT_R21, pt_regs, regs[21]);
+	OFFSET(PT_R22, pt_regs, regs[22]);
+	OFFSET(PT_R23, pt_regs, regs[23]);
+	OFFSET(PT_R24, pt_regs, regs[24]);
+	OFFSET(PT_R25, pt_regs, regs[25]);
+	OFFSET(PT_R26, pt_regs, regs[26]);
+	OFFSET(PT_R27, pt_regs, regs[27]);
+	OFFSET(PT_R28, pt_regs, regs[28]);
+	OFFSET(PT_R29, pt_regs, regs[29]);
+	OFFSET(PT_R30, pt_regs, regs[30]);
+	OFFSET(PT_R31, pt_regs, regs[31]);
+	OFFSET(PT_CRMD, pt_regs, csr_crmd);
+	OFFSET(PT_PRMD, pt_regs, csr_prmd);
+	OFFSET(PT_EUEN, pt_regs, csr_euen);
+	OFFSET(PT_ECFG, pt_regs, csr_ecfg);
+	OFFSET(PT_ESTAT, pt_regs, csr_estat);
+	OFFSET(PT_EPC, pt_regs, csr_epc);
+	OFFSET(PT_BVADDR, pt_regs, csr_badvaddr);
+	OFFSET(PT_ORIG_A0, pt_regs, orig_a0);
+	DEFINE(PT_SIZE, sizeof(struct pt_regs));
+	BLANK();
+}
+
+void output_task_defines(void)
+{
+	COMMENT("LoongArch task_struct offsets.");
+	OFFSET(TASK_STATE, task_struct, __state);
+	OFFSET(TASK_THREAD_INFO, task_struct, stack);
+	OFFSET(TASK_FLAGS, task_struct, flags);
+	OFFSET(TASK_MM, task_struct, mm);
+	OFFSET(TASK_PID, task_struct, pid);
+	DEFINE(TASK_STRUCT_SIZE, sizeof(struct task_struct));
+	BLANK();
+}
+
+void output_thread_info_defines(void)
+{
+	COMMENT("LoongArch thread_info offsets.");
+	OFFSET(TI_TASK, thread_info, task);
+	OFFSET(TI_FLAGS, thread_info, flags);
+	OFFSET(TI_TP_VALUE, thread_info, tp_value);
+	OFFSET(TI_CPU, thread_info, cpu);
+	OFFSET(TI_PRE_COUNT, thread_info, preempt_count);
+	OFFSET(TI_REGS, thread_info, regs);
+	DEFINE(_THREAD_SIZE, THREAD_SIZE);
+	DEFINE(_THREAD_MASK, THREAD_MASK);
+	DEFINE(_IRQ_STACK_SIZE, IRQ_STACK_SIZE);
+	DEFINE(_IRQ_STACK_START, IRQ_STACK_START);
+	BLANK();
+}
+
+void output_thread_defines(void)
+{
+	COMMENT("LoongArch specific thread_struct offsets.");
+	OFFSET(THREAD_REG01, task_struct, thread.reg01);
+	OFFSET(THREAD_REG03, task_struct, thread.reg03);
+	OFFSET(THREAD_REG22, task_struct, thread.reg22);
+	OFFSET(THREAD_REG23, task_struct, thread.reg23);
+	OFFSET(THREAD_REG24, task_struct, thread.reg24);
+	OFFSET(THREAD_REG25, task_struct, thread.reg25);
+	OFFSET(THREAD_REG26, task_struct, thread.reg26);
+	OFFSET(THREAD_REG27, task_struct, thread.reg27);
+	OFFSET(THREAD_REG28, task_struct, thread.reg28);
+	OFFSET(THREAD_REG29, task_struct, thread.reg29);
+	OFFSET(THREAD_REG30, task_struct, thread.reg30);
+	OFFSET(THREAD_REG31, task_struct, thread.reg31);
+	OFFSET(THREAD_CSRCRMD, task_struct,
+	       thread.csr_crmd);
+	OFFSET(THREAD_CSRPRMD, task_struct,
+	       thread.csr_prmd);
+	OFFSET(THREAD_CSREUEN, task_struct,
+	       thread.csr_euen);
+	OFFSET(THREAD_CSRECFG, task_struct,
+	       thread.csr_ecfg);
+
+	OFFSET(THREAD_SCR0, task_struct, thread.scr0);
+	OFFSET(THREAD_SCR1, task_struct, thread.scr1);
+	OFFSET(THREAD_SCR2, task_struct, thread.scr2);
+	OFFSET(THREAD_SCR3, task_struct, thread.scr3);
+
+	OFFSET(THREAD_EFLAG, task_struct, thread.eflag);
+
+	OFFSET(THREAD_FPU, task_struct, thread.fpu);
+
+	OFFSET(THREAD_BVADDR, task_struct, \
+	       thread.csr_badvaddr);
+	OFFSET(THREAD_BUADDR, task_struct, \
+	       thread.csr_baduaddr);
+	OFFSET(THREAD_ECODE, task_struct, \
+	       thread.error_code);
+	OFFSET(THREAD_TRAPNO, task_struct, thread.trap_nr);
+	BLANK();
+}
+
+void output_thread_fpu_defines(void)
+{
+	OFFSET(THREAD_FPR0, loongarch_fpu, fpr[0]);
+	OFFSET(THREAD_FPR1, loongarch_fpu, fpr[1]);
+	OFFSET(THREAD_FPR2, loongarch_fpu, fpr[2]);
+	OFFSET(THREAD_FPR3, loongarch_fpu, fpr[3]);
+	OFFSET(THREAD_FPR4, loongarch_fpu, fpr[4]);
+	OFFSET(THREAD_FPR5, loongarch_fpu, fpr[5]);
+	OFFSET(THREAD_FPR6, loongarch_fpu, fpr[6]);
+	OFFSET(THREAD_FPR7, loongarch_fpu, fpr[7]);
+	OFFSET(THREAD_FPR8, loongarch_fpu, fpr[8]);
+	OFFSET(THREAD_FPR9, loongarch_fpu, fpr[9]);
+	OFFSET(THREAD_FPR10, loongarch_fpu, fpr[10]);
+	OFFSET(THREAD_FPR11, loongarch_fpu, fpr[11]);
+	OFFSET(THREAD_FPR12, loongarch_fpu, fpr[12]);
+	OFFSET(THREAD_FPR13, loongarch_fpu, fpr[13]);
+	OFFSET(THREAD_FPR14, loongarch_fpu, fpr[14]);
+	OFFSET(THREAD_FPR15, loongarch_fpu, fpr[15]);
+	OFFSET(THREAD_FPR16, loongarch_fpu, fpr[16]);
+	OFFSET(THREAD_FPR17, loongarch_fpu, fpr[17]);
+	OFFSET(THREAD_FPR18, loongarch_fpu, fpr[18]);
+	OFFSET(THREAD_FPR19, loongarch_fpu, fpr[19]);
+	OFFSET(THREAD_FPR20, loongarch_fpu, fpr[20]);
+	OFFSET(THREAD_FPR21, loongarch_fpu, fpr[21]);
+	OFFSET(THREAD_FPR22, loongarch_fpu, fpr[22]);
+	OFFSET(THREAD_FPR23, loongarch_fpu, fpr[23]);
+	OFFSET(THREAD_FPR24, loongarch_fpu, fpr[24]);
+	OFFSET(THREAD_FPR25, loongarch_fpu, fpr[25]);
+	OFFSET(THREAD_FPR26, loongarch_fpu, fpr[26]);
+	OFFSET(THREAD_FPR27, loongarch_fpu, fpr[27]);
+	OFFSET(THREAD_FPR28, loongarch_fpu, fpr[28]);
+	OFFSET(THREAD_FPR29, loongarch_fpu, fpr[29]);
+	OFFSET(THREAD_FPR30, loongarch_fpu, fpr[30]);
+	OFFSET(THREAD_FPR31, loongarch_fpu, fpr[31]);
+
+	OFFSET(THREAD_FCSR, loongarch_fpu, fcsr);
+	OFFSET(THREAD_FCC,  loongarch_fpu, fcc);
+	OFFSET(THREAD_VCSR, loongarch_fpu, vcsr);
+	BLANK();
+}
+
+void output_mm_defines(void)
+{
+	COMMENT("Size of struct page");
+	DEFINE(STRUCT_PAGE_SIZE, sizeof(struct page));
+	BLANK();
+	COMMENT("Linux mm_struct offsets.");
+	OFFSET(MM_USERS, mm_struct, mm_users);
+	OFFSET(MM_PGD, mm_struct, pgd);
+	OFFSET(MM_CONTEXT, mm_struct, context);
+	BLANK();
+	DEFINE(_PGD_T_SIZE, sizeof(pgd_t));
+	DEFINE(_PMD_T_SIZE, sizeof(pmd_t));
+	DEFINE(_PTE_T_SIZE, sizeof(pte_t));
+	BLANK();
+	DEFINE(_PGD_T_LOG2, PGD_T_LOG2);
+#ifndef __PAGETABLE_PMD_FOLDED
+	DEFINE(_PMD_T_LOG2, PMD_T_LOG2);
+#endif
+	DEFINE(_PTE_T_LOG2, PTE_T_LOG2);
+	BLANK();
+	DEFINE(_PGD_ORDER, PGD_ORDER);
+#ifndef __PAGETABLE_PMD_FOLDED
+	DEFINE(_PMD_ORDER, PMD_ORDER);
+#endif
+	DEFINE(_PTE_ORDER, PTE_ORDER);
+	BLANK();
+	DEFINE(_PMD_SHIFT, PMD_SHIFT);
+	DEFINE(_PGDIR_SHIFT, PGDIR_SHIFT);
+	BLANK();
+	DEFINE(_PTRS_PER_PGD, PTRS_PER_PGD);
+	DEFINE(_PTRS_PER_PMD, PTRS_PER_PMD);
+	DEFINE(_PTRS_PER_PTE, PTRS_PER_PTE);
+	BLANK();
+	DEFINE(_PAGE_SHIFT, PAGE_SHIFT);
+	DEFINE(_PAGE_SIZE, PAGE_SIZE);
+	BLANK();
+}
+
+#ifdef CONFIG_64BIT
+void output_sc_defines(void)
+{
+	COMMENT("Linux sigcontext offsets.");
+	OFFSET(SC_REGS, sigcontext, sc_regs);
+	OFFSET(SC_PC, sigcontext, sc_pc);
+	OFFSET(SC_FPC_CSR, sigcontext, sc_fcsr);
+	BLANK();
+}
+#endif
+
+void output_signal_defined(void)
+{
+	COMMENT("Linux signal numbers.");
+	DEFINE(_SIGHUP, SIGHUP);
+	DEFINE(_SIGINT, SIGINT);
+	DEFINE(_SIGQUIT, SIGQUIT);
+	DEFINE(_SIGILL, SIGILL);
+	DEFINE(_SIGTRAP, SIGTRAP);
+	DEFINE(_SIGIOT, SIGIOT);
+	DEFINE(_SIGABRT, SIGABRT);
+	DEFINE(_SIGFPE, SIGFPE);
+	DEFINE(_SIGKILL, SIGKILL);
+	DEFINE(_SIGBUS, SIGBUS);
+	DEFINE(_SIGSEGV, SIGSEGV);
+	DEFINE(_SIGSYS, SIGSYS);
+	DEFINE(_SIGPIPE, SIGPIPE);
+	DEFINE(_SIGALRM, SIGALRM);
+	DEFINE(_SIGTERM, SIGTERM);
+	DEFINE(_SIGUSR1, SIGUSR1);
+	DEFINE(_SIGUSR2, SIGUSR2);
+	DEFINE(_SIGCHLD, SIGCHLD);
+	DEFINE(_SIGPWR, SIGPWR);
+	DEFINE(_SIGWINCH, SIGWINCH);
+	DEFINE(_SIGURG, SIGURG);
+	DEFINE(_SIGIO, SIGIO);
+	DEFINE(_SIGSTOP, SIGSTOP);
+	DEFINE(_SIGTSTP, SIGTSTP);
+	DEFINE(_SIGCONT, SIGCONT);
+	DEFINE(_SIGTTIN, SIGTTIN);
+	DEFINE(_SIGTTOU, SIGTTOU);
+	DEFINE(_SIGVTALRM, SIGVTALRM);
+	DEFINE(_SIGPROF, SIGPROF);
+	DEFINE(_SIGXCPU, SIGXCPU);
+	DEFINE(_SIGXFSZ, SIGXFSZ);
+	BLANK();
+}
diff --git a/arch/loongarch/kernel/cmpxchg.c b/arch/loongarch/kernel/cmpxchg.c
new file mode 100644
index 000000000000..30f9f1ee4f0a
--- /dev/null
+++ b/arch/loongarch/kernel/cmpxchg.c
@@ -0,0 +1,100 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <linux/bitops.h>
+#include <asm/cmpxchg.h>
+
+unsigned long __xchg_small(volatile void *ptr, unsigned long val, unsigned int size)
+{
+	u32 old32, new32, load32, mask;
+	volatile u32 *ptr32;
+	unsigned int shift;
+
+	/* Check that ptr is naturally aligned */
+	WARN_ON((unsigned long)ptr & (size - 1));
+
+	/* Mask value to the correct size. */
+	mask = GENMASK((size * BITS_PER_BYTE) - 1, 0);
+	val &= mask;
+
+	/*
+	 * Calculate a shift & mask that correspond to the value we wish to
+	 * exchange within the naturally aligned 4 byte integerthat includes
+	 * it.
+	 */
+	shift = (unsigned long)ptr & 0x3;
+	shift *= BITS_PER_BYTE;
+	mask <<= shift;
+
+	/*
+	 * Calculate a pointer to the naturally aligned 4 byte integer that
+	 * includes our byte of interest, and load its value.
+	 */
+	ptr32 = (volatile u32 *)((unsigned long)ptr & ~0x3);
+	load32 = *ptr32;
+
+	do {
+		old32 = load32;
+		new32 = (load32 & ~mask) | (val << shift);
+		load32 = arch_cmpxchg(ptr32, old32, new32);
+	} while (load32 != old32);
+
+	return (load32 & mask) >> shift;
+}
+
+unsigned long __cmpxchg_small(volatile void *ptr, unsigned long old,
+			      unsigned long new, unsigned int size)
+{
+	u32 mask, old32, new32, load32, load;
+	volatile u32 *ptr32;
+	unsigned int shift;
+
+	/* Check that ptr is naturally aligned */
+	WARN_ON((unsigned long)ptr & (size - 1));
+
+	/* Mask inputs to the correct size. */
+	mask = GENMASK((size * BITS_PER_BYTE) - 1, 0);
+	old &= mask;
+	new &= mask;
+
+	/*
+	 * Calculate a shift & mask that correspond to the value we wish to
+	 * compare & exchange within the naturally aligned 4 byte integer
+	 * that includes it.
+	 */
+	shift = (unsigned long)ptr & 0x3;
+	shift *= BITS_PER_BYTE;
+	mask <<= shift;
+
+	/*
+	 * Calculate a pointer to the naturally aligned 4 byte integer that
+	 * includes our byte of interest, and load its value.
+	 */
+	ptr32 = (volatile u32 *)((unsigned long)ptr & ~0x3);
+	load32 = *ptr32;
+
+	while (true) {
+		/*
+		 * Ensure the byte we want to exchange matches the expected
+		 * old value, and if not then bail.
+		 */
+		load = (load32 & mask) >> shift;
+		if (load != old)
+			return load;
+
+		/*
+		 * Calculate the old & new values of the naturally aligned
+		 * 4 byte integer that include the byte we want to exchange.
+		 * Attempt to exchange the old value for the new value, and
+		 * return if we succeed.
+		 */
+		old32 = (load32 & ~mask) | (old << shift);
+		new32 = (load32 & ~mask) | (new << shift);
+		load32 = arch_cmpxchg(ptr32, old32, new32);
+		if (load32 == old32)
+			return old;
+	}
+}
diff --git a/arch/loongarch/kernel/io.c b/arch/loongarch/kernel/io.c
new file mode 100644
index 000000000000..7d5ef9e0ae8f
--- /dev/null
+++ b/arch/loongarch/kernel/io.c
@@ -0,0 +1,94 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/export.h>
+#include <linux/types.h>
+#include <linux/io.h>
+
+/*
+ * Copy data from IO memory space to "real" memory space.
+ */
+void __memcpy_fromio(void *to, const volatile void __iomem *from, size_t count)
+{
+	while (count && !IS_ALIGNED((unsigned long)from, 8)) {
+		*(u8 *)to = __raw_readb(from);
+		from++;
+		to++;
+		count--;
+	}
+
+	while (count >= 8) {
+		*(u64 *)to = __raw_readq(from);
+		from += 8;
+		to += 8;
+		count -= 8;
+	}
+
+	while (count) {
+		*(u8 *)to = __raw_readb(from);
+		from++;
+		to++;
+		count--;
+	}
+}
+EXPORT_SYMBOL(__memcpy_fromio);
+
+/*
+ * Copy data from "real" memory space to IO memory space.
+ */
+void __memcpy_toio(volatile void __iomem *to, const void *from, size_t count)
+{
+	while (count && !IS_ALIGNED((unsigned long)to, 8)) {
+		__raw_writeb(*(u8 *)from, to);
+		from++;
+		to++;
+		count--;
+	}
+
+	while (count >= 8) {
+		__raw_writeq(*(u64 *)from, to);
+		from += 8;
+		to += 8;
+		count -= 8;
+	}
+
+	while (count) {
+		__raw_writeb(*(u8 *)from, to);
+		from++;
+		to++;
+		count--;
+	}
+}
+EXPORT_SYMBOL(__memcpy_toio);
+
+/*
+ * "memset" on IO memory space.
+ */
+void __memset_io(volatile void __iomem *dst, int c, size_t count)
+{
+	u64 qc = (u8)c;
+
+	qc |= qc << 8;
+	qc |= qc << 16;
+	qc |= qc << 32;
+
+	while (count && !IS_ALIGNED((unsigned long)dst, 8)) {
+		__raw_writeb(c, dst);
+		dst++;
+		count--;
+	}
+
+	while (count >= 8) {
+		__raw_writeq(qc, dst);
+		dst += 8;
+		count -= 8;
+	}
+
+	while (count) {
+		__raw_writeb(c, dst);
+		dst++;
+		count--;
+	}
+}
+EXPORT_SYMBOL(__memset_io);
diff --git a/arch/loongarch/kernel/proc.c b/arch/loongarch/kernel/proc.c
new file mode 100644
index 000000000000..14e8fff4321e
--- /dev/null
+++ b/arch/loongarch/kernel/proc.c
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/seq_file.h>
+#include <asm/bootinfo.h>
+#include <asm/cpu.h>
+#include <asm/cpu-features.h>
+#include <asm/idle.h>
+#include <asm/processor.h>
+#include <asm/time.h>
+
+/*
+ * No lock; only written during early bootup by CPU 0.
+ */
+static RAW_NOTIFIER_HEAD(proc_cpuinfo_chain);
+
+int __ref register_proc_cpuinfo_notifier(struct notifier_block *nb)
+{
+	return raw_notifier_chain_register(&proc_cpuinfo_chain, nb);
+}
+
+int proc_cpuinfo_notifier_call_chain(unsigned long val, void *v)
+{
+	return raw_notifier_call_chain(&proc_cpuinfo_chain, val, v);
+}
+
+static int show_cpuinfo(struct seq_file *m, void *v)
+{
+	unsigned long n = (unsigned long) v - 1;
+	unsigned int version = cpu_data[n].processor_id & 0xff;
+	unsigned int fp_version = cpu_data[n].fpu_vers;
+	struct proc_cpuinfo_notifier_args proc_cpuinfo_notifier_args;
+
+	/*
+	 * For the first processor also print the system type
+	 */
+	if (n == 0)
+		seq_printf(m, "system type\t\t: %s\n", get_system_type());
+
+	seq_printf(m, "processor\t\t: %ld\n", n);
+	seq_printf(m, "package\t\t\t: %d\n", cpu_data[n].package);
+	seq_printf(m, "core\t\t\t: %d\n", cpu_core(&cpu_data[n]));
+	seq_printf(m, "cpu family\t\t: %s\n", __cpu_family[n]);
+	seq_printf(m, "model name\t\t: %s\n", __cpu_full_name[n]);
+	seq_printf(m, "CPU Revision\t\t: 0x%02x\n", version);
+	seq_printf(m, "FPU Revision\t\t: 0x%02x\n", fp_version);
+	seq_printf(m, "CPU MHz\t\t\t: %llu.%02llu\n",
+		      cpu_clock_freq / 1000000, (cpu_clock_freq / 10000) % 100);
+	seq_printf(m, "BogoMIPS\t\t: %llu.%02llu\n",
+		      (cpu_data[n].udelay_val * cpu_clock_freq / const_clock_freq) / (500000/HZ),
+		      ((cpu_data[n].udelay_val * cpu_clock_freq / const_clock_freq) / (5000/HZ)) % 100);
+	seq_printf(m, "TLB entries\t\t: %d\n", cpu_data[n].tlbsize);
+	seq_printf(m, "Address sizes\t\t: %d bits physical, %d bits virtual\n",
+		      cpu_pabits + 1, cpu_vabits + 1);
+
+	seq_printf(m, "isa\t\t\t:");
+	if (cpu_has_loongarch32)
+		seq_printf(m, "%s", " loongarch32");
+	if (cpu_has_loongarch64)
+		seq_printf(m, "%s", " loongarch64");
+	seq_printf(m, "\n");
+
+	seq_printf(m, "features\t\t:");
+	if (cpu_has_cpucfg)	seq_printf(m, "%s", " cpucfg");
+	if (cpu_has_lam)	seq_printf(m, "%s", " lam");
+	if (cpu_has_ual)	seq_printf(m, "%s", " ual");
+	if (cpu_has_fpu)	seq_printf(m, "%s", " fpu");
+	if (cpu_has_lsx)	seq_printf(m, "%s", " lsx");
+	if (cpu_has_lasx)	seq_printf(m, "%s", " lasx");
+	if (cpu_has_complex)	seq_printf(m, "%s", " complex");
+	if (cpu_has_crypto)	seq_printf(m, "%s", " crypto");
+	if (cpu_has_lvz)	seq_printf(m, "%s", " lvz");
+	if (cpu_has_lbt_x86)	seq_printf(m, "%s", " lbt_x86");
+	if (cpu_has_lbt_arm)	seq_printf(m, "%s", " lbt_arm");
+	if (cpu_has_lbt_mips)	seq_printf(m, "%s", " lbt_mips");
+	seq_printf(m, "\n");
+
+	seq_printf(m, "hardware watchpoint\t: %s",
+		      cpu_has_watch ? "yes, " : "no\n");
+	if (cpu_has_watch) {
+		seq_printf(m, "iwatch count: %d, dwatch count: %d\n",
+		      cpu_data[n].watch_ireg_count, cpu_data[n].watch_dreg_count);
+	}
+
+	proc_cpuinfo_notifier_args.m = m;
+	proc_cpuinfo_notifier_args.n = n;
+
+	raw_notifier_call_chain(&proc_cpuinfo_chain, 0,
+				&proc_cpuinfo_notifier_args);
+
+	seq_printf(m, "\n");
+
+	return 0;
+}
+
+static void *c_start(struct seq_file *m, loff_t *pos)
+{
+	unsigned long i = *pos;
+
+	return i < NR_CPUS ? (void *)(i + 1) : NULL;
+}
+
+static void *c_next(struct seq_file *m, void *v, loff_t *pos)
+{
+	++*pos;
+	return c_start(m, pos);
+}
+
+static void c_stop(struct seq_file *m, void *v)
+{
+}
+
+const struct seq_operations cpuinfo_op = {
+	.start	= c_start,
+	.next	= c_next,
+	.stop	= c_stop,
+	.show	= show_cpuinfo,
+};
-- 
2.27.0

diff --git a/arch/loongarch/include/asm/cmpxchg.h b/arch/loongarch/include/asm/cmpxchg.h
index 97356f984e6e..77a21dc6def1 100644
--- a/arch/loongarch/include/asm/cmpxchg.h
+++ b/arch/loongarch/include/asm/cmpxchg.h
@@ -6,6 +6,7 @@
 #define __ASM_CMPXCHG_H
 
 #include <linux/bug.h>
+#include <asm/barrier.h>
 #include <asm/compiler.h>
 
 /*

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

* [PATCH 13/19] LoongArch: Add some library functions
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (10 preceding siblings ...)
  2021-07-06  4:18 ` [PATCH 12/19] LoongArch: Add misc common routines Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06 10:17   ` Arnd Bergmann
  2021-07-06  4:18 ` [PATCH 14/19] LoongArch: Add 64-bit Loongson platform Huacai Chen
                   ` (8 subsequent siblings)
  20 siblings, 1 reply; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

This patch adds some library functions for LoongArch, including: delay,
memset, memcpy, memmove, copy_user, strncpy_user, strnlen_user and tlb
dump functions.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 arch/loongarch/include/asm/delay.h  |  26 +++++++
 arch/loongarch/include/asm/string.h |  17 +++++
 arch/loongarch/lib/Makefile         |   4 +-
 arch/loongarch/lib/clear_user.S     |  41 +++++++++++
 arch/loongarch/lib/copy_user.S      |  45 ++++++++++++
 arch/loongarch/lib/delay.c          |  47 ++++++++++++
 arch/loongarch/lib/dump_tlb.c       | 107 ++++++++++++++++++++++++++++
 arch/loongarch/lib/memcpy.S         |  32 +++++++++
 arch/loongarch/lib/memmove.S        |  45 ++++++++++++
 arch/loongarch/lib/memset.S         |  30 ++++++++
 arch/loongarch/lib/strncpy_user.S   |  51 +++++++++++++
 arch/loongarch/lib/strnlen_user.S   |  47 ++++++++++++
 12 files changed, 490 insertions(+), 2 deletions(-)
 create mode 100644 arch/loongarch/include/asm/delay.h
 create mode 100644 arch/loongarch/include/asm/string.h
 create mode 100644 arch/loongarch/lib/clear_user.S
 create mode 100644 arch/loongarch/lib/copy_user.S
 create mode 100644 arch/loongarch/lib/delay.c
 create mode 100644 arch/loongarch/lib/dump_tlb.c
 create mode 100644 arch/loongarch/lib/memcpy.S
 create mode 100644 arch/loongarch/lib/memmove.S
 create mode 100644 arch/loongarch/lib/memset.S
 create mode 100644 arch/loongarch/lib/strncpy_user.S
 create mode 100644 arch/loongarch/lib/strnlen_user.S

diff --git a/arch/loongarch/include/asm/delay.h b/arch/loongarch/include/asm/delay.h
new file mode 100644
index 000000000000..717227c1c79d
--- /dev/null
+++ b/arch/loongarch/include/asm/delay.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_DELAY_H
+#define _ASM_DELAY_H
+
+#include <linux/param.h>
+
+extern void __delay(unsigned long loops);
+extern void __ndelay(unsigned long ns);
+extern void __udelay(unsigned long us);
+
+#define ndelay(ns) __ndelay(ns)
+#define udelay(us) __udelay(us)
+
+/* make sure "usecs *= ..." in udelay do not overflow. */
+#if HZ >= 1000
+#define MAX_UDELAY_MS	1
+#elif HZ <= 200
+#define MAX_UDELAY_MS	5
+#else
+#define MAX_UDELAY_MS	(1000 / HZ)
+#endif
+
+#endif /* _ASM_DELAY_H */
diff --git a/arch/loongarch/include/asm/string.h b/arch/loongarch/include/asm/string.h
new file mode 100644
index 000000000000..21e9e0a58194
--- /dev/null
+++ b/arch/loongarch/include/asm/string.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_STRING_H
+#define _ASM_STRING_H
+
+#define __HAVE_ARCH_MEMSET
+extern void *memset(void *__s, int __c, size_t __count);
+
+#define __HAVE_ARCH_MEMCPY
+extern void *memcpy(void *__to, __const__ void *__from, size_t __n);
+
+#define __HAVE_ARCH_MEMMOVE
+extern void *memmove(void *__dest, __const__ void *__src, size_t __n);
+
+#endif /* _ASM_STRING_H */
diff --git a/arch/loongarch/lib/Makefile b/arch/loongarch/lib/Makefile
index 04916bcde679..2f337487878e 100644
--- a/arch/loongarch/lib/Makefile
+++ b/arch/loongarch/lib/Makefile
@@ -3,5 +3,5 @@
 # Makefile for LoongArch-specific library files..
 #
 
-lib-y	+= delay.o memcpy.o memset.o \
-	   strncpy_user.o strnlen_user.o dump_tlb.o
+lib-y	+= delay.o memset.o memcpy.o memmove.o clear_user.o \
+	   copy_user.o strncpy_user.o strnlen_user.o dump_tlb.o
diff --git a/arch/loongarch/lib/clear_user.S b/arch/loongarch/lib/clear_user.S
new file mode 100644
index 000000000000..3cc251cc57da
--- /dev/null
+++ b/arch/loongarch/lib/clear_user.S
@@ -0,0 +1,41 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <asm/asm.h>
+#include <asm/asmmacro.h>
+#include <asm/export.h>
+#include <asm/regdef.h>
+
+#define _ASM_EXTABLE(from, to)			\
+	.section __ex_table, "a";		\
+	PTR	from, to;			\
+	.previous
+
+/*
+ * size_t __clear_user(void *addr, size_t size)
+ *
+ * a0: addr
+ * a1: size
+ */
+SYM_FUNC_START(__clear_user)
+	beqz	a1, 2f
+
+1:	st.b	zero, a0, 0
+	addi.d	a0, a0, 1
+	addi.d	a1, a1, -1
+	bgt	a1, zero, 1b
+
+2:	move	v0, a1
+	jr	ra
+
+	.section .fixup, "ax"
+3:	move	v0, a1
+	jr	ra
+	.previous
+
+	_ASM_EXTABLE(1b, 3b)
+SYM_FUNC_END(__clear_user)
+
+EXPORT_SYMBOL(__clear_user)
diff --git a/arch/loongarch/lib/copy_user.S b/arch/loongarch/lib/copy_user.S
new file mode 100644
index 000000000000..5a5d7a25995a
--- /dev/null
+++ b/arch/loongarch/lib/copy_user.S
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <asm/asm.h>
+#include <asm/asmmacro.h>
+#include <asm/export.h>
+#include <asm/regdef.h>
+
+#define _ASM_EXTABLE(from, to)			\
+	.section __ex_table, "a";		\
+	PTR	from, to;			\
+	.previous
+
+/*
+ * size_t __copy_user(void *to, const void *from, size_t n)
+ *
+ * a0: to
+ * a1: from
+ * a2: n
+ */
+SYM_FUNC_START(__copy_user)
+	beqz	a2, 3f
+
+1:	ld.b	t0, a1, 0
+2:	st.b	t0, a0, 0
+	addi.d	a0, a0, 1
+	addi.d	a1, a1, 1
+	addi.d	a2, a2, -1
+	bgt	a2, zero, 1b
+
+3:	move	v0, a2
+	jr	ra
+
+	.section .fixup, "ax"
+4:	move	v0, a2
+	jr	ra
+	.previous
+
+	_ASM_EXTABLE(1b, 4b)
+	_ASM_EXTABLE(2b, 4b)
+SYM_FUNC_END(__copy_user)
+
+EXPORT_SYMBOL(__copy_user)
diff --git a/arch/loongarch/lib/delay.c b/arch/loongarch/lib/delay.c
new file mode 100644
index 000000000000..d5650b7ff7b6
--- /dev/null
+++ b/arch/loongarch/lib/delay.c
@@ -0,0 +1,47 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/smp.h>
+#include <linux/timex.h>
+
+#include <asm/compiler.h>
+#include <asm/processor.h>
+
+void __delay(unsigned long cycles)
+{
+	u64 t0 = get_cycles();
+
+	while ((unsigned long)(get_cycles() - t0) < cycles)
+		cpu_relax();
+}
+EXPORT_SYMBOL(__delay);
+
+/*
+ * Division by multiplication: you don't have to worry about
+ * loss of precision.
+ *
+ * Use only for very small delays ( < 1 msec).	Should probably use a
+ * lookup table, really, as the multiplications take much too long with
+ * short delays.  This is a "reasonable" implementation, though (and the
+ * first constant multiplications gets optimized away if the delay is
+ * a constant)
+ */
+
+void __udelay(unsigned long us)
+{
+	unsigned int lpj = raw_current_cpu_data.udelay_val;
+
+	__delay((us * 0x000010c7ull * HZ * lpj) >> 32);
+}
+EXPORT_SYMBOL(__udelay);
+
+void __ndelay(unsigned long ns)
+{
+	unsigned int lpj = raw_current_cpu_data.udelay_val;
+
+	__delay((ns * 0x00000005ull * HZ * lpj) >> 32);
+}
+EXPORT_SYMBOL(__ndelay);
diff --git a/arch/loongarch/lib/dump_tlb.c b/arch/loongarch/lib/dump_tlb.c
new file mode 100644
index 000000000000..b6e61455698b
--- /dev/null
+++ b/arch/loongarch/lib/dump_tlb.c
@@ -0,0 +1,107 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/kernel.h>
+#include <linux/mm.h>
+
+#include <asm/loongarchregs.h>
+#include <asm/page.h>
+#include <asm/pgtable.h>
+#include <asm/tlb.h>
+
+void dump_tlb_regs(void)
+{
+	const int field = 2 * sizeof(unsigned long);
+
+	pr_info("Index    : %0x\n", read_csr_tlbidx());
+	pr_info("PageSize : %0x\n", read_csr_pagesize());
+	pr_info("EntryHi  : %0*lx\n", field, read_csr_entryhi());
+	pr_info("EntryLo0 : %0*lx\n", field, read_csr_entrylo0());
+	pr_info("EntryLo1 : %0*lx\n", field, read_csr_entrylo1());
+}
+
+static void dump_tlb(int first, int last)
+{
+	unsigned long s_entryhi, entryhi, asid;
+	unsigned long long entrylo0, entrylo1, pa;
+	unsigned int index;
+	unsigned int s_index, s_asid;
+	unsigned int pagesize, c0, c1, i;
+	unsigned long asidmask = cpu_asid_mask(&current_cpu_data);
+	int pwidth = 11;
+	int vwidth = 11;
+	int asidwidth = DIV_ROUND_UP(ilog2(asidmask) + 1, 4);
+
+	s_entryhi = read_csr_entryhi();
+	s_index = read_csr_tlbidx();
+	s_asid = read_csr_asid();
+
+	for (i = first; i <= last; i++) {
+		write_csr_index(i);
+		tlb_read();
+		pagesize = read_csr_pagesize();
+		entryhi	 = read_csr_entryhi();
+		entrylo0 = read_csr_entrylo0();
+		entrylo1 = read_csr_entrylo1();
+		index = read_csr_tlbidx();
+		asid = read_csr_asid();
+
+		/* EHINV bit marks entire entry as invalid */
+		if (index & CSR_TLBIDX_EHINV)
+			continue;
+		/*
+		 * ASID takes effect in absence of G (global) bit.
+		 */
+		if (!((entrylo0 | entrylo1) & ENTRYLO_G) &&
+		    asid != s_asid)
+			continue;
+
+		/*
+		 * Only print entries in use
+		 */
+		pr_info("Index: %2d pgsize=%x ", i, (1 << pagesize));
+
+		c0 = (entrylo0 & ENTRYLO_C) >> ENTRYLO_C_SHIFT;
+		c1 = (entrylo1 & ENTRYLO_C) >> ENTRYLO_C_SHIFT;
+
+		pr_cont("va=%0*lx asid=%0*lx",
+			vwidth, (entryhi & ~0x1fffUL),
+			asidwidth, entryhi & asidmask);
+		/* RI/XI are in awkward places, so mask them off separately */
+		pa = entrylo0 & ~(ENTRYLO_RI | ENTRYLO_XI);
+		pa = (pa << 6) & PAGE_MASK;
+		pr_cont("\n\t[");
+		pr_cont("ri=%d xi=%d ",
+			(entrylo0 & ENTRYLO_RI) ? 1 : 0,
+			(entrylo0 & ENTRYLO_XI) ? 1 : 0);
+		pr_cont("pa=%0*llx c=%d d=%d v=%d g=%d plv=%d] [",
+			pwidth, pa, c0,
+			(entrylo0 & ENTRYLO_D) ? 1 : 0,
+			(entrylo0 & ENTRYLO_V) ? 1 : 0,
+			(entrylo0 & ENTRYLO_G) ? 1 : 0,
+			(entrylo0 & ENTRYLO_PLV) >> ENTRYLO_PLV_SHIFT);
+		/* RI/XI are in awkward places, so mask them off separately */
+		pa = entrylo1 & ~(ENTRYLO_RI | ENTRYLO_XI);
+		pa = (pa << 6) & PAGE_MASK;
+		pr_cont("ri=%d xi=%d ",
+			(entrylo1 & ENTRYLO_RI) ? 1 : 0,
+			(entrylo1 & ENTRYLO_XI) ? 1 : 0);
+		pr_cont("pa=%0*llx c=%d d=%d v=%d g=%d plv=%d]\n",
+			pwidth, pa, c1,
+			(entrylo1 & ENTRYLO_D) ? 1 : 0,
+			(entrylo1 & ENTRYLO_V) ? 1 : 0,
+			(entrylo1 & ENTRYLO_G) ? 1 : 0,
+			(entrylo1 & ENTRYLO_PLV) >> ENTRYLO_PLV_SHIFT);
+	}
+	pr_info("\n");
+
+	write_csr_entryhi(s_entryhi);
+	write_csr_tlbidx(s_index);
+	write_csr_asid(s_asid);
+}
+
+void dump_tlb_all(void)
+{
+	dump_tlb(0, current_cpu_data.tlbsize - 1);
+}
diff --git a/arch/loongarch/lib/memcpy.S b/arch/loongarch/lib/memcpy.S
new file mode 100644
index 000000000000..e94899d99624
--- /dev/null
+++ b/arch/loongarch/lib/memcpy.S
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <asm/asmmacro.h>
+#include <asm/export.h>
+#include <asm/regdef.h>
+
+/*
+ * void *memcpy(void *dst, const void *src, size_t n)
+ *
+ * a0: dst
+ * a1: src
+ * a2: n
+ */
+SYM_FUNC_START(memcpy)
+	move	a3, a0
+	beqz	a2, 2f
+
+1:	ld.b	t0, a1, 0
+	st.b	t0, a0, 0
+	addi.d	a0, a0, 1
+	addi.d	a1, a1, 1
+	addi.d	a2, a2, -1
+	bgt	a2, zero, 1b
+
+2:	move	v0, a3
+	jr	ra
+SYM_FUNC_END(memcpy)
+
+EXPORT_SYMBOL(memcpy)
diff --git a/arch/loongarch/lib/memmove.S b/arch/loongarch/lib/memmove.S
new file mode 100644
index 000000000000..f55d8dcd09dd
--- /dev/null
+++ b/arch/loongarch/lib/memmove.S
@@ -0,0 +1,45 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <asm/asmmacro.h>
+#include <asm/export.h>
+#include <asm/regdef.h>
+
+/*
+ * void *rmemcpy(void *dst, const void *src, size_t n)
+ *
+ * a0: dst
+ * a1: src
+ * a2: n
+ */
+SYM_FUNC_START(rmemcpy)
+	move	a3, a0
+	beqz	a2, 2f
+
+	add.d	a0, a0, a2
+	add.d	a1, a1, a2
+
+1:	ld.b	t0, a1, -1
+	st.b	t0, a0, -1
+	addi.d	a0, a0, -1
+	addi.d	a1, a1, -1
+	addi.d	a2, a2, -1
+	bgt	a2, zero, 1b
+
+2:	move	v0, a3
+	jr	ra
+SYM_FUNC_END(rmemcpy)
+
+SYM_FUNC_START(memmove)
+	blt	a0, a1, 1f	/* dst < src, memcpy */
+	blt	a1, a0, 2f	/* src < dst, rmemcpy */
+	jr	ra		/* dst == src, return */
+
+1:	b	memcpy
+
+2:	b	rmemcpy
+SYM_FUNC_END(memmove)
+
+EXPORT_SYMBOL(memmove)
diff --git a/arch/loongarch/lib/memset.S b/arch/loongarch/lib/memset.S
new file mode 100644
index 000000000000..f599017e68fd
--- /dev/null
+++ b/arch/loongarch/lib/memset.S
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <asm/asmmacro.h>
+#include <asm/export.h>
+#include <asm/regdef.h>
+
+/*
+ * void *memset(void *s, int c, size_t n)
+ *
+ * a0: s
+ * a1: c
+ * a2: n
+ */
+SYM_FUNC_START(memset)
+	move	a3, a0
+	beqz	a2, 2f
+
+1:	st.b	a1, a0, 0
+	addi.d	a0, a0, 1
+	addi.d	a2, a2, -1
+	bgt	a2, zero, 1b
+
+2:	move	v0, a3
+	jr	ra
+SYM_FUNC_END(memset)
+
+EXPORT_SYMBOL(memset)
diff --git a/arch/loongarch/lib/strncpy_user.S b/arch/loongarch/lib/strncpy_user.S
new file mode 100644
index 000000000000..b42d81045929
--- /dev/null
+++ b/arch/loongarch/lib/strncpy_user.S
@@ -0,0 +1,51 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/errno.h>
+#include <asm/asm.h>
+#include <asm/asmmacro.h>
+#include <asm/export.h>
+#include <asm/regdef.h>
+
+#define _ASM_EXTABLE(from, to)			\
+	.section __ex_table, "a";		\
+	PTR	from, to;			\
+	.previous
+
+/*
+ * long __strncpy_from_user(char *to, const char *from, long len)
+ *
+ * a0: to
+ * a1: from
+ * a2: len
+ */
+SYM_FUNC_START(__strncpy_from_user)
+	move	a3, zero
+
+1:	ld.b	t0, a1, 0
+	st.b	t0, a0, 0
+	addi.d	a0, a0, 1
+	addi.d	a1, a1, 1
+	beqz	t0, 2f
+
+	addi.d	a3, a3, 1
+	blt	a3, a2, 1b
+
+	/*
+	 * return len if the entire buffer filled,
+	 * return strlen else
+	 */
+2:	move	v0, a3
+	jr	ra
+
+	.section .fixup, "ax"
+	/* return -EFAULT if exception before terminator */
+3:	li.w	a0, -EFAULT
+	jr	ra
+	.previous
+
+	_ASM_EXTABLE(1b, 3b)
+SYM_FUNC_END(__strncpy_from_user)
+
+EXPORT_SYMBOL(__strncpy_from_user)
diff --git a/arch/loongarch/lib/strnlen_user.S b/arch/loongarch/lib/strnlen_user.S
new file mode 100644
index 000000000000..9288a5ad294e
--- /dev/null
+++ b/arch/loongarch/lib/strnlen_user.S
@@ -0,0 +1,47 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <asm/asm.h>
+#include <asm/asmmacro.h>
+#include <asm/export.h>
+#include <asm/regdef.h>
+
+#define _ASM_EXTABLE(from, to)			\
+	.section __ex_table, "a";		\
+	PTR	from, to;			\
+	.previous
+
+/*
+ * long __strnlen_user(const char *s, long n)
+ *
+ * a0: s
+ * a1: n
+ */
+SYM_FUNC_START(__strnlen_user)
+	move	a2, zero
+
+1:	ld.b	t0, a0, 0
+	addi.d	a0, a0, 1
+	addi.d	a2, a2, 1
+	beqz	t0, 2f
+
+	bge	a1, a2, 1b
+
+	/*
+	 * return the size of a string including the ending NUL character
+	 * up to a maximum of n
+	 */
+2:	move	v0, a2
+	jr	ra
+
+	.section .fixup, "ax"
+	/* return 0 in case of error */
+3:	move	v0, zero
+	jr	ra
+	.previous
+
+	_ASM_EXTABLE(1b, 3b)
+SYM_FUNC_END(__strnlen_user)
+
+EXPORT_SYMBOL(__strnlen_user)
-- 
2.27.0


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

* [PATCH 14/19] LoongArch: Add 64-bit Loongson platform
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (11 preceding siblings ...)
  2021-07-06  4:18 ` [PATCH 13/19] LoongArch: Add some library functions Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06  4:18 ` [PATCH 15/19] LoongArch: Add PCI controller support Huacai Chen
                   ` (7 subsequent siblings)
  20 siblings, 0 replies; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

Currently, LoongArch has two types of processors: 32-bit Loongson and
64-bit Loongson. This patch adds the 64-bit Loongson platform support,
including platform-specific init, setup, irq and reset code.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 .../include/asm/mach-loongson64/boot_param.h  |  91 ++++++++++
 .../include/asm/mach-loongson64/irq.h         |  77 ++++++++
 .../include/asm/mach-loongson64/loongson.h    | 167 +++++++++++++++++
 arch/loongarch/loongson64/boardinfo.c         |  36 ++++
 arch/loongarch/loongson64/env.c               | 170 ++++++++++++++++++
 arch/loongarch/loongson64/init.c              | 138 ++++++++++++++
 arch/loongarch/loongson64/irq.c               |  84 +++++++++
 arch/loongarch/loongson64/mem.c               |  87 +++++++++
 arch/loongarch/loongson64/msi.c               |  41 +++++
 arch/loongarch/loongson64/reset.c             |  54 ++++++
 arch/loongarch/loongson64/rtc.c               |  36 ++++
 arch/loongarch/loongson64/setup.c             |  37 ++++
 12 files changed, 1018 insertions(+)
 create mode 100644 arch/loongarch/include/asm/mach-loongson64/boot_param.h
 create mode 100644 arch/loongarch/include/asm/mach-loongson64/irq.h
 create mode 100644 arch/loongarch/include/asm/mach-loongson64/loongson.h
 create mode 100644 arch/loongarch/loongson64/boardinfo.c
 create mode 100644 arch/loongarch/loongson64/env.c
 create mode 100644 arch/loongarch/loongson64/init.c
 create mode 100644 arch/loongarch/loongson64/irq.c
 create mode 100644 arch/loongarch/loongson64/mem.c
 create mode 100644 arch/loongarch/loongson64/msi.c
 create mode 100644 arch/loongarch/loongson64/reset.c
 create mode 100644 arch/loongarch/loongson64/rtc.c
 create mode 100644 arch/loongarch/loongson64/setup.c

diff --git a/arch/loongarch/include/asm/mach-loongson64/boot_param.h b/arch/loongarch/include/asm/mach-loongson64/boot_param.h
new file mode 100644
index 000000000000..0598cc7c8203
--- /dev/null
+++ b/arch/loongarch/include/asm/mach-loongson64/boot_param.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_MACH_LOONGSON64_BOOT_PARAM_H_
+#define __ASM_MACH_LOONGSON64_BOOT_PARAM_H_
+
+#ifdef CONFIG_VT
+#include <linux/screen_info.h>
+#endif
+
+#define ADDRESS_TYPE_SYSRAM	1
+#define ADDRESS_TYPE_RESERVED	2
+#define ADDRESS_TYPE_ACPI	3
+#define ADDRESS_TYPE_NVS	4
+#define ADDRESS_TYPE_PMEM	5
+
+#define LOONGSON3_BOOT_MEM_MAP_MAX 128
+
+#define LOONGSON_EFIBOOT_SIGNATURE	"BPI"
+#define LOONGSON_MEM_LINKLIST		"MEM"
+#define LOONGSON_VBIOS_LINKLIST		"VBIOS"
+#define LOONGSON_SCREENINFO_LINKLIST	"SINFO"
+
+/* Values for Version BPI */
+enum bpi_version {
+	BPI_VERSION_V1 = 1000, /* Signature="BPI01000" */
+	BPI_VERSION_V2 = 1001, /* Signature="BPI01001" */
+};
+
+/* Flags in bootparamsinterface */
+#define BPI_FLAGS_UEFI_SUPPORTED BIT(0)
+
+struct _extention_list_hdr {
+	u64	signature;
+	u32	length;
+	u8	revision;
+	u8	checksum;
+	struct	_extention_list_hdr *next;
+} __packed;
+
+struct bootparamsinterface {
+	u64	signature;	/* {"B", "P", "I", "0", "1", ... } */
+	void	*systemtable;
+	struct	_extention_list_hdr *extlist;
+	u64	flags;
+} __packed;
+
+struct loongsonlist_mem_map {
+	struct	_extention_list_hdr header;	/* {"M", "E", "M"} */
+	u8	map_count;
+	struct	loongson_mem_map {
+		u32 mem_type;
+		u64 mem_start;
+		u64 mem_size;
+	} __packed map[LOONGSON3_BOOT_MEM_MAP_MAX];
+} __packed;
+
+struct loongsonlist_vbios {
+	struct	_extention_list_hdr header;	/* {"V", "B", "I", "O", "S"} */
+	u64	vbios_addr;
+} __packed;
+
+struct loongsonlist_screeninfo {
+	struct	_extention_list_hdr header;	/* {"S", "I", "N", "F", "O"} */
+	struct	screen_info si;
+} __packed;
+
+struct loongson_board_info {
+	int bios_size;
+	char *bios_vendor;
+	char *bios_version;
+	char *bios_release_date;
+	char *board_name;
+	char *board_vendor;
+};
+
+struct loongson_system_configuration {
+	int bpi_ver;
+	int nr_cpus;
+	int nr_nodes;
+	int nr_pch_pics;
+	int boot_cpu_id;
+	int cores_per_node;
+	int cores_per_package;
+	char *cpuname;
+	u64 vgabios_addr;
+};
+
+extern struct loongson_board_info b_info;
+extern struct bootparamsinterface *efi_bp;
+extern struct loongsonlist_mem_map *loongson_mem_map;
+extern struct loongson_system_configuration loongson_sysconf;
+#endif
diff --git a/arch/loongarch/include/asm/mach-loongson64/irq.h b/arch/loongarch/include/asm/mach-loongson64/irq.h
new file mode 100644
index 000000000000..9e03a960e7b6
--- /dev/null
+++ b/arch/loongarch/include/asm/mach-loongson64/irq.h
@@ -0,0 +1,77 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_MACH_LOONGSON64_IRQ_H_
+#define __ASM_MACH_LOONGSON64_IRQ_H_
+
+#define NR_IRQS	(64 + 256)
+
+#define LOONGSON_CPU_UART0_VEC		10 /* CPU UART0 */
+#define LOONGSON_CPU_THSENS_VEC		14 /* CPU Thsens */
+#define LOONGSON_CPU_HT0_VEC		16 /* CPU HT0 irq vector base number */
+#define LOONGSON_CPU_HT1_VEC		24 /* CPU HT1 irq vector base number */
+
+/* IRQ number definitions */
+#define LOONGSON_LPC_IRQ_BASE		0
+#define LOONGSON_LPC_LAST_IRQ		(LOONGSON_LPC_IRQ_BASE + 15)
+
+#define LOONGSON_CPU_IRQ_BASE		16
+#define LOONGSON_LINTC_IRQ		(LOONGSON_CPU_IRQ_BASE + 2) /* IP2 for CPU local interrupt controller */
+#define LOONGSON_BRIDGE_IRQ		(LOONGSON_CPU_IRQ_BASE + 3) /* IP3 for bridge */
+#define LOONGSON_TIMER_IRQ		(LOONGSON_CPU_IRQ_BASE + 11) /* IP11 CPU Timer */
+#define LOONGSON_CPU_LAST_IRQ		(LOONGSON_CPU_IRQ_BASE + 14)
+
+#define LOONGSON_PCH_IRQ_BASE		64
+#define LOONGSON_PCH_ACPI_IRQ		(LOONGSON_PCH_IRQ_BASE + 47)
+#define LOONGSON_PCH_LAST_IRQ		(LOONGSON_PCH_IRQ_BASE + 64 - 1)
+
+#define LOONGSON_MSI_IRQ_BASE		(LOONGSON_PCH_IRQ_BASE + 64)
+#define LOONGSON_MSI_LAST_IRQ		(LOONGSON_PCH_IRQ_BASE + 256 - 1)
+
+#define GSI_MIN_CPU_IRQ		LOONGSON_CPU_IRQ_BASE
+#define GSI_MAX_CPU_IRQ		(LOONGSON_CPU_IRQ_BASE + 48 - 1)
+#define GSI_MIN_PCH_IRQ		LOONGSON_PCH_IRQ_BASE
+#define GSI_MAX_PCH_IRQ		(LOONGSON_PCH_IRQ_BASE + 256 - 1)
+
+#define MAX_PCH_PICS 4
+
+extern int find_pch_pic(u32 gsi);
+
+static inline void eiointc_enable(void)
+{
+	uint64_t misc;
+
+	misc = iocsr_readq(LOONGARCH_IOCSR_MISC_FUNC);
+	misc |= IOCSR_MISC_FUNC_EXT_IOI_EN;
+	iocsr_writeq(misc, LOONGARCH_IOCSR_MISC_FUNC);
+}
+
+struct acpi_madt_lio_pic;
+struct acpi_madt_eio_pic;
+struct acpi_madt_ht_pic;
+struct acpi_madt_bio_pic;
+struct acpi_madt_msi_pic;
+struct acpi_madt_lpc_pic;
+
+struct fwnode_handle *liointc_acpi_init(struct acpi_madt_lio_pic *acpi_liointc);
+struct fwnode_handle *eiointc_acpi_init(struct acpi_madt_eio_pic *acpi_eiointc);
+
+struct fwnode_handle *htvec_acpi_init(struct fwnode_handle *parent,
+					struct acpi_madt_ht_pic *acpi_htvec);
+struct fwnode_handle *pch_lpc_acpi_init(struct fwnode_handle *parent,
+					struct acpi_madt_lpc_pic *acpi_pchlpc);
+struct fwnode_handle *pch_msi_acpi_init(struct fwnode_handle *parent,
+					struct acpi_madt_msi_pic *acpi_pchmsi);
+struct fwnode_handle *pch_pic_acpi_init(struct fwnode_handle *parent,
+					struct acpi_madt_bio_pic *acpi_pchpic);
+
+extern struct acpi_madt_lio_pic *acpi_liointc;
+extern struct acpi_madt_eio_pic *acpi_eiointc;
+extern struct acpi_madt_ht_pic *acpi_htintc;
+extern struct acpi_madt_lpc_pic *acpi_pchlpc;
+extern struct acpi_madt_msi_pic *acpi_pchmsi;
+extern struct acpi_madt_bio_pic *acpi_pchpic[MAX_PCH_PICS];
+
+extern struct fwnode_handle *acpi_liointc_handle;
+extern struct fwnode_handle *acpi_msidomain_handle;
+extern struct fwnode_handle *acpi_picdomain_handle[MAX_PCH_PICS];
+
+#endif /* __ASM_MACH_LOONGSON64_IRQ_H_ */
diff --git a/arch/loongarch/include/asm/mach-loongson64/loongson.h b/arch/loongarch/include/asm/mach-loongson64/loongson.h
new file mode 100644
index 000000000000..f003fa204185
--- /dev/null
+++ b/arch/loongarch/include/asm/mach-loongson64/loongson.h
@@ -0,0 +1,167 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef __ASM_MACH_LOONGSON64_LOONGSON_H
+#define __ASM_MACH_LOONGSON64_LOONGSON_H
+
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/irq.h>
+#include <linux/pci.h>
+#include <asm/addrspace.h>
+
+#include <boot_param.h>
+
+extern const struct plat_smp_ops loongson3_smp_ops;
+
+/* loongson-specific command line, env and memory initialization */
+extern void __init fw_init_environ(void);
+extern void __init fw_init_memory(void);
+extern void __init fw_init_numa_memory(void);
+
+#define LOONGSON_REG(x) \
+	(*(volatile u32 *)((char *)TO_UNCAC(LOONGSON_REG_BASE) + (x)))
+
+#define LOONGSON_LIO_BASE	0x18000000
+#define LOONGSON_LIO_SIZE	0x00100000	/* 1M */
+#define LOONGSON_LIO_TOP	(LOONGSON_LIO_BASE+LOONGSON_LIO_SIZE-1)
+
+#define LOONGSON_BOOT_BASE	0x1c000000
+#define LOONGSON_BOOT_SIZE	0x02000000	/* 32M */
+#define LOONGSON_BOOT_TOP	(LOONGSON_BOOT_BASE+LOONGSON_BOOT_SIZE-1)
+
+#define LOONGSON_REG_BASE	0x1fe00000
+#define LOONGSON_REG_SIZE	0x00100000	/* 1M */
+#define LOONGSON_REG_TOP	(LOONGSON_REG_BASE+LOONGSON_REG_SIZE-1)
+
+/* GPIO Regs - r/w */
+
+#define LOONGSON_GPIODATA		LOONGSON_REG(0x11c)
+#define LOONGSON_GPIOIE			LOONGSON_REG(0x120)
+#define LOONGSON_REG_GPIO_BASE          (LOONGSON_REG_BASE + 0x11c)
+
+#define MAX_PACKAGES 16
+
+/* Chip Config registor of each physical cpu package */
+extern u64 loongson_chipcfg[MAX_PACKAGES];
+#define LOONGSON_CHIPCFG(id) (*(volatile u32 *)(loongson_chipcfg[id]))
+
+/* Chip Temperature registor of each physical cpu package */
+extern u64 loongson_chiptemp[MAX_PACKAGES];
+#define LOONGSON_CHIPTEMP(id) (*(volatile u32 *)(loongson_chiptemp[id]))
+
+/* Freq Control register of each physical cpu package */
+extern u64 loongson_freqctrl[MAX_PACKAGES];
+#define LOONGSON_FREQCTRL(id) (*(volatile u32 *)(loongson_freqctrl[id]))
+
+#define xconf_readl(addr) readl(addr)
+#define xconf_readq(addr) readq(addr)
+
+static inline void xconf_writel(u32 val, volatile void __iomem *addr)
+{
+	asm volatile (
+	"	st.w	%[v], %[hw], 0	\n"
+	"	ld.b	$r0, %[hw], 0	\n"
+	:
+	: [hw] "r" (addr), [v] "r" (val)
+	);
+}
+
+static inline void xconf_writeq(u64 val64, volatile void __iomem *addr)
+{
+	asm volatile (
+	"	st.d	%[v], %[hw], 0	\n"
+	"	ld.b	$r0, %[hw], 0	\n"
+	:
+	: [hw] "r" (addr),  [v] "r" (val64)
+	);
+}
+
+/* ============== LS7A registers =============== */
+#define LS7A_PCH_REG_BASE		0x10000000UL
+/* LPC regs */
+#define LS7A_LPC_REG_BASE		(LS7A_PCH_REG_BASE + 0x00002000)
+/* CHIPCFG regs */
+#define LS7A_CHIPCFG_REG_BASE		(LS7A_PCH_REG_BASE + 0x00010000)
+/* MISC reg base */
+#define LS7A_MISC_REG_BASE		(LS7A_PCH_REG_BASE + 0x00080000)
+/* ACPI regs */
+#define LS7A_ACPI_REG_BASE		(LS7A_MISC_REG_BASE + 0x00050000)
+/* RTC regs */
+#define LS7A_RTC_REG_BASE		(LS7A_MISC_REG_BASE + 0x00050100)
+
+#define LS7A_DMA_CFG			(void *)TO_UNCAC(LS7A_CHIPCFG_REG_BASE + 0x041c)
+#define LS7A_DMA_NODE_SHF		8
+#define LS7A_DMA_NODE_MASK		0x1F00
+
+#define LS7A_INT_MASK_REG		(void *)TO_UNCAC(LS7A_PCH_REG_BASE + 0x020)
+#define LS7A_INT_EDGE_REG		(void *)TO_UNCAC(LS7A_PCH_REG_BASE + 0x060)
+#define LS7A_INT_CLEAR_REG		(void *)TO_UNCAC(LS7A_PCH_REG_BASE + 0x080)
+#define LS7A_INT_HTMSI_EN_REG		(void *)TO_UNCAC(LS7A_PCH_REG_BASE + 0x040)
+#define LS7A_INT_ROUTE_ENTRY_REG	(void *)TO_UNCAC(LS7A_PCH_REG_BASE + 0x100)
+#define LS7A_INT_HTMSI_VEC_REG		(void *)TO_UNCAC(LS7A_PCH_REG_BASE + 0x200)
+#define LS7A_INT_STATUS_REG		(void *)TO_UNCAC(LS7A_PCH_REG_BASE + 0x3a0)
+#define LS7A_INT_POL_REG		(void *)TO_UNCAC(LS7A_PCH_REG_BASE + 0x3e0)
+#define LS7A_LPC_INT_CTL		(void *)TO_UNCAC(LS7A_PCH_REG_BASE + 0x2000)
+#define LS7A_LPC_INT_ENA		(void *)TO_UNCAC(LS7A_PCH_REG_BASE + 0x2004)
+#define LS7A_LPC_INT_STS		(void *)TO_UNCAC(LS7A_PCH_REG_BASE + 0x2008)
+#define LS7A_LPC_INT_CLR		(void *)TO_UNCAC(LS7A_PCH_REG_BASE + 0x200c)
+#define LS7A_LPC_INT_POL		(void *)TO_UNCAC(LS7A_PCH_REG_BASE + 0x2010)
+
+#define LS7A_PMCON_SOC_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x000)
+#define LS7A_PMCON_RESUME_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x004)
+#define LS7A_PMCON_RTC_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x008)
+#define LS7A_PM1_EVT_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x00c)
+#define LS7A_PM1_ENA_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x010)
+#define LS7A_PM1_CNT_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x014)
+#define LS7A_PM1_TMR_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x018)
+#define LS7A_P_CNT_REG			(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x01c)
+#define LS7A_GPE0_STS_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x028)
+#define LS7A_GPE0_ENA_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x02c)
+#define LS7A_RST_CNT_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x030)
+#define LS7A_WD_SET_REG			(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x034)
+#define LS7A_WD_TIMER_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x038)
+#define LS7A_THSENS_CNT_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x04c)
+#define LS7A_GEN_RTC_1_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x050)
+#define LS7A_GEN_RTC_2_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x054)
+#define LS7A_DPM_CFG_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x400)
+#define LS7A_DPM_STS_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x404)
+#define LS7A_DPM_CNT_REG		(void *)TO_UNCAC(LS7A_ACPI_REG_BASE + 0x408)
+
+typedef enum {
+	ACPI_PCI_HOTPLUG_STATUS	= 1 << 1,
+	ACPI_CPU_HOTPLUG_STATUS	= 1 << 2,
+	ACPI_MEM_HOTPLUG_STATUS	= 1 << 3,
+	ACPI_POWERBUTTON_STATUS	= 1 << 8,
+	ACPI_RTC_WAKE_STATUS	= 1 << 10,
+	ACPI_PCI_WAKE_STATUS	= 1 << 14,
+	ACPI_ANY_WAKE_STATUS	= 1 << 15,
+} AcpiEventStatusBits;
+
+#define HT1LO_OFFSET		0xe0000000000UL
+
+/* PCI Configuration Space Base */
+#define HT1LO_PCICFG_BASE	0xefdfe000000UL
+#define HT1LO_PCICFG_BASE_TP1	0xefdff000000UL
+
+#define MCFG_EXT_PCICFG_BASE		0xefe00000000UL
+#define HT1LO_EXT_PCICFG_BASE		(((struct pci_controller *)(bus)->sysdata)->mcfg_addr)
+#define HT1LO_EXT_PCICFG_BASE_TP1	(HT1LO_EXT_PCICFG_BASE + 0x10000000)
+
+/* REG ACCESS*/
+#define ls7a_readb(addr)			  (*(volatile unsigned char  *)TO_UNCAC(addr))
+#define ls7a_readw(addr)			  (*(volatile unsigned short *)TO_UNCAC(addr))
+#define ls7a_readl(addr)			  (*(volatile unsigned int   *)TO_UNCAC(addr))
+#define ls7a_readq(addr)			  (*(volatile unsigned long  *)TO_UNCAC(addr))
+#define ls7a_writeb(val, addr)		*(volatile unsigned char  *)TO_UNCAC(addr) = (val)
+#define ls7a_writew(val, addr)		*(volatile unsigned short *)TO_UNCAC(addr) = (val)
+#define ls7a_writel(val, addr)		ls7a_write_type(val, addr, uint32_t)
+#define ls7a_writeq(val, addr)		ls7a_write_type(val, addr, uint64_t)
+#define ls7a_write(val, addr)		ls7a_write_type(val, addr, uint64_t)
+
+extern struct pci_ops ls7a_pci_ops;
+
+#endif /* __ASM_MACH_LOONGSON64_LOONGSON_H */
diff --git a/arch/loongarch/loongson64/boardinfo.c b/arch/loongarch/loongson64/boardinfo.c
new file mode 100644
index 000000000000..67ffba105ebd
--- /dev/null
+++ b/arch/loongarch/loongson64/boardinfo.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/efi.h>
+#include <linux/init.h>
+#include <linux/kobject.h>
+
+#include <boot_param.h>
+
+static ssize_t boardinfo_show(struct kobject *kobj,
+			      struct kobj_attribute *attr, char *buf)
+{
+	return sprintf(buf,
+		"BIOS Information\n"
+		"Vendor\t\t\t: %s\n"
+		"Version\t\t\t: %s\n"
+		"ROM Size\t\t: %d KB\n"
+		"Release Date\t\t: %s\n\n"
+		"Board Information\n"
+		"Manufacturer\t\t: %s\n"
+		"Board Name\t\t: %s\n"
+		"Family\t\t\t: LOONGSON3\n\n",
+		b_info.bios_vendor, b_info.bios_version,
+		b_info.bios_size, b_info.bios_release_date,
+		b_info.board_vendor, b_info.board_name);
+}
+
+static struct kobj_attribute boardinfo_attr = __ATTR(boardinfo, 0444,
+						     boardinfo_show, NULL);
+
+static int __init boardinfo_init(void)
+{
+	return sysfs_create_file(efi_kobj, &boardinfo_attr.attr);
+}
+late_initcall(boardinfo_init);
diff --git a/arch/loongarch/loongson64/env.c b/arch/loongarch/loongson64/env.c
new file mode 100644
index 000000000000..e6752ddbefc2
--- /dev/null
+++ b/arch/loongarch/loongson64/env.c
@@ -0,0 +1,170 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/export.h>
+#include <linux/acpi.h>
+#include <linux/efi.h>
+#include <asm/fw.h>
+#include <asm/time.h>
+#include <asm/bootinfo.h>
+#include <loongson.h>
+
+struct bootparamsinterface *efi_bp;
+struct loongsonlist_mem_map *loongson_mem_map;
+struct loongsonlist_vbios *pvbios;
+struct loongson_system_configuration loongson_sysconf;
+EXPORT_SYMBOL(loongson_sysconf);
+
+u64 loongson_chipcfg[MAX_PACKAGES];
+u64 loongson_chiptemp[MAX_PACKAGES];
+u64 loongson_freqctrl[MAX_PACKAGES];
+unsigned long long smp_group[MAX_PACKAGES];
+
+static void __init register_addrs_set(u64 *registers, const u64 addr, int num)
+{
+	u64 i;
+
+	for (i = 0; i < num; i++) {
+		*registers = (i << 44) | addr;
+		registers++;
+	}
+}
+
+u8 ext_listhdr_checksum(u8 *buffer, u32 length)
+{
+	u8 sum = 0;
+	u8 *end = buffer + length;
+
+	while (buffer < end) {
+		sum = (u8)(sum + *(buffer++));
+	}
+
+	return (sum);
+}
+
+int parse_mem(struct _extention_list_hdr *head)
+{
+	loongson_mem_map = (struct loongsonlist_mem_map *)head;
+	if (ext_listhdr_checksum((u8 *)loongson_mem_map, head->length)) {
+		pr_warn("mem checksum error\n");
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+int parse_vbios(struct _extention_list_hdr *head)
+{
+	pvbios = (struct loongsonlist_vbios *)head;
+
+	if (ext_listhdr_checksum((u8 *)pvbios, head->length)) {
+		pr_warn("vbios_addr checksum error\n");
+		return -EPERM;
+	}
+
+	loongson_sysconf.vgabios_addr = pvbios->vbios_addr;
+
+	return 0;
+}
+
+static int parse_screeninfo(struct _extention_list_hdr *head)
+{
+	struct loongsonlist_screeninfo *pscreeninfo;
+
+	pscreeninfo = (struct loongsonlist_screeninfo *)head;
+	if (ext_listhdr_checksum((u8 *)pscreeninfo, head->length)) {
+		pr_warn("screeninfo_addr checksum error\n");
+		return -EPERM;
+	}
+
+	memcpy(&screen_info, &pscreeninfo->si, sizeof(screen_info));
+
+	return 0;
+}
+
+static int list_find(struct _extention_list_hdr *head)
+{
+	struct _extention_list_hdr *fhead = head;
+
+	if (fhead == NULL) {
+		pr_warn("the link is empty!\n");
+		return -1;
+	}
+
+	while (fhead != NULL) {
+		if (memcmp(&(fhead->signature), LOONGSON_MEM_LINKLIST, 3) == 0) {
+			if (parse_mem(fhead) != 0) {
+				pr_warn("parse mem failed\n");
+				return -EPERM;
+			}
+		} else if (memcmp(&(fhead->signature), LOONGSON_VBIOS_LINKLIST, 5) == 0) {
+			if (parse_vbios(fhead) != 0) {
+				pr_warn("parse vbios failed\n");
+				return -EPERM;
+			}
+		} else if (memcmp(&(fhead->signature), LOONGSON_SCREENINFO_LINKLIST, 5) == 0) {
+			if (parse_screeninfo(fhead) != 0) {
+				pr_warn("parse screeninfo failed\n");
+				return -EPERM;
+			}
+		}
+		fhead = fhead->next;
+	}
+
+	return 0;
+}
+
+static void __init parse_bpi_flags(void)
+{
+	if (efi_bp->flags & BPI_FLAGS_UEFI_SUPPORTED)
+		set_bit(EFI_BOOT, &efi.flags);
+	else
+		clear_bit(EFI_BOOT, &efi.flags);
+}
+
+static int get_bpi_version(void *signature)
+{
+	char data[8];
+	int r, version = 0;
+
+	memset(data, 0, 8);
+	memcpy(data, signature + 4, 4);
+	r = kstrtoint(data, 0, &version);
+
+	if (r < 0 || version < BPI_VERSION_V1)
+		panic("Fatal error, invalid BPI version: %d\n", version);
+
+	if (version >= BPI_VERSION_V2)
+		parse_bpi_flags();
+
+	return version;
+}
+
+void __init fw_init_environ(void)
+{
+	efi_bp = (struct bootparamsinterface *)_fw_envp;
+	loongson_sysconf.bpi_ver = get_bpi_version(&efi_bp->signature);
+
+	register_addrs_set(smp_group, TO_UNCAC(0x1fe01000), 16);
+	register_addrs_set(loongson_chipcfg, TO_UNCAC(0x1fe00180), 16);
+	register_addrs_set(loongson_chiptemp, TO_UNCAC(0x1fe0019c), 16);
+	register_addrs_set(loongson_freqctrl, TO_UNCAC(0x1fe001d0), 16);
+
+	if (list_find(efi_bp->extlist))
+		pr_warn("Scan bootparam failed\n");
+}
+
+static int __init init_cpu_fullname(void)
+{
+	int cpu;
+
+	if (loongson_sysconf.cpuname && !strncmp(loongson_sysconf.cpuname, "Loongson", 8)) {
+		for (cpu = 0; cpu < NR_CPUS; cpu++)
+			__cpu_full_name[cpu] = loongson_sysconf.cpuname;
+	}
+	return 0;
+}
+arch_initcall(init_cpu_fullname);
diff --git a/arch/loongarch/loongson64/init.c b/arch/loongarch/loongson64/init.c
new file mode 100644
index 000000000000..3fedebbbf665
--- /dev/null
+++ b/arch/loongarch/loongson64/init.c
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <linux/acpi.h>
+#include <linux/dmi.h>
+#include <linux/efi.h>
+#include <linux/memblock.h>
+#include <asm/acpi.h>
+#include <asm/bootinfo.h>
+#include <asm/cacheflush.h>
+#include <asm/efi.h>
+#include <asm/fw.h>
+#include <asm/time.h>
+
+#include <loongson.h>
+
+#define SMBIOS_BIOSSIZE_OFFSET		0x09
+#define SMBIOS_BIOSEXTERN_OFFSET	0x13
+#define SMBIOS_FREQLOW_OFFSET		0x16
+#define SMBIOS_FREQHIGH_OFFSET		0x17
+#define SMBIOS_FREQLOW_MASK		0xFF
+#define SMBIOS_CORE_PACKAGE_OFFSET	0x23
+#define LOONGSON_EFI_ENABLE		(1 << 3)
+
+struct loongson_board_info b_info;
+static const char dmi_empty_string[] = "        ";
+
+const char *dmi_string_parse(const struct dmi_header *dm, u8 s)
+{
+	const u8 *bp = ((u8 *) dm) + dm->length;
+
+	if (s) {
+		s--;
+		while (s > 0 && *bp) {
+			bp += strlen(bp) + 1;
+			s--;
+		}
+
+		if (*bp != 0) {
+			size_t len = strlen(bp)+1;
+			size_t cmp_len = len > 8 ? 8 : len;
+
+			if (!memcmp(bp, dmi_empty_string, cmp_len))
+				return dmi_empty_string;
+
+			return bp;
+		}
+	}
+
+	return "";
+
+}
+
+static void __init parse_cpu_table(const struct dmi_header *dm)
+{
+	long freq_temp = 0;
+	char *dmi_data = (char *)dm;
+
+	freq_temp = ((*(dmi_data + SMBIOS_FREQHIGH_OFFSET) << 8) +
+			((*(dmi_data + SMBIOS_FREQLOW_OFFSET)) & SMBIOS_FREQLOW_MASK));
+	cpu_clock_freq = freq_temp * 1000000;
+
+	loongson_sysconf.cpuname = (void *)dmi_string_parse(dm, dmi_data[16]);
+	loongson_sysconf.cores_per_package = *(dmi_data + SMBIOS_CORE_PACKAGE_OFFSET);
+
+	pr_info("CpuClock = %llu\n", cpu_clock_freq);
+
+}
+
+static void __init parse_bios_table(const struct dmi_header *dm)
+{
+	int bios_extern;
+	char *dmi_data = (char *)dm;
+
+	bios_extern = *(dmi_data + SMBIOS_BIOSEXTERN_OFFSET);
+	b_info.bios_size = *(dmi_data + SMBIOS_BIOSSIZE_OFFSET);
+
+	if (bios_extern & LOONGSON_EFI_ENABLE)
+		set_bit(EFI_BOOT, &efi.flags);
+	else
+		clear_bit(EFI_BOOT, &efi.flags);
+}
+
+static void __init find_tokens(const struct dmi_header *dm, void *dummy)
+{
+	switch (dm->type) {
+	case 0x0: /* Extern BIOS */
+		parse_bios_table(dm);
+		break;
+	case 0x4: /* Calling interface */
+		parse_cpu_table(dm);
+		break;
+	}
+}
+static void __init smbios_parse(void)
+{
+	b_info.bios_vendor = (void *)dmi_get_system_info(DMI_BIOS_VENDOR);
+	b_info.bios_version = (void *)dmi_get_system_info(DMI_BIOS_VERSION);
+	b_info.bios_release_date = (void *)dmi_get_system_info(DMI_BIOS_DATE);
+	b_info.board_vendor = (void *)dmi_get_system_info(DMI_BOARD_VENDOR);
+	b_info.board_name = (void *)dmi_get_system_info(DMI_BOARD_NAME);
+	dmi_walk(find_tokens, NULL);
+}
+
+void __init early_init(void)
+{
+	fw_init_cmdline();
+	fw_init_environ();
+	early_memblock_init();
+}
+
+void __init platform_init(void)
+{
+	/* init base address of io space */
+	set_io_port_base((unsigned long)
+		ioremap(LOONGSON_LIO_BASE, LOONGSON_LIO_SIZE));
+
+	efi_init();
+#ifdef CONFIG_ACPI_TABLE_UPGRADE
+	acpi_table_upgrade();
+#endif
+#ifdef CONFIG_ACPI
+	acpi_gbl_use_default_register_widths = false;
+	acpi_boot_table_init();
+	acpi_boot_init();
+#endif
+	loongarch_pci_ops = &ls7a_pci_ops;
+
+	fw_init_memory();
+	dmi_setup();
+	smbios_parse();
+	pr_info("The BIOS Version: %s\n", b_info.bios_version);
+
+	efi_runtime_init();
+}
diff --git a/arch/loongarch/loongson64/irq.c b/arch/loongarch/loongson64/irq.c
new file mode 100644
index 000000000000..aabe457570ce
--- /dev/null
+++ b/arch/loongarch/loongson64/irq.c
@@ -0,0 +1,84 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/compiler.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/stddef.h>
+#include <asm/irq.h>
+#include <asm/setup.h>
+#include <asm/loongarchregs.h>
+#include <loongson.h>
+
+struct acpi_madt_lio_pic *acpi_liointc;
+struct acpi_madt_eio_pic *acpi_eiointc;
+struct acpi_madt_ht_pic *acpi_htintc;
+struct acpi_madt_lpc_pic *acpi_pchlpc;
+struct acpi_madt_msi_pic *acpi_pchmsi;
+struct acpi_madt_bio_pic *acpi_pchpic[MAX_PCH_PICS];
+
+struct fwnode_handle *acpi_liointc_handle;
+struct fwnode_handle *acpi_msidomain_handle;
+struct fwnode_handle *acpi_picdomain_handle[MAX_PCH_PICS];
+
+int find_pch_pic(u32 gsi)
+{
+	int i, start, end;
+
+	/* Find the PCH_PIC that manages this GSI. */
+	for (i = 0; i < loongson_sysconf.nr_pch_pics; i++) {
+		struct acpi_madt_bio_pic *irq_cfg = acpi_pchpic[i];
+
+		start = irq_cfg->gsi_base;
+		end   = irq_cfg->gsi_base + irq_cfg->size;
+		if (gsi >= start && gsi < end)
+			return i;
+	}
+
+	pr_err("ERROR: Unable to locate PCH_PIC for GSI %d\n", gsi);
+	return -1;
+}
+
+void __init setup_IRQ(void)
+{
+	int i;
+	struct fwnode_handle *pch_parent_handle;
+
+	if (!acpi_eiointc)
+		cpu_data[0].options &= ~LOONGARCH_CPU_EXTIOI;
+
+	loongarch_cpu_irq_init(NULL, NULL);
+	acpi_liointc_handle = liointc_acpi_init(acpi_liointc);
+
+	if (cpu_has_extioi) {
+		pr_info("Using EIOINTC interrupt mode\n");
+		pch_parent_handle = eiointc_acpi_init(acpi_eiointc);
+	} else {
+		pr_info("Using HTVECINTC interrupt mode\n");
+		pch_parent_handle = htvec_acpi_init(acpi_liointc_handle, acpi_htintc);
+	}
+
+	for (i = 0; i < loongson_sysconf.nr_pch_pics; i++)
+		acpi_picdomain_handle[i] = pch_pic_acpi_init(pch_parent_handle, acpi_pchpic[i]);
+
+	acpi_msidomain_handle = pch_msi_acpi_init(pch_parent_handle, acpi_pchmsi);
+	irq_set_default_host(irq_find_matching_fwnode(acpi_picdomain_handle[0], DOMAIN_BUS_ANY));
+
+	pch_lpc_acpi_init(acpi_picdomain_handle[0], acpi_pchlpc);
+}
+
+void __init arch_init_irq(void)
+{
+	clear_csr_ecfg(ECFG0_IM);
+	clear_csr_estat(ESTATF_IP);
+
+	setup_IRQ();
+
+	set_csr_ecfg(ECFGF_IP0 | ECFGF_IP1 | ECFGF_IPI | ECFGF_PC);
+}
diff --git a/arch/loongarch/loongson64/mem.c b/arch/loongarch/loongson64/mem.c
new file mode 100644
index 000000000000..2e0b60642326
--- /dev/null
+++ b/arch/loongarch/loongson64/mem.c
@@ -0,0 +1,87 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/memblock.h>
+
+#include <asm/bootinfo.h>
+#include <asm/sections.h>
+
+#include <loongson.h>
+#include <boot_param.h>
+
+void __init early_memblock_init(void)
+{
+	int i;
+	u32 mem_type;
+	u64 mem_start, mem_end, mem_size;
+
+	/* parse memory information */
+	for (i = 0; i < loongson_mem_map->map_count; i++) {
+		mem_type = loongson_mem_map->map[i].mem_type;
+		mem_start = loongson_mem_map->map[i].mem_start;
+		mem_size = loongson_mem_map->map[i].mem_size;
+		mem_end = mem_start + mem_size;
+
+		switch (mem_type) {
+		case ADDRESS_TYPE_SYSRAM:
+			memblock_add(mem_start, mem_size);
+			if (max_low_pfn < (mem_end >> PAGE_SHIFT))
+				max_low_pfn = mem_end >> PAGE_SHIFT;
+			break;
+		}
+	}
+	memblock_set_current_limit(PFN_PHYS(max_low_pfn));
+}
+
+void __init fw_init_memory(void)
+{
+	int i;
+	u32 mem_type;
+	u64 mem_start, mem_end, mem_size;
+	unsigned long start_pfn, end_pfn;
+	static unsigned long num_physpages;
+
+	/* parse memory information */
+	for (i = 0; i < loongson_mem_map->map_count; i++) {
+		mem_type = loongson_mem_map->map[i].mem_type;
+		mem_start = loongson_mem_map->map[i].mem_start;
+		mem_size = loongson_mem_map->map[i].mem_size;
+		mem_end = mem_start + mem_size;
+
+		switch (mem_type) {
+		case ADDRESS_TYPE_SYSRAM:
+			mem_start = PFN_ALIGN(mem_start);
+			mem_end = PFN_ALIGN(mem_end - PAGE_SIZE + 1);
+			num_physpages += (mem_size >> PAGE_SHIFT);
+			memblock_add(loongson_mem_map->map[i].mem_start,
+				     loongson_mem_map->map[i].mem_size);
+			memblock_set_node(mem_start, mem_size, &memblock.memory, 0);
+			break;
+		case ADDRESS_TYPE_ACPI:
+		case ADDRESS_TYPE_RESERVED:
+			memblock_reserve(loongson_mem_map->map[i].mem_start,
+					 loongson_mem_map->map[i].mem_size);
+			break;
+		}
+	}
+
+	get_pfn_range_for_nid(0, &start_pfn, &end_pfn);
+	pr_info("start_pfn=0x%lx, end_pfn=0x%lx, num_physpages:0x%lx\n",
+				start_pfn, end_pfn, num_physpages);
+
+	NODE_DATA(0)->node_start_pfn = start_pfn;
+	NODE_DATA(0)->node_spanned_pages = end_pfn - start_pfn;
+
+	/* used by finalize_initrd() */
+	max_low_pfn = end_pfn;
+
+	/* Reserve the first 2MB */
+	memblock_reserve(PHYS_OFFSET, 0x200000);
+
+	/* Reserve the kernel text/data/bss */
+	memblock_reserve(__pa_symbol(&_text),
+			 __pa_symbol(&_end) - __pa_symbol(&_text));
+}
diff --git a/arch/loongarch/loongson64/msi.c b/arch/loongarch/loongson64/msi.c
new file mode 100644
index 000000000000..26b7a59cba85
--- /dev/null
+++ b/arch/loongarch/loongson64/msi.c
@@ -0,0 +1,41 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/msi.h>
+#include <linux/pci.h>
+#include <linux/spinlock.h>
+
+static bool msix_enable = 1;
+core_param(msix, msix_enable, bool, 0664);
+
+int arch_setup_msi_irqs(struct pci_dev *dev, int nvec, int type)
+{
+	struct irq_domain *msi_domain;
+
+	if (!pci_msi_enabled())
+		return -ENOSPC;
+
+	if (type == PCI_CAP_ID_MSIX && !msix_enable)
+		return -ENOSPC;
+
+	if (type == PCI_CAP_ID_MSI && nvec > 1)
+		return 1;
+
+	msi_domain = irq_find_matching_fwnode(acpi_msidomain_handle, DOMAIN_BUS_PCI_MSI);
+	if (!msi_domain)
+		return -ENOSPC;
+
+	return msi_domain_alloc_irqs(msi_domain, &dev->dev, nvec);
+
+}
+
+void arch_teardown_msi_irq(unsigned int irq)
+{
+	struct irq_domain *msi_domain;
+
+	msi_domain = irq_find_matching_fwnode(acpi_msidomain_handle, DOMAIN_BUS_PCI_MSI);
+	if (msi_domain)
+		irq_domain_free_irqs(irq, 1);
+}
diff --git a/arch/loongarch/loongson64/reset.c b/arch/loongarch/loongson64/reset.c
new file mode 100644
index 000000000000..0ca20679a0a7
--- /dev/null
+++ b/arch/loongarch/loongson64/reset.c
@@ -0,0 +1,54 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Huacai Chen, chenhuacai@loongson.cn
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/acpi.h>
+#include <linux/cpu.h>
+#include <linux/delay.h>
+#include <linux/efi.h>
+#include <linux/init.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <acpi/reboot.h>
+#include <asm/bootinfo.h>
+#include <asm/delay.h>
+#include <asm/idle.h>
+#include <asm/reboot.h>
+#include <boot_param.h>
+
+static void loongson_restart(void)
+{
+#ifdef CONFIG_EFI
+	if (efi_capsule_pending(NULL))
+		efi_reboot(REBOOT_WARM, NULL);
+	else
+		efi_reboot(REBOOT_COLD, NULL);
+#endif
+	if (!acpi_disabled)
+		acpi_reboot();
+
+	while (1) {
+		__arch_cpu_idle();
+	}
+}
+
+static void loongson_poweroff(void)
+{
+#ifdef CONFIG_EFI
+	efi.reset_system(EFI_RESET_SHUTDOWN, EFI_SUCCESS, 0, NULL);
+#endif
+	while (1) {
+		__arch_cpu_idle();
+	}
+}
+
+static int __init loongarch_reboot_setup(void)
+{
+	pm_restart = loongson_restart;
+	pm_power_off = loongson_poweroff;
+
+	return 0;
+}
+
+arch_initcall(loongarch_reboot_setup);
diff --git a/arch/loongarch/loongson64/rtc.c b/arch/loongarch/loongson64/rtc.c
new file mode 100644
index 000000000000..645a4b40dcc0
--- /dev/null
+++ b/arch/loongarch/loongson64/rtc.c
@@ -0,0 +1,36 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <loongson.h>
+
+#define RTC_TOYREAD0    0x2C
+#define RTC_YEAR        0x30
+
+unsigned long loongson_get_rtc_time(void)
+{
+	unsigned int year, mon, day, hour, min, sec;
+	unsigned int value;
+
+	value = ls7a_readl(LS7A_RTC_REG_BASE + RTC_TOYREAD0);
+	sec = (value >> 4) & 0x3f;
+	min = (value >> 10) & 0x3f;
+	hour = (value >> 16) & 0x1f;
+	day = (value >> 21) & 0x1f;
+	mon = (value >> 26) & 0x3f;
+	year = ls7a_readl(LS7A_RTC_REG_BASE + RTC_YEAR);
+
+	year = 1900 + year;
+
+	return mktime64(year, mon, day, hour, min, sec);
+}
+
+void read_persistent_clock64(struct timespec64 *ts)
+{
+	ts->tv_sec = loongson_get_rtc_time();
+	ts->tv_nsec = 0;
+}
diff --git a/arch/loongarch/loongson64/setup.c b/arch/loongarch/loongson64/setup.c
new file mode 100644
index 000000000000..941089d40c43
--- /dev/null
+++ b/arch/loongarch/loongson64/setup.c
@@ -0,0 +1,37 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/export.h>
+#include <linux/init.h>
+#include <asm/bootinfo.h>
+
+#ifdef CONFIG_VT
+#include <linux/console.h>
+#include <linux/screen_info.h>
+#include <linux/platform_device.h>
+#endif
+
+#include <loongson.h>
+
+const char *get_system_type(void)
+{
+	return "generic-loongson-machine";
+}
+
+void __init plat_mem_setup(void)
+{
+}
+
+static int __init register_gop_device(void)
+{
+	void *pd;
+
+	if (screen_info.orig_video_isVGA != VIDEO_TYPE_EFI)
+		return 0;
+	pd = platform_device_register_data(NULL, "efi-framebuffer", 0,
+			&screen_info, sizeof(screen_info));
+	return PTR_ERR_OR_ZERO(pd);
+}
+subsys_initcall(register_gop_device);
-- 
2.27.0


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

* [PATCH 15/19] LoongArch: Add PCI controller support
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (12 preceding siblings ...)
  2021-07-06  4:18 ` [PATCH 14/19] LoongArch: Add 64-bit Loongson platform Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06 10:17   ` Arnd Bergmann
  2021-07-06  4:18 ` [PATCH 16/19] LoongArch: Add VDSO and VSYSCALL support Huacai Chen
                   ` (6 subsequent siblings)
  20 siblings, 1 reply; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

Loongson64 based systems are PC-like systems which use PCI/PCIe as its
I/O bus, This patch adds the PCI host controller support for LoongArch.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 arch/loongarch/include/asm/pci.h  | 124 ++++++++++++++++++
 arch/loongarch/pci/acpi.c         | 194 ++++++++++++++++++++++++++++
 arch/loongarch/pci/mmconfig.c     | 105 +++++++++++++++
 arch/loongarch/pci/pci-loongson.c | 130 +++++++++++++++++++
 arch/loongarch/pci/pci.c          | 207 ++++++++++++++++++++++++++++++
 5 files changed, 760 insertions(+)
 create mode 100644 arch/loongarch/include/asm/pci.h
 create mode 100644 arch/loongarch/pci/acpi.c
 create mode 100644 arch/loongarch/pci/mmconfig.c
 create mode 100644 arch/loongarch/pci/pci-loongson.c
 create mode 100644 arch/loongarch/pci/pci.c

diff --git a/arch/loongarch/include/asm/pci.h b/arch/loongarch/include/asm/pci.h
new file mode 100644
index 000000000000..0b73a1e76280
--- /dev/null
+++ b/arch/loongarch/include/asm/pci.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_PCI_H
+#define _ASM_PCI_H
+
+#include <linux/mm.h>
+
+#ifdef __KERNEL__
+
+/*
+ * This file essentially defines the interface between board specific
+ * PCI code and LoongArch common PCI code. Should potentially put into
+ * include/asm/pci.h file.
+ */
+
+#include <linux/ioport.h>
+#include <linux/list.h>
+
+extern const struct pci_ops *__read_mostly loongarch_pci_ops;
+
+/*
+ * Each pci channel is a top-level PCI bus seem by CPU.	 A machine  with
+ * multiple PCI channels may have multiple PCI host controllers or a
+ * single controller supporting multiple channels.
+ */
+struct pci_controller {
+	struct list_head list;
+	struct pci_bus *bus;
+	struct device_node *of_node;
+
+	struct pci_ops *pci_ops;
+	struct resource *mem_resource;
+	unsigned long mem_offset;
+	struct resource *io_resource;
+	unsigned long io_offset;
+	unsigned long io_map_base;
+	struct resource *busn_resource;
+
+	unsigned int node;
+	unsigned int index;
+	unsigned int need_domain_info;
+#ifdef CONFIG_ACPI
+	struct acpi_device *companion;
+#endif
+	phys_addr_t mcfg_addr;
+};
+
+extern void pcibios_add_root_resources(struct list_head *resources);
+
+extern phys_addr_t mcfg_addr_init(int domain);
+
+#ifdef CONFIG_PCI_DOMAINS
+static inline void set_pci_need_domain_info(struct pci_controller *hose,
+					    int need_domain_info)
+{
+	hose->need_domain_info = need_domain_info;
+}
+#endif /* CONFIG_PCI_DOMAINS */
+
+/*
+ * Can be used to override the logic in pci_scan_bus for skipping
+ * already-configured bus numbers - to be used for buggy BIOSes
+ * or architectures with incomplete PCI setup by the loader
+ */
+static inline unsigned int pcibios_assign_all_busses(void)
+{
+	return 1;
+}
+
+#define PCIBIOS_MIN_IO		0
+#define PCIBIOS_MIN_MEM		0x20000000
+#define PCIBIOS_MIN_CARDBUS_IO	0x4000
+
+#define HAVE_PCI_MMAP
+#define ARCH_GENERIC_PCI_MMAP_RESOURCE
+#define HAVE_ARCH_PCI_RESOURCE_TO_USER
+
+/*
+ * Dynamic DMA mapping stuff.
+ * LoongArch has everything mapped statically.
+ */
+
+#include <linux/types.h>
+#include <linux/slab.h>
+#include <linux/scatterlist.h>
+#include <linux/string.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_PCI_DOMAINS
+#define pci_domain_nr(bus) (((struct pci_controller *)(bus)->sysdata)->index)
+
+static inline int pci_proc_domain(struct pci_bus *bus)
+{
+	struct pci_controller *hose = bus->sysdata;
+
+	return hose->need_domain_info;
+}
+#endif /* CONFIG_PCI_DOMAINS */
+
+/* mmconfig.c */
+
+#ifdef CONFIG_PCI_MMCONFIG
+struct pci_mmcfg_region {
+	struct list_head list;
+	phys_addr_t address;
+	u16 segment;
+	u8 start_bus;
+	u8 end_bus;
+};
+
+extern phys_addr_t pci_mmconfig_addr(u16 seg, u8 start);
+extern int pci_mmconfig_delete(u16 seg, u8 start, u8 end);
+extern struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus);
+extern struct list_head pci_mmcfg_list;
+#endif /*CONFIG_PCI_MMCONFIG*/
+
+#endif /* __KERNEL__ */
+
+/* generic pci stuff */
+#include <asm-generic/pci.h>
+
+#endif /* _ASM_PCI_H */
diff --git a/arch/loongarch/pci/acpi.c b/arch/loongarch/pci/acpi.c
new file mode 100644
index 000000000000..68e4c3f5e88f
--- /dev/null
+++ b/arch/loongarch/pci/acpi.c
@@ -0,0 +1,194 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/dmi.h>
+#include <linux/slab.h>
+#include <linux/pci-acpi.h>
+
+#include <asm/pci.h>
+#include <loongson.h>
+
+struct pci_root_info {
+	struct acpi_pci_root_info common;
+	struct pci_controller pc;
+#ifdef CONFIG_PCI_MMCONFIG
+	bool mcfg_added;
+	u8 start_bus;
+	u8 end_bus;
+#endif
+};
+
+void pcibios_add_bus(struct pci_bus *bus)
+{
+	acpi_pci_add_bus(bus);
+}
+
+int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
+{
+	struct pci_controller *pc = bridge->bus->sysdata;
+
+	ACPI_COMPANION_SET(&bridge->dev, pc->companion);
+	return 0;
+}
+
+#ifdef CONFIG_PCI_MMCONFIG
+static void teardown_mcfg(struct acpi_pci_root_info *ci)
+{
+	struct pci_root_info *info;
+
+	info = container_of(ci, struct pci_root_info, common);
+	if (info->mcfg_added) {
+		pci_mmconfig_delete(info->pc.index,
+				    info->start_bus, info->end_bus);
+		info->mcfg_added = false;
+	}
+}
+#else
+static void teardown_mcfg(struct acpi_pci_root_info *ci)
+{
+}
+#endif
+
+static void acpi_release_root_info(struct acpi_pci_root_info *info)
+{
+	if (info) {
+		teardown_mcfg(info);
+		kfree(container_of(info, struct pci_root_info, common));
+	}
+}
+
+static int acpi_prepare_root_resources(struct acpi_pci_root_info *ci)
+{
+	struct acpi_device *device = ci->bridge;
+	struct resource_entry *entry, *tmp;
+	int status;
+
+	status = acpi_pci_probe_root_resources(ci);
+	if (status > 0)
+		return status;
+
+	resource_list_for_each_entry_safe(entry, tmp, &ci->resources) {
+		dev_dbg(&device->dev,
+			   "host bridge window %pR (ignored)\n", entry->res);
+		resource_list_destroy_entry(entry);
+	}
+
+	pcibios_add_root_resources(&ci->resources);
+
+	return 0;
+}
+
+static int pci_read(struct pci_bus *bus,
+		unsigned int devfn,
+		int where, int size, u32 *value)
+{
+	return loongarch_pci_ops->read(bus,
+				 devfn, where, size, value);
+}
+
+static int pci_write(struct pci_bus *bus,
+		unsigned int devfn,
+		int where, int size, u32 value)
+{
+	return loongarch_pci_ops->write(bus,
+				  devfn, where, size, value);
+}
+
+struct pci_ops pci_root_ops = {
+	.read = pci_read,
+	.write = pci_write,
+};
+static struct acpi_pci_root_ops acpi_pci_root_ops = {
+	.pci_ops = &pci_root_ops,
+	.release_info = acpi_release_root_info,
+	.prepare_resources = acpi_prepare_root_resources,
+};
+
+static void init_controller_resources(struct pci_controller *controller,
+		struct pci_bus *bus)
+{
+	struct resource_entry *entry, *tmp;
+	struct resource *res;
+
+	controller->io_map_base = loongarch_io_port_base;
+
+	resource_list_for_each_entry_safe(entry, tmp, &bus->resources) {
+		res = entry->res;
+		if (res->flags & IORESOURCE_MEM)
+			controller->mem_resource = res;
+		else if (res->flags & IORESOURCE_IO)
+			controller->io_resource = res;
+		else
+			continue;
+	}
+}
+
+struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
+{
+	struct pci_bus *bus;
+	struct pci_root_info *info;
+	struct pci_controller *controller;
+	int domain = root->segment;
+	int busnum = root->secondary.start;
+	static int need_domain_info;
+	struct acpi_device *device = root->device;
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info) {
+		pr_warn("pci_bus %04x:%02x: ignored (out of memory)\n", domain, busnum);
+		return NULL;
+	}
+
+	controller = &info->pc;
+	controller->companion = device;
+	controller->index = domain;
+
+#ifdef CONFIG_PCI_MMCONFIG
+	root->mcfg_addr = pci_mmconfig_addr(domain, busnum);
+	controller->mcfg_addr = pci_mmconfig_addr(domain, busnum);
+#endif
+	if (!controller->mcfg_addr)
+		controller->mcfg_addr = mcfg_addr_init(controller->index);
+
+	controller->node = 0;
+
+	bus = pci_find_bus(domain, busnum);
+	if (bus) {
+		memcpy(bus->sysdata, controller, sizeof(struct pci_controller));
+		kfree(info);
+	} else {
+		bus = acpi_pci_root_create(root, &acpi_pci_root_ops,
+					   &info->common, controller);
+		if (bus) {
+			need_domain_info = need_domain_info || pci_domain_nr(bus);
+			set_pci_need_domain_info(controller, need_domain_info);
+			init_controller_resources(controller, bus);
+
+			/*
+			 * We insert PCI resources into the iomem_resource
+			 * and ioport_resource trees in either
+			 * pci_bus_claim_resources() or
+			 * pci_bus_assign_resources().
+			 */
+			if (pci_has_flag(PCI_PROBE_ONLY)) {
+				pci_bus_claim_resources(bus);
+			} else {
+				struct pci_bus *child;
+
+				pci_bus_size_bridges(bus);
+				pci_bus_assign_resources(bus);
+				list_for_each_entry(child, &bus->children, node)
+					pcie_bus_configure_settings(child);
+			}
+		} else {
+			kfree(info);
+		}
+	}
+
+	return bus;
+}
diff --git a/arch/loongarch/pci/mmconfig.c b/arch/loongarch/pci/mmconfig.c
new file mode 100644
index 000000000000..d65820e3068a
--- /dev/null
+++ b/arch/loongarch/pci/mmconfig.c
@@ -0,0 +1,105 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/pci.h>
+#include <linux/acpi.h>
+#include <linux/init.h>
+#include <linux/dmi.h>
+#include <linux/slab.h>
+#include <linux/pci-acpi.h>
+#include <asm/pci.h>
+#include <loongson.h>
+
+#define PREFIX "PCI: "
+
+static DEFINE_MUTEX(pci_mmcfg_lock);
+
+LIST_HEAD(pci_mmcfg_list);
+
+struct pci_mmcfg_region *pci_mmconfig_lookup(int segment, int bus)
+{
+	struct pci_mmcfg_region *cfg;
+
+	list_for_each_entry(cfg, &pci_mmcfg_list, list)
+		if (cfg->segment == segment &&
+		    cfg->start_bus <= bus && bus <= cfg->end_bus)
+			return cfg;
+
+	return NULL;
+}
+
+static int __init pci_parse_mcfg(struct acpi_table_header *header)
+{
+	struct acpi_table_mcfg *mcfg;
+	struct acpi_mcfg_allocation *cfg_table, *cfg;
+	struct pci_mmcfg_region *new, *arr;
+	unsigned long i;
+	int entries;
+
+	if (header->length < sizeof(struct acpi_table_mcfg))
+		return -EINVAL;
+
+	/* how many config structures do we have */
+	entries = (header->length - sizeof(struct acpi_table_mcfg)) /
+				sizeof(struct acpi_mcfg_allocation);
+
+
+	mcfg = (struct acpi_table_mcfg *)header;
+	cfg_table = (struct acpi_mcfg_allocation *) &mcfg[1];
+
+	arr = kcalloc(entries, sizeof(*arr), GFP_KERNEL);
+	if (!arr)
+		return -ENOMEM;
+
+	for (i = 0, new = arr; i < entries; i++, new++) {
+		cfg = &cfg_table[i];
+		new->address = cfg->address;
+		new->segment = cfg->pci_segment;
+		new->start_bus = cfg->start_bus_number;
+		new->end_bus = cfg->end_bus_number;
+
+		list_add(&new->list, &pci_mmcfg_list);
+		pr_info(PREFIX
+		       "MMCONFIG for domain %04x [bus %02x-%02x](base %#lx)\n",
+		       new->segment, new->start_bus, new->end_bus, (unsigned long)(new->address));
+	}
+
+	return 0;
+}
+
+void __init pci_mmcfg_late_init(void)
+{
+	int err;
+
+	err = acpi_table_parse(ACPI_SIG_MCFG, pci_parse_mcfg);
+	if (err)
+		pr_err("Failed to parse MCFG (%d)\n", err);
+}
+
+phys_addr_t pci_mmconfig_addr(u16 seg, u8 start)
+{
+	struct pci_mmcfg_region *cfg;
+
+	cfg = pci_mmconfig_lookup(seg, start);
+	if (!cfg)
+		return (phys_addr_t)NULL;
+
+	return cfg->address;
+}
+
+/* Delete MMCFG information for host bridges */
+int pci_mmconfig_delete(u16 seg, u8 start, u8 end)
+{
+	struct pci_mmcfg_region *cfg;
+
+	list_for_each_entry(cfg, &pci_mmcfg_list, list)
+		if (cfg->segment == seg && cfg->start_bus == start &&
+		    cfg->end_bus == end) {
+			list_del(&cfg->list);
+			kfree(cfg);
+			return 0;
+		}
+
+	return -ENOENT;
+}
diff --git a/arch/loongarch/pci/pci-loongson.c b/arch/loongarch/pci/pci-loongson.c
new file mode 100644
index 000000000000..e74f887a330e
--- /dev/null
+++ b/arch/loongarch/pci/pci-loongson.c
@@ -0,0 +1,130 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/vgaarb.h>
+
+#include <loongson.h>
+
+#define PCI_ACCESS_READ  0
+#define PCI_ACCESS_WRITE 1
+
+static int ls7a_pci_config_access(unsigned char access_type,
+		struct pci_bus *bus, unsigned int devfn,
+		int where, u32 *data)
+{
+	unsigned char busnum = bus->number;
+	int device = PCI_SLOT(devfn);
+	int function = PCI_FUNC(devfn);
+	int reg = where & ~3;
+	u_int64_t addr;
+	void *addrp;
+
+	/*
+	 * Filter out non-supported devices.
+	 * device 2: misc, device 21: confbus
+	 */
+	if (where < PCI_CFG_SPACE_SIZE) { /* standard config */
+		addr = (busnum << 16) | (device << 11) | (function << 8) | reg;
+		if (busnum == 0) {
+			if (device > 23 || (device >= 9 && device <= 20 && function == 1))
+				return PCIBIOS_DEVICE_NOT_FOUND;
+			addrp = (void *)TO_UNCAC(HT1LO_PCICFG_BASE | addr);
+		} else {
+			addrp = (void *)TO_UNCAC(HT1LO_PCICFG_BASE_TP1 | addr);
+		}
+	} else if (where < PCI_CFG_SPACE_EXP_SIZE) {  /* extended config */
+		reg = (reg & 0xff) | ((reg & 0xf00) << 16);
+		addr = (busnum << 16) | (device << 11) | (function << 8) | reg;
+		if (busnum == 0) {
+			if (device > 23 || (device >= 9 && device <= 20 && function == 1))
+				return PCIBIOS_DEVICE_NOT_FOUND;
+			addrp = (void *)TO_UNCAC(HT1LO_EXT_PCICFG_BASE | addr);
+		} else {
+			addrp = (void *)TO_UNCAC(HT1LO_EXT_PCICFG_BASE_TP1 | addr);
+		}
+	} else {
+		return PCIBIOS_DEVICE_NOT_FOUND;
+	}
+
+	if (access_type == PCI_ACCESS_WRITE)
+		writel(*data, addrp);
+	else {
+		*data = readl(addrp);
+		if (*data == 0xffffffff) {
+			*data = -1;
+			return PCIBIOS_DEVICE_NOT_FOUND;
+		}
+	}
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int ls7a_pci_pcibios_read(struct pci_bus *bus, unsigned int devfn,
+				 int where, int size, u32 *val)
+{
+	u32 data = 0;
+	int ret = ls7a_pci_config_access(PCI_ACCESS_READ,
+			bus, devfn, where, &data);
+
+	if (size == 1)
+		*val = (data >> ((where & 3) << 3)) & 0xff;
+	else if (size == 2)
+		*val = (data >> ((where & 3) << 3)) & 0xffff;
+	else
+		*val = data;
+
+	return ret;
+}
+
+static int ls7a_pci_pcibios_write(struct pci_bus *bus, unsigned int devfn,
+				  int where, int size, u32 val)
+{
+	int ret;
+	u32 data = 0;
+
+	if (size == 4)
+		data = val;
+	else {
+		ret = ls7a_pci_config_access(PCI_ACCESS_READ,
+				bus, devfn, where, &data);
+		if (ret != PCIBIOS_SUCCESSFUL)
+			return ret;
+
+		if (size == 1)
+			data = (data & ~(0xff << ((where & 3) << 3))) |
+			    (val << ((where & 3) << 3));
+		else if (size == 2)
+			data = (data & ~(0xffff << ((where & 3) << 3))) |
+			    (val << ((where & 3) << 3));
+	}
+
+	ret = ls7a_pci_config_access(PCI_ACCESS_WRITE,
+			bus, devfn, where, &data);
+
+	return ret;
+}
+
+struct pci_ops ls7a_pci_ops = {
+	.read = ls7a_pci_pcibios_read,
+	.write = ls7a_pci_pcibios_write
+};
+
+static void pci_fixup_vgadev(struct pci_dev *pdev)
+{
+	struct pci_dev *devp = NULL;
+
+	while ((devp = pci_get_class(PCI_CLASS_DISPLAY_VGA << 8, devp))) {
+		if (devp->vendor != PCI_VENDOR_ID_LOONGSON) {
+			vga_set_default_device(devp);
+			dev_info(&pdev->dev,
+				"Overriding boot device as %X:%X\n",
+				devp->vendor, devp->device);
+		}
+	}
+}
+DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_DC, pci_fixup_vgadev);
diff --git a/arch/loongarch/pci/pci.c b/arch/loongarch/pci/pci.c
new file mode 100644
index 000000000000..afdf70fc8770
--- /dev/null
+++ b/arch/loongarch/pci/pci.c
@@ -0,0 +1,207 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/acpi.h>
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/of_address.h>
+#include <loongson.h>
+
+const struct pci_ops *__read_mostly loongarch_pci_ops;
+
+int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn,
+						int reg, int len, u32 *val)
+{
+	struct pci_bus *bus_tmp = pci_find_bus(domain, bus);
+
+	if (bus_tmp)
+		return bus_tmp->ops->read(bus_tmp, devfn, reg, len, val);
+	return -EINVAL;
+}
+
+int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,
+						int reg, int len, u32 val)
+{
+	struct pci_bus *bus_tmp = pci_find_bus(domain, bus);
+
+	if (bus_tmp)
+		return bus_tmp->ops->write(bus_tmp, devfn, reg, len, val);
+	return -EINVAL;
+}
+
+void pci_resource_to_user(const struct pci_dev *dev, int bar,
+			  const struct resource *rsrc, resource_size_t *start,
+			  resource_size_t *end)
+{
+	phys_addr_t size = resource_size(rsrc);
+
+	*start = rsrc->start;
+	*end = rsrc->start + size - 1;
+}
+
+/*
+ * We need to avoid collisions with `mirrored' VGA ports
+ * and other strange ISA hardware, so we always want the
+ * addresses to be allocated in the 0x000-0x0ff region
+ * modulo 0x400.
+ *
+ * Why? Because some silly external IO cards only decode
+ * the low 10 bits of the IO address. The 0x00-0xff region
+ * is reserved for motherboard devices that decode all 16
+ * bits, so it's ok to allocate at, say, 0x2800-0x28ff,
+ * but we want to try to avoid allocating at 0x2900-0x2bff
+ * which might have be mirrored at 0x0100-0x03ff..
+ */
+resource_size_t
+pcibios_align_resource(void *data, const struct resource *res,
+		       resource_size_t size, resource_size_t align)
+{
+	struct pci_dev *dev = data;
+	struct pci_controller *hose = dev->sysdata;
+	resource_size_t start = res->start;
+
+	if (res->flags & IORESOURCE_IO) {
+		/* Make sure we start at our min on all hoses */
+		if (start < PCIBIOS_MIN_IO + hose->io_resource->start)
+			start = PCIBIOS_MIN_IO + hose->io_resource->start;
+
+		/*
+		 * Put everything into 0x00-0xff region modulo 0x400
+		 */
+		if (start & 0x300)
+			start = (start + 0x3ff) & ~0x3ff;
+	} else if (res->flags & IORESOURCE_MEM) {
+		/* Make sure we start at our min on all hoses */
+		if (start < PCIBIOS_MIN_MEM)
+			start = PCIBIOS_MIN_MEM;
+	}
+
+	return start;
+}
+
+phys_addr_t mcfg_addr_init(int domain)
+{
+	return (((u64) domain << 44) | MCFG_EXT_PCICFG_BASE);
+}
+
+static struct resource pci_mem_resource = {
+	.name	= "pci memory space",
+	.flags	= IORESOURCE_MEM,
+};
+
+static struct resource pci_io_resource = {
+	.name	= "pci io space",
+	.flags	= IORESOURCE_IO,
+};
+
+void pcibios_add_root_resources(struct list_head *resources)
+{
+	if (resources) {
+		pci_add_resource(resources, &pci_mem_resource);
+		pci_add_resource(resources, &pci_io_resource);
+	}
+}
+
+static int __init pcibios_set_cache_line_size(void)
+{
+	unsigned int lsize;
+
+	/*
+	 * Set PCI cacheline size to that of the highest level in the
+	 * cache hierarchy.
+	 */
+	lsize = cpu_dcache_line_size();
+	lsize = cpu_vcache_line_size() ? : lsize;
+	lsize = cpu_scache_line_size() ? : lsize;
+
+	BUG_ON(!lsize);
+
+	pci_dfl_cache_line_size = lsize >> 2;
+
+	pr_debug("PCI: pci_cache_line_size set to %d bytes\n", lsize);
+
+	return 0;
+}
+
+static int __init pcibios_init(void)
+{
+	return pcibios_set_cache_line_size();
+}
+
+subsys_initcall(pcibios_init);
+
+static int pcibios_enable_resources(struct pci_dev *dev, int mask)
+{
+	int idx;
+	u16 cmd, old_cmd;
+	struct resource *r;
+
+	pci_read_config_word(dev, PCI_COMMAND, &cmd);
+	old_cmd = cmd;
+
+	for (idx = 0; idx < PCI_NUM_RESOURCES; idx++) {
+		/* Only set up the requested stuff */
+		if (!(mask & (1<<idx)))
+			continue;
+
+		r = &dev->resource[idx];
+		if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM)))
+			continue;
+		if ((idx == PCI_ROM_RESOURCE) &&
+				(!(r->flags & IORESOURCE_ROM_ENABLE)))
+			continue;
+		if (!r->start && r->end) {
+			pci_err(dev,
+				"can't enable device: resource collisions\n");
+			return -EINVAL;
+		}
+		if (r->flags & IORESOURCE_IO)
+			cmd |= PCI_COMMAND_IO;
+		if (r->flags & IORESOURCE_MEM)
+			cmd |= PCI_COMMAND_MEMORY;
+	}
+
+	if (cmd != old_cmd) {
+		pci_info(dev, "enabling device (%04x -> %04x)\n", old_cmd, cmd);
+		pci_write_config_word(dev, PCI_COMMAND, cmd);
+	}
+
+	return 0;
+}
+
+int pcibios_dev_init(struct pci_dev *dev)
+{
+#ifdef CONFIG_ACPI
+	if (acpi_disabled)
+		return 0;
+	if (pci_dev_msi_enabled(dev))
+		return 0;
+	return acpi_pci_irq_enable(dev);
+#endif
+}
+
+int pcibios_enable_device(struct pci_dev *dev, int mask)
+{
+	int err;
+
+	err = pcibios_enable_resources(dev, mask);
+	if (err < 0)
+		return err;
+
+	return pcibios_dev_init(dev);
+}
+
+void pcibios_fixup_bus(struct pci_bus *bus)
+{
+	struct pci_dev *dev = bus->self;
+
+	if (pci_has_flag(PCI_PROBE_ONLY) && dev &&
+	    (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) {
+		pci_read_bridge_bases(bus);
+	}
+}
-- 
2.27.0


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

* [PATCH 16/19] LoongArch: Add VDSO and VSYSCALL support
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (13 preceding siblings ...)
  2021-07-06  4:18 ` [PATCH 15/19] LoongArch: Add PCI controller support Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06 10:17   ` Arnd Bergmann
  2021-07-06  4:18 ` [PATCH 17/19] LoongArch: Add multi-processor (SMP) support Huacai Chen
                   ` (5 subsequent siblings)
  20 siblings, 1 reply; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

This patch adds VDSO and VSYSCALL support (gettimeofday and its friends)
for LoongArch.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 arch/loongarch/include/asm/vdso.h             |  50 +++
 arch/loongarch/include/asm/vdso/clocksource.h |   8 +
 .../loongarch/include/asm/vdso/gettimeofday.h | 101 ++++++
 arch/loongarch/include/asm/vdso/processor.h   |  14 +
 arch/loongarch/include/asm/vdso/vdso.h        |  42 +++
 arch/loongarch/include/asm/vdso/vsyscall.h    |  27 ++
 arch/loongarch/kernel/vdso.c                  | 132 ++++++++
 arch/loongarch/vdso/Makefile                  |  97 ++++++
 arch/loongarch/vdso/elf.S                     |  15 +
 arch/loongarch/vdso/genvdso.c                 | 311 ++++++++++++++++++
 arch/loongarch/vdso/genvdso.h                 | 122 +++++++
 arch/loongarch/vdso/sigreturn.S               |  24 ++
 arch/loongarch/vdso/vdso.lds.S                |  65 ++++
 arch/loongarch/vdso/vgettimeofday.c           |  26 ++
 14 files changed, 1034 insertions(+)
 create mode 100644 arch/loongarch/include/asm/vdso.h
 create mode 100644 arch/loongarch/include/asm/vdso/clocksource.h
 create mode 100644 arch/loongarch/include/asm/vdso/gettimeofday.h
 create mode 100644 arch/loongarch/include/asm/vdso/processor.h
 create mode 100644 arch/loongarch/include/asm/vdso/vdso.h
 create mode 100644 arch/loongarch/include/asm/vdso/vsyscall.h
 create mode 100644 arch/loongarch/kernel/vdso.c
 create mode 100644 arch/loongarch/vdso/Makefile
 create mode 100644 arch/loongarch/vdso/elf.S
 create mode 100644 arch/loongarch/vdso/genvdso.c
 create mode 100644 arch/loongarch/vdso/genvdso.h
 create mode 100644 arch/loongarch/vdso/sigreturn.S
 create mode 100644 arch/loongarch/vdso/vdso.lds.S
 create mode 100644 arch/loongarch/vdso/vgettimeofday.c

diff --git a/arch/loongarch/include/asm/vdso.h b/arch/loongarch/include/asm/vdso.h
new file mode 100644
index 000000000000..d137ee9b217a
--- /dev/null
+++ b/arch/loongarch/include/asm/vdso.h
@@ -0,0 +1,50 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef __ASM_VDSO_H
+#define __ASM_VDSO_H
+
+#include <linux/mm_types.h>
+#include <vdso/datapage.h>
+
+#include <asm/barrier.h>
+
+/**
+ * struct loongarch_vdso_image - Details of a VDSO image.
+ * @data: Pointer to VDSO image data (page-aligned).
+ * @size: Size of the VDSO image data (page-aligned).
+ * @off_sigreturn: Offset of the sigreturn() trampoline.
+ * @off_rt_sigreturn: Offset of the rt_sigreturn() trampoline.
+ * @mapping: Special mapping structure.
+ *
+ * This structure contains details of a VDSO image, including the image data
+ * and offsets of certain symbols required by the kernel. It is generated as
+ * part of the VDSO build process, aside from the mapping page array, which is
+ * populated at runtime.
+ */
+struct loongarch_vdso_image {
+	void *data;
+	unsigned long size;
+
+	unsigned long off_sigreturn;
+	unsigned long off_rt_sigreturn;
+
+	struct vm_special_mapping mapping;
+};
+
+/*
+ * The following structures are auto-generated as part of the build for each
+ * ABI by genvdso, see arch/loongarch/vdso/Makefile.
+ */
+
+extern struct loongarch_vdso_image vdso_image;
+
+union loongarch_vdso_data {
+	struct vdso_data data[CS_BASES];
+	u8 page[PAGE_SIZE];
+};
+
+#endif /* __ASM_VDSO_H */
diff --git a/arch/loongarch/include/asm/vdso/clocksource.h b/arch/loongarch/include/asm/vdso/clocksource.h
new file mode 100644
index 000000000000..13cd580d406d
--- /dev/null
+++ b/arch/loongarch/include/asm/vdso/clocksource.h
@@ -0,0 +1,8 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+#ifndef __ASM_VDSOCLOCKSOURCE_H
+#define __ASM_VDSOCLOCKSOURCE_H
+
+#define VDSO_ARCH_CLOCKMODES	\
+	VDSO_CLOCKMODE_CPU
+
+#endif /* __ASM_VDSOCLOCKSOURCE_H */
diff --git a/arch/loongarch/include/asm/vdso/gettimeofday.h b/arch/loongarch/include/asm/vdso/gettimeofday.h
new file mode 100644
index 000000000000..d86bfd3a890e
--- /dev/null
+++ b/arch/loongarch/include/asm/vdso/gettimeofday.h
@@ -0,0 +1,101 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef __ASM_VDSO_GETTIMEOFDAY_H
+#define __ASM_VDSO_GETTIMEOFDAY_H
+
+#ifndef __ASSEMBLY__
+
+#include <asm/vdso/vdso.h>
+#include <asm/clocksource.h>
+#include <asm/unistd.h>
+#include <asm/vdso.h>
+
+#define VDSO_HAS_CLOCK_GETRES		1
+
+static __always_inline long gettimeofday_fallback(
+				struct __kernel_old_timeval *_tv,
+				struct timezone *_tz)
+{
+	register struct __kernel_old_timeval *tv asm("a0") = _tv;
+	register struct timezone *tz asm("a1") = _tz;
+	register long nr asm("a7") = __NR_gettimeofday;
+	register long ret asm("v0");
+
+	asm volatile(
+	"       syscall 0\n"
+	: "=r" (ret)
+	: "r" (nr), "r" (tv), "r" (tz)
+	: "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
+	  "$t8", "memory");
+
+	return ret;
+}
+
+static __always_inline long clock_gettime_fallback(
+					clockid_t _clkid,
+					struct __kernel_timespec *_ts)
+{
+	register clockid_t clkid asm("a0") = _clkid;
+	register struct __kernel_timespec *ts asm("a1") = _ts;
+	register long nr asm("a7") = __NR_clock_gettime;
+	register long ret asm("v0");
+
+	asm volatile(
+	"       syscall 0\n"
+	: "=r" (ret)
+	: "r" (nr), "r" (clkid), "r" (ts)
+	: "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
+	  "$t8", "memory");
+
+	return ret;
+}
+
+static __always_inline int clock_getres_fallback(
+					clockid_t _clkid,
+					struct __kernel_timespec *_ts)
+{
+	register clockid_t clkid asm("a0") = _clkid;
+	register struct __kernel_timespec *ts asm("a1") = _ts;
+	register long nr asm("a7") = __NR_clock_getres;
+	register long ret asm("v0");
+
+	asm volatile(
+	"       syscall 0\n"
+	: "=r" (ret)
+	: "r" (nr), "r" (clkid), "r" (ts)
+	: "$t0", "$t1", "$t2", "$t3", "$t4", "$t5", "$t6", "$t7",
+	  "$t8", "memory");
+
+	return ret;
+}
+
+static __always_inline u64 __arch_get_hw_counter(s32 clock_mode,
+						 const struct vdso_data *vd)
+{
+	unsigned int count;
+
+	__asm__ __volatile__(
+	"	rdtime.d %0, $zero\n"
+	: "=r" (count));
+
+	return count;
+}
+
+static inline bool loongarch_vdso_hres_capable(void)
+{
+	return true;
+}
+#define __arch_vdso_hres_capable loongarch_vdso_hres_capable
+
+static __always_inline const struct vdso_data *__arch_get_vdso_data(void)
+{
+	return get_vdso_data();
+}
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* __ASM_VDSO_GETTIMEOFDAY_H */
diff --git a/arch/loongarch/include/asm/vdso/processor.h b/arch/loongarch/include/asm/vdso/processor.h
new file mode 100644
index 000000000000..7fb46fd66512
--- /dev/null
+++ b/arch/loongarch/include/asm/vdso/processor.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef __ASM_VDSO_PROCESSOR_H
+#define __ASM_VDSO_PROCESSOR_H
+
+#ifndef __ASSEMBLY__
+
+#define cpu_relax()	barrier()
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* __ASM_VDSO_PROCESSOR_H */
diff --git a/arch/loongarch/include/asm/vdso/vdso.h b/arch/loongarch/include/asm/vdso/vdso.h
new file mode 100644
index 000000000000..873a983614ac
--- /dev/null
+++ b/arch/loongarch/include/asm/vdso/vdso.h
@@ -0,0 +1,42 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef __ASSEMBLY__
+
+#include <asm/asm.h>
+#include <asm/page.h>
+#include <asm/vdso.h>
+
+static inline unsigned long get_vdso_base(void)
+{
+	unsigned long addr;
+
+	/*
+	 * Get the base load address of the VDSO. We have to avoid generating
+	 * relocations and references to the GOT because ld.so does not peform
+	 * relocations on the VDSO. We use the current offset from the VDSO base
+	 * and perform a PC-relative branch which gives the absolute address in
+	 * ra, and take the difference. The assembler chokes on
+	 * "li.w %0, _start - .", so embed the offset as a word and branch over
+	 * it.
+	 *
+	 */
+
+	__asm__(
+	" la.pcrel %0, _start\n"
+	: "=r" (addr)
+	:
+	:);
+
+	return addr;
+}
+
+static inline const struct vdso_data *get_vdso_data(void)
+{
+	return (const struct vdso_data *)(get_vdso_base() - PAGE_SIZE);
+}
+
+#endif /* __ASSEMBLY__ */
diff --git a/arch/loongarch/include/asm/vdso/vsyscall.h b/arch/loongarch/include/asm/vdso/vsyscall.h
new file mode 100644
index 000000000000..5de615383a22
--- /dev/null
+++ b/arch/loongarch/include/asm/vdso/vsyscall.h
@@ -0,0 +1,27 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __ASM_VDSO_VSYSCALL_H
+#define __ASM_VDSO_VSYSCALL_H
+
+#ifndef __ASSEMBLY__
+
+#include <linux/timekeeper_internal.h>
+#include <vdso/datapage.h>
+
+extern struct vdso_data *vdso_data;
+
+/*
+ * Update the vDSO data page to keep in sync with kernel timekeeping.
+ */
+static __always_inline
+struct vdso_data *__loongarch_get_k_vdso_data(void)
+{
+	return vdso_data;
+}
+#define __arch_get_k_vdso_data __loongarch_get_k_vdso_data
+
+/* The asm-generic header needs to be included after the definitions above */
+#include <asm-generic/vdso/vsyscall.h>
+
+#endif /* !__ASSEMBLY__ */
+
+#endif /* __ASM_VDSO_VSYSCALL_H */
diff --git a/arch/loongarch/kernel/vdso.c b/arch/loongarch/kernel/vdso.c
new file mode 100644
index 000000000000..82635c3a4d74
--- /dev/null
+++ b/arch/loongarch/kernel/vdso.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <linux/binfmts.h>
+#include <linux/elf.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/random.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/timekeeper_internal.h>
+
+#include <asm/abi.h>
+#include <asm/page.h>
+#include <asm/vdso.h>
+#include <vdso/helpers.h>
+#include <vdso/vsyscall.h>
+
+/* Kernel-provided data used by the VDSO. */
+static union loongarch_vdso_data loongarch_vdso_data __page_aligned_data;
+struct vdso_data *vdso_data = loongarch_vdso_data.data;
+
+/*
+ * Mapping for the VDSO data pages. The real pages are mapped manually, as
+ * what we map and where within the area they are mapped is determined at
+ * runtime.
+ */
+static struct page *no_pages[] = { NULL };
+static struct vm_special_mapping vdso_vvar_mapping = {
+	.name = "[vvar]",
+	.pages = no_pages,
+};
+
+static void __init init_vdso_image(struct loongarch_vdso_image *image)
+{
+	unsigned long num_pages, i;
+	unsigned long data_pfn;
+
+	BUG_ON(!PAGE_ALIGNED(image->data));
+	BUG_ON(!PAGE_ALIGNED(image->size));
+
+	num_pages = image->size / PAGE_SIZE;
+
+	data_pfn = __phys_to_pfn(__pa_symbol(image->data));
+	for (i = 0; i < num_pages; i++)
+		image->mapping.pages[i] = pfn_to_page(data_pfn + i);
+}
+
+static int __init init_vdso(void)
+{
+	init_vdso_image(&vdso_image);
+	return 0;
+}
+subsys_initcall(init_vdso);
+
+static unsigned long vdso_base(void)
+{
+	unsigned long base;
+
+	if (current->flags & PF_RANDOMIZE) {
+		base += get_random_int() & (VDSO_RANDOMIZE_SIZE - 1);
+		base = PAGE_ALIGN(base);
+	}
+
+	return base;
+}
+
+int arch_setup_additional_pages(struct linux_binprm *bprm, int uses_interp)
+{
+	struct loongarch_vdso_image *image = current->thread.abi->vdso;
+	struct mm_struct *mm = current->mm;
+	unsigned long vvar_size, size, base, data_addr, vdso_addr;
+	struct vm_area_struct *vma;
+	int ret;
+
+	if (mmap_write_lock_killable(mm))
+		return -EINTR;
+
+	/*
+	 * Determine total area size. This includes the VDSO data itself
+	 * and the data page.
+	 */
+	vvar_size = PAGE_SIZE;
+	size = vvar_size + image->size;
+
+	base = get_unmapped_area(NULL, vdso_base(), size, 0, 0);
+	if (IS_ERR_VALUE(base)) {
+		ret = base;
+		goto out;
+	}
+
+	data_addr = base;
+	vdso_addr = data_addr + PAGE_SIZE;
+
+	vma = _install_special_mapping(mm, base, vvar_size,
+				       VM_READ | VM_MAYREAD,
+				       &vdso_vvar_mapping);
+	if (IS_ERR(vma)) {
+		ret = PTR_ERR(vma);
+		goto out;
+	}
+
+	/* Map data page. */
+	ret = remap_pfn_range(vma, data_addr,
+			      virt_to_phys(vdso_data) >> PAGE_SHIFT,
+			      PAGE_SIZE, PAGE_READONLY);
+	if (ret)
+		goto out;
+
+	/* Map VDSO image. */
+	vma = _install_special_mapping(mm, vdso_addr, image->size,
+				       VM_READ | VM_EXEC |
+				       VM_MAYREAD | VM_MAYWRITE | VM_MAYEXEC,
+				       &image->mapping);
+	if (IS_ERR(vma)) {
+		ret = PTR_ERR(vma);
+		goto out;
+	}
+
+	mm->context.vdso = (void *)vdso_addr;
+	ret = 0;
+
+out:
+	mmap_write_unlock(mm);
+	return ret;
+}
diff --git a/arch/loongarch/vdso/Makefile b/arch/loongarch/vdso/Makefile
new file mode 100644
index 000000000000..99810605c6fa
--- /dev/null
+++ b/arch/loongarch/vdso/Makefile
@@ -0,0 +1,97 @@
+# SPDX-License-Identifier: GPL-2.0
+# Objects to go into the VDSO.
+
+# Absolute relocation type $(ARCH_REL_TYPE_ABS) needs to be defined before
+# the inclusion of generic Makefile.
+ARCH_REL_TYPE_ABS := R_LARCH_32|R_LARCH_64|R_LARCH_JUMP_SLOT
+include $(srctree)/lib/vdso/Makefile
+
+obj-vdso-y := elf.o vgettimeofday.o sigreturn.o
+
+# Common compiler flags between ABIs.
+ccflags-vdso := \
+	$(filter -I%,$(KBUILD_CFLAGS)) \
+	$(filter -E%,$(KBUILD_CFLAGS)) \
+	$(filter -march=%,$(KBUILD_CFLAGS)) \
+	$(filter -m%-float,$(KBUILD_CFLAGS)) \
+	-D__VDSO__
+
+ifeq ($(cc-name),clang)
+ccflags-vdso += $(filter --target=%,$(KBUILD_CFLAGS))
+endif
+
+cflags-vdso := $(ccflags-vdso) \
+	$(filter -W%,$(filter-out -Wa$(comma)%,$(KBUILD_CFLAGS))) \
+	-O2 -g -fno-strict-aliasing -fno-common -fno-builtin -G 0 \
+	-fno-stack-protector -fno-jump-tables -DDISABLE_BRANCH_PROFILING \
+	$(call cc-option, -fno-asynchronous-unwind-tables) \
+	$(call cc-option, -fno-stack-protector)
+aflags-vdso := $(ccflags-vdso) \
+	-D__ASSEMBLY__ -Wa,-gdwarf-2
+
+ifneq ($(c-gettimeofday-y),)
+  CFLAGS_vgettimeofday.o += -include $(c-gettimeofday-y)
+endif
+
+# VDSO linker flags.
+ldflags-y := -Bsymbolic --no-undefined -soname=linux-vdso.so.1 \
+	$(filter -E%,$(KBUILD_CFLAGS)) -nostdlib -shared \
+	--hash-style=sysv --build-id -T
+
+GCOV_PROFILE := n
+
+#
+# Shared build commands.
+#
+
+quiet_cmd_vdsold_and_vdso_check = LD      $@
+      cmd_vdsold_and_vdso_check = $(cmd_ld); $(cmd_vdso_check)
+
+quiet_cmd_vdsoas_o_S = AS       $@
+      cmd_vdsoas_o_S = $(CC) $(a_flags) -c -o $@ $<
+
+# Strip rule for the raw .so files
+$(obj)/%.so.raw: OBJCOPYFLAGS := -S
+$(obj)/%.so.raw: $(obj)/%.so.dbg.raw FORCE
+	$(call if_changed,objcopy)
+
+hostprogs := genvdso
+
+quiet_cmd_genvdso = GENVDSO $@
+define cmd_genvdso
+	$(foreach file,$(filter %.raw,$^),cp $(file) $(file:%.raw=%) &&) \
+	$(obj)/genvdso $(<:%.raw=%) $(<:%.dbg.raw=%) $@ $(VDSO_NAME)
+endef
+
+#
+# Build native VDSO.
+#
+
+native-abi := $(filter -mabi=%,$(KBUILD_CFLAGS))
+
+targets += $(obj-vdso-y)
+targets += vdso.lds
+targets += vdso.so.dbg.raw vdso.so.raw
+targets += vdso.so.dbg vdso.so
+targets += vdso-image.c
+
+obj-vdso := $(obj-vdso-y:%.o=$(obj)/%.o)
+
+$(obj-vdso): KBUILD_CFLAGS := $(cflags-vdso) $(native-abi)
+$(obj-vdso): KBUILD_AFLAGS := $(aflags-vdso) $(native-abi)
+
+$(obj)/vdso.lds: KBUILD_CPPFLAGS := $(ccflags-vdso) $(native-abi)
+
+$(obj)/vdso.so.dbg.raw: $(obj)/vdso.lds $(obj-vdso) FORCE
+	$(call if_changed,vdsold_and_vdso_check)
+
+$(obj)/vdso-image.c: $(obj)/vdso.so.dbg.raw $(obj)/vdso.so.raw \
+                     $(obj)/genvdso FORCE
+	$(call if_changed,genvdso)
+
+obj-y += vdso-image.o
+
+
+
+# FIXME: Need install rule for debug.
+# Needs to deal with dependency for generation of dbg by cmd_genvdso...
diff --git a/arch/loongarch/vdso/elf.S b/arch/loongarch/vdso/elf.S
new file mode 100644
index 000000000000..a6d50bf863ff
--- /dev/null
+++ b/arch/loongarch/vdso/elf.S
@@ -0,0 +1,15 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <asm/vdso/vdso.h>
+
+#include <linux/elfnote.h>
+#include <linux/version.h>
+
+ELFNOTE_START(Linux, 0, "a")
+	.long LINUX_VERSION_CODE
+ELFNOTE_END
diff --git a/arch/loongarch/vdso/genvdso.c b/arch/loongarch/vdso/genvdso.c
new file mode 100644
index 000000000000..3a3d5f776a2f
--- /dev/null
+++ b/arch/loongarch/vdso/genvdso.c
@@ -0,0 +1,311 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+/*
+ * This tool is used to generate the real VDSO images from the raw image. It
+ * first patches up the LoongArch ABI flags and GNU attributes sections defined in
+ * elf.S to have the correct name and type. It then generates a C source file
+ * to be compiled into the kernel containing the VDSO image data and a
+ * loongarch_vdso_image struct for it, including symbol offsets extracted from the
+ * image.
+ *
+ * We need to be passed both a stripped and unstripped VDSO image. The stripped
+ * image is compiled into the kernel, but we must also patch up the unstripped
+ * image's ABI flags sections so that it can be installed and used for
+ * debugging.
+ */
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <byteswap.h>
+#include <elf.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <inttypes.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* Define these in case the system elf.h is not new enough to have them. */
+#ifndef EM_LOONGARCH
+#define EM_LOONGARCH 258
+#endif
+
+#ifndef EF_LARCH_ABI_LP32
+#define EF_LARCH_ABI_LP32		0x00000001	/* LP32 ABI.  */
+#endif
+
+#ifndef EF_LARCH_ABI_LPX32
+#define EF_LARCH_ABI_LPX32		0x00000002	/* LPX32 ABI  */
+#endif
+
+#ifndef EF_LARCH_ABI_LP64
+#define EF_LARCH_ABI_LP64		0x00000003	/* LP64 ABI  */
+#endif
+
+enum {
+	ABI_LP32  = (1 << 0),
+	ABI_LPX32 = (1 << 1),
+	ABI_LP64  = (1 << 2),
+
+	ABI_ALL = ABI_LP32 | ABI_LPX32 | ABI_LP64,
+};
+
+/* Symbols the kernel requires offsets for. */
+static struct {
+	const char *name;
+	const char *offset_name;
+	unsigned int abis;
+} vdso_symbols[] = {
+	{ "__vdso_rt_sigreturn", "off_rt_sigreturn", ABI_ALL },
+	{}
+};
+
+static const char *program_name;
+static const char *vdso_name;
+static unsigned char elf_class;
+static unsigned int elf_abi;
+static bool need_swap;
+static FILE *out_file;
+
+#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
+# define HOST_ORDER		ELFDATA2LSB
+#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
+# define HOST_ORDER		ELFDATA2MSB
+#endif
+
+#define BUILD_SWAP(bits)						\
+	static uint##bits##_t swap_uint##bits(uint##bits##_t val)	\
+	{								\
+		return need_swap ? bswap_##bits(val) : val;		\
+	}
+
+BUILD_SWAP(16)
+BUILD_SWAP(32)
+BUILD_SWAP(64)
+
+#define __FUNC(name, bits) name##bits
+#define _FUNC(name, bits) __FUNC(name, bits)
+#define FUNC(name) _FUNC(name, ELF_BITS)
+
+#define __ELF(x, bits) Elf##bits##_##x
+#define _ELF(x, bits) __ELF(x, bits)
+#define ELF(x) _ELF(x, ELF_BITS)
+
+/*
+ * Include genvdso.h twice with ELF_BITS defined differently to get functions
+ * for both ELF32 and ELF64.
+ */
+
+#define ELF_BITS 32
+#include "genvdso.h"
+#undef ELF_BITS
+
+#define ELF_BITS 64
+#include "genvdso.h"
+#undef ELF_BITS
+
+static void *map_vdso(const char *path, size_t *_size)
+{
+	int fd;
+	struct stat stat;
+	void *addr;
+	const Elf32_Ehdr *ehdr;
+
+	fd = open(path, O_RDWR);
+	if (fd < 0) {
+		fprintf(stderr, "%s: Failed to open '%s': %s\n", program_name,
+			path, strerror(errno));
+		return NULL;
+	}
+
+	if (fstat(fd, &stat) != 0) {
+		fprintf(stderr, "%s: Failed to stat '%s': %s\n", program_name,
+			path, strerror(errno));
+		return NULL;
+	}
+
+	addr = mmap(NULL, stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd,
+		    0);
+	if (addr == MAP_FAILED) {
+		fprintf(stderr, "%s: Failed to map '%s': %s\n", program_name,
+			path, strerror(errno));
+		return NULL;
+	}
+
+	/* ELF32/64 header formats are the same for the bits we're checking. */
+	ehdr = addr;
+
+	if (memcmp(ehdr->e_ident, ELFMAG, SELFMAG) != 0) {
+		fprintf(stderr, "%s: '%s' is not an ELF file\n", program_name,
+			path);
+		return NULL;
+	}
+
+	elf_class = ehdr->e_ident[EI_CLASS];
+	switch (elf_class) {
+	case ELFCLASS32:
+	case ELFCLASS64:
+		break;
+	default:
+		fprintf(stderr, "%s: '%s' has invalid ELF class\n",
+			program_name, path);
+		return NULL;
+	}
+
+	switch (ehdr->e_ident[EI_DATA]) {
+	case ELFDATA2LSB:
+	case ELFDATA2MSB:
+		need_swap = ehdr->e_ident[EI_DATA] != HOST_ORDER;
+		break;
+	default:
+		fprintf(stderr, "%s: '%s' has invalid ELF data order\n",
+			program_name, path);
+		return NULL;
+	}
+
+	if (swap_uint16(ehdr->e_machine) != EM_LOONGARCH) {
+		fprintf(stderr,
+			"%s: '%s' has invalid ELF machine (expected EM_LOONGARCH)\n",
+			program_name, path);
+		return NULL;
+	} else if (swap_uint16(ehdr->e_type) != ET_DYN) {
+		fprintf(stderr,
+			"%s: '%s' has invalid ELF type (expected ET_DYN)\n",
+			program_name, path);
+		return NULL;
+	}
+
+	*_size = stat.st_size;
+	return addr;
+}
+
+static bool patch_vdso(const char *path, void *vdso)
+{
+	if (elf_class == ELFCLASS64)
+		return patch_vdso64(path, vdso);
+	else
+		return patch_vdso32(path, vdso);
+}
+
+static bool get_symbols(const char *path, void *vdso)
+{
+	if (elf_class == ELFCLASS64)
+		return get_symbols64(path, vdso);
+	else
+		return get_symbols32(path, vdso);
+}
+
+int main(int argc, char **argv)
+{
+	const char *dbg_vdso_path, *vdso_path, *out_path;
+	void *dbg_vdso, *vdso;
+	size_t dbg_vdso_size, vdso_size, i;
+
+	program_name = argv[0];
+
+	if (argc < 4 || argc > 5) {
+		fprintf(stderr,
+			"Usage: %s <debug VDSO> <stripped VDSO> <output file> [<name>]\n",
+			program_name);
+		return EXIT_FAILURE;
+	}
+
+	dbg_vdso_path = argv[1];
+	vdso_path = argv[2];
+	out_path = argv[3];
+	vdso_name = (argc > 4) ? argv[4] : "";
+
+	dbg_vdso = map_vdso(dbg_vdso_path, &dbg_vdso_size);
+	if (!dbg_vdso)
+		return EXIT_FAILURE;
+
+	vdso = map_vdso(vdso_path, &vdso_size);
+	if (!vdso)
+		return EXIT_FAILURE;
+
+	/* Patch both the VDSOs' ABI flags sections. */
+	if (!patch_vdso(dbg_vdso_path, dbg_vdso))
+		return EXIT_FAILURE;
+	if (!patch_vdso(vdso_path, vdso))
+		return EXIT_FAILURE;
+
+	if (msync(dbg_vdso, dbg_vdso_size, MS_SYNC) != 0) {
+		fprintf(stderr, "%s: Failed to sync '%s': %s\n", program_name,
+			dbg_vdso_path, strerror(errno));
+		return EXIT_FAILURE;
+	} else if (msync(vdso, vdso_size, MS_SYNC) != 0) {
+		fprintf(stderr, "%s: Failed to sync '%s': %s\n", program_name,
+			vdso_path, strerror(errno));
+		return EXIT_FAILURE;
+	}
+
+	out_file = fopen(out_path, "w");
+	if (!out_file) {
+		fprintf(stderr, "%s: Failed to open '%s': %s\n", program_name,
+			out_path, strerror(errno));
+		return EXIT_FAILURE;
+	}
+
+	fprintf(out_file, "/* Automatically generated - do not edit */\n");
+	fprintf(out_file, "#include <linux/linkage.h>\n");
+	fprintf(out_file, "#include <linux/mm.h>\n");
+	fprintf(out_file, "#include <asm/vdso.h>\n");
+	fprintf(out_file, "static int vdso_mremap(\n");
+	fprintf(out_file, "	const struct vm_special_mapping *sm,\n");
+	fprintf(out_file, "	struct vm_area_struct *new_vma)\n");
+	fprintf(out_file, "{\n");
+	fprintf(out_file, "	unsigned long new_size =\n");
+	fprintf(out_file, "	new_vma->vm_end - new_vma->vm_start;\n");
+	fprintf(out_file, "	if (vdso_image.size != new_size)\n");
+	fprintf(out_file, "		return -EINVAL;\n");
+	fprintf(out_file, "	current->mm->context.vdso =\n");
+	fprintf(out_file, "	(void *)(new_vma->vm_start);\n");
+	fprintf(out_file, "	return 0;\n");
+	fprintf(out_file, "}\n");
+
+	/* Write out the stripped VDSO data. */
+	fprintf(out_file,
+		"static unsigned char vdso_data[PAGE_ALIGN(%zu)] __page_aligned_data = {\n\t",
+		vdso_size);
+	for (i = 0; i < vdso_size; i++) {
+		if (!(i % 10))
+			fprintf(out_file, "\n\t");
+		fprintf(out_file, "0x%02x, ", ((unsigned char *)vdso)[i]);
+	}
+	fprintf(out_file, "\n};\n");
+
+	/* Preallocate a page array. */
+	fprintf(out_file,
+		"static struct page *vdso_pages[PAGE_ALIGN(%zu) / PAGE_SIZE];\n",
+		vdso_size);
+
+	fprintf(out_file, "struct loongarch_vdso_image vdso_image%s%s = {\n",
+		(vdso_name[0]) ? "_" : "", vdso_name);
+	fprintf(out_file, "\t.data = vdso_data,\n");
+	fprintf(out_file, "\t.size = PAGE_ALIGN(%zu),\n", vdso_size);
+	fprintf(out_file, "\t.mapping = {\n");
+	fprintf(out_file, "\t\t.name = \"[vdso]\",\n");
+	fprintf(out_file, "\t\t.pages = vdso_pages,\n");
+	fprintf(out_file, "\t\t.mremap = vdso_mremap,\n");
+	fprintf(out_file, "\t},\n");
+
+	/* Calculate and write symbol offsets to <output file> */
+	if (!get_symbols(dbg_vdso_path, dbg_vdso)) {
+		unlink(out_path);
+		return EXIT_FAILURE;
+	}
+
+	fprintf(out_file, "};\n");
+
+	return EXIT_SUCCESS;
+}
diff --git a/arch/loongarch/vdso/genvdso.h b/arch/loongarch/vdso/genvdso.h
new file mode 100644
index 000000000000..4cc83e996607
--- /dev/null
+++ b/arch/loongarch/vdso/genvdso.h
@@ -0,0 +1,122 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+static inline bool FUNC(patch_vdso)(const char *path, void *vdso)
+{
+	const ELF(Ehdr) *ehdr = vdso;
+	void *shdrs;
+	ELF(Shdr) *shdr;
+	uint16_t sh_count, sh_entsize, i;
+
+	shdrs = vdso + FUNC(swap_uint)(ehdr->e_shoff);
+	sh_count = swap_uint16(ehdr->e_shnum);
+	sh_entsize = swap_uint16(ehdr->e_shentsize);
+
+	shdr = shdrs + (sh_entsize * swap_uint16(ehdr->e_shstrndx));
+
+	for (i = 0; i < sh_count; i++) {
+		shdr = shdrs + (i * sh_entsize);
+
+		/*
+		 * Ensure there are no relocation sections - ld.so does not
+		 * relocate the VDSO so if there are relocations things will
+		 * break.
+		 */
+		switch (swap_uint32(shdr->sh_type)) {
+		case SHT_REL:
+		case SHT_RELA:
+			fprintf(stderr,
+				"%s: '%s' contains relocation sections\n",
+				program_name, path);
+			return false;
+		}
+	}
+
+	return true;
+}
+
+static inline bool FUNC(get_symbols)(const char *path, void *vdso)
+{
+	const ELF(Ehdr) *ehdr = vdso;
+	void *shdrs, *symtab;
+	ELF(Shdr) *shdr;
+	const ELF(Sym) *sym;
+	char *strtab, *name;
+	uint16_t sh_count, sh_entsize, st_count, st_entsize, i, j;
+	uint64_t offset;
+	uint32_t flags;
+
+	shdrs = vdso + FUNC(swap_uint)(ehdr->e_shoff);
+	sh_count = swap_uint16(ehdr->e_shnum);
+	sh_entsize = swap_uint16(ehdr->e_shentsize);
+
+	for (i = 0; i < sh_count; i++) {
+		shdr = shdrs + (i * sh_entsize);
+
+		if (swap_uint32(shdr->sh_type) == SHT_SYMTAB)
+			break;
+	}
+
+	if (i == sh_count) {
+		fprintf(stderr, "%s: '%s' has no symbol table\n", program_name,
+			path);
+		return false;
+	}
+
+	/* Get flags */
+	flags = swap_uint32(ehdr->e_flags);
+	switch (flags) {
+	case EF_LARCH_ABI_LP32:
+		elf_abi = ABI_LP32;
+		break;
+	case EF_LARCH_ABI_LPX32:
+		elf_abi = ABI_LPX32;
+		break;
+	case EF_LARCH_ABI_LP64:
+	default:
+		elf_abi = ABI_LP64;
+		break;
+	}
+
+	/* Get symbol table. */
+	symtab = vdso + FUNC(swap_uint)(shdr->sh_offset);
+	st_entsize = FUNC(swap_uint)(shdr->sh_entsize);
+	st_count = FUNC(swap_uint)(shdr->sh_size) / st_entsize;
+
+	/* Get string table. */
+	shdr = shdrs + (swap_uint32(shdr->sh_link) * sh_entsize);
+	strtab = vdso + FUNC(swap_uint)(shdr->sh_offset);
+
+	/* Write offsets for symbols needed by the kernel. */
+	for (i = 0; vdso_symbols[i].name; i++) {
+		if (!(vdso_symbols[i].abis & elf_abi))
+			continue;
+
+		for (j = 0; j < st_count; j++) {
+			sym = symtab + (j * st_entsize);
+			name = strtab + swap_uint32(sym->st_name);
+
+			if (!strcmp(name, vdso_symbols[i].name)) {
+				offset = FUNC(swap_uint)(sym->st_value);
+
+				fprintf(out_file,
+					"\t.%s = 0x%" PRIx64 ",\n",
+					vdso_symbols[i].offset_name, offset);
+				break;
+			}
+		}
+
+		if (j == st_count) {
+			fprintf(stderr,
+				"%s: '%s' is missing required symbol '%s'\n",
+				program_name, path, vdso_symbols[i].name);
+			return false;
+		}
+	}
+
+	return true;
+}
diff --git a/arch/loongarch/vdso/sigreturn.S b/arch/loongarch/vdso/sigreturn.S
new file mode 100644
index 000000000000..38ce55577510
--- /dev/null
+++ b/arch/loongarch/vdso/sigreturn.S
@@ -0,0 +1,24 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <asm/vdso/vdso.h>
+
+#include <linux/linkage.h>
+#include <uapi/asm/unistd.h>
+
+#include <asm/regdef.h>
+#include <asm/asm.h>
+
+	.section	.text
+	.cfi_sections	.debug_frame
+
+SYM_FUNC_START(__vdso_rt_sigreturn)
+
+	li.w	a7, __NR_rt_sigreturn
+	syscall	0
+
+SYM_FUNC_END(__vdso_rt_sigreturn)
diff --git a/arch/loongarch/vdso/vdso.lds.S b/arch/loongarch/vdso/vdso.lds.S
new file mode 100644
index 000000000000..57736ff67a4d
--- /dev/null
+++ b/arch/loongarch/vdso/vdso.lds.S
@@ -0,0 +1,65 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+OUTPUT_FORMAT("elf64-loongarch", "elf64-loongarch", "elf64-loongarch")
+
+OUTPUT_ARCH(loongarch)
+
+SECTIONS
+{
+	PROVIDE(_start = .);
+	. = SIZEOF_HEADERS;
+
+	.hash		: { *(.hash) }			:text
+	.gnu.hash	: { *(.gnu.hash) }
+	.dynsym		: { *(.dynsym) }
+	.dynstr		: { *(.dynstr) }
+	.gnu.version	: { *(.gnu.version) }
+	.gnu.version_d	: { *(.gnu.version_d) }
+	.gnu.version_r	: { *(.gnu.version_r) }
+
+	.note		: { *(.note.*) }		:text :note
+
+	.text		: { *(.text*) }			:text
+	PROVIDE (__etext = .);
+	PROVIDE (_etext = .);
+	PROVIDE (etext = .);
+
+	.eh_frame_hdr	: { *(.eh_frame_hdr) }		:text :eh_frame_hdr
+	.eh_frame	: { KEEP (*(.eh_frame)) }	:text
+
+	.dynamic	: { *(.dynamic) }		:text :dynamic
+
+	.rodata		: { *(.rodata*) }		:text
+
+	_end = .;
+	PROVIDE(end = .);
+
+	/DISCARD/	: {
+		*(.gnu.attributes)
+		*(.note.GNU-stack)
+		*(.data .data.* .gnu.linkonce.d.* .sdata*)
+		*(.bss .sbss .dynbss .dynsbss)
+	}
+}
+
+PHDRS
+{
+	text		PT_LOAD		FLAGS(5) FILEHDR PHDRS; /* PF_R|PF_X */
+	dynamic		PT_DYNAMIC	FLAGS(4);		/* PF_R */
+	note		PT_NOTE		FLAGS(4);		/* PF_R */
+	eh_frame_hdr	PT_GNU_EH_FRAME;
+}
+
+VERSION
+{
+	LINUX_2.6 {
+	global:
+		__vdso_clock_gettime;
+		__vdso_gettimeofday;
+	local: *;
+	};
+}
diff --git a/arch/loongarch/vdso/vgettimeofday.c b/arch/loongarch/vdso/vgettimeofday.c
new file mode 100644
index 000000000000..ba5d3cab7a5f
--- /dev/null
+++ b/arch/loongarch/vdso/vgettimeofday.c
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * LoongArch userspace implementations of gettimeofday() and similar.
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/time.h>
+#include <linux/types.h>
+
+int __vdso_clock_gettime(clockid_t clock,
+			 struct __kernel_timespec *ts)
+{
+	return __cvdso_clock_gettime(clock, ts);
+}
+
+int __vdso_gettimeofday(struct __kernel_old_timeval *tv,
+			struct timezone *tz)
+{
+	return __cvdso_gettimeofday(tv, tz);
+}
+
+int __vdso_clock_getres(clockid_t clock_id,
+			struct __kernel_timespec *res)
+{
+	return __cvdso_clock_getres(clock_id, res);
+}
-- 
2.27.0


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

* [PATCH 17/19] LoongArch: Add multi-processor (SMP) support
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (14 preceding siblings ...)
  2021-07-06  4:18 ` [PATCH 16/19] LoongArch: Add VDSO and VSYSCALL support Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06 10:17   ` Arnd Bergmann
                     ` (4 more replies)
  2021-07-06  4:18 ` [PATCH 18/19] LoongArch: Add Non-Uniform Memory Access (NUMA) support Huacai Chen
                   ` (4 subsequent siblings)
  20 siblings, 5 replies; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

This patch adds multi-processor (SMP) support for LoongArch.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 arch/loongarch/Kconfig                        |  79 ++-
 arch/loongarch/include/asm/atomic.h           |   4 +
 arch/loongarch/include/asm/barrier.h          | 119 ++++
 arch/loongarch/include/asm/cmpxchg.h          |   1 +
 arch/loongarch/include/asm/futex.h            |   1 +
 arch/loongarch/include/asm/hardirq.h          |   2 +
 .../include/asm/mach-loongson64/irq.h         |   3 +
 arch/loongarch/include/asm/percpu.h           | 177 ++++++
 arch/loongarch/include/asm/pgtable.h          |  21 +
 arch/loongarch/include/asm/smp.h              | 144 +++++
 arch/loongarch/include/asm/stackframe.h       |  20 +
 arch/loongarch/include/asm/tlbflush.h         |  22 +-
 arch/loongarch/include/asm/topology.h         |   7 +-
 arch/loongarch/include/asm/vdso/processor.h   |  11 +-
 arch/loongarch/kernel/Makefile                |   2 +
 arch/loongarch/kernel/acpi.c                  |  70 ++-
 arch/loongarch/kernel/head.S                  |  19 +
 arch/loongarch/kernel/irq.c                   |   1 +
 arch/loongarch/kernel/proc.c                  |   5 +
 arch/loongarch/kernel/reset.c                 |  12 +
 arch/loongarch/kernel/setup.c                 |  26 +
 arch/loongarch/kernel/signal.c                |  20 +
 arch/loongarch/kernel/smp.c                   | 435 +++++++++++++++
 arch/loongarch/kernel/time.c                  |  23 +
 arch/loongarch/kernel/topology.c              |  43 +-
 arch/loongarch/kernel/vmlinux.lds.S           |   3 +
 arch/loongarch/loongson64/Makefile            |   2 +
 arch/loongarch/loongson64/init.c              |   3 +
 arch/loongarch/loongson64/irq.c               |  38 ++
 arch/loongarch/loongson64/smp.c               | 510 ++++++++++++++++++
 arch/loongarch/mm/tlbex.S                     |  69 +++
 include/linux/cpuhotplug.h                    |   1 +
 32 files changed, 1881 insertions(+), 12 deletions(-)
 create mode 100644 arch/loongarch/include/asm/smp.h
 create mode 100644 arch/loongarch/kernel/smp.c
 create mode 100644 arch/loongarch/loongson64/smp.c

diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 7d5889a264c6..74beb370d943 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -55,6 +55,7 @@ config LOONGARCH
 	select GENERIC_LIB_CMPDI2
 	select GENERIC_LIB_LSHRDI3
 	select GENERIC_LIB_UCMPDI2
+	select GENERIC_SMP_IDLE_THREAD
 	select GENERIC_TIME_VSYSCALL
 	select HANDLE_DOMAIN_IRQ
 	select HAVE_ARCH_AUDITSYSCALL
@@ -87,7 +88,7 @@ config LOONGARCH
 	select HAVE_REGS_AND_STACK_ACCESS_API
 	select HAVE_RSEQ
 	select HAVE_SYSCALL_TRACEPOINTS
-	select HAVE_VIRT_CPU_ACCOUNTING_GEN if 64BIT
+	select HAVE_VIRT_CPU_ACCOUNTING_GEN if 64BIT || !SMP
 	select IRQ_FORCED_THREADING
 	select MODULES_USE_ELF_RELA if MODULES && 64BIT
 	select MODULES_USE_ELF_REL if MODULES
@@ -117,6 +118,8 @@ config MACH_LOONGSON64
 	select NR_CPUS_DEFAULT_4
 	select SPARSE_IRQ
 	select SYS_HAS_CPU_LOONGSON64
+	select SYS_SUPPORTS_SMP
+	select SYS_SUPPORTS_HOTPLUG_CPU
 	select SYS_SUPPORTS_64BIT_KERNEL
 	select ZONE_DMA32
 	help
@@ -140,6 +143,9 @@ config SCHED_OMIT_FRAME_POINTER
 	bool
 	default y
 
+config SYS_SUPPORTS_HOTPLUG_CPU
+	bool
+
 config GENERIC_CSUM
 	def_bool y
 
@@ -321,6 +327,77 @@ config EFI
 	  resultant kernel should continue to boot on existing non-EFI
 	  platforms.
 
+config SMP
+	bool "Multi-Processing support"
+	depends on SYS_SUPPORTS_SMP
+	help
+	  This enables support for systems with more than one CPU. If you have
+	  a system with only one CPU, say N. If you have a system with more
+	  than one CPU, say Y.
+
+	  If you say N here, the kernel will run on uni- and multiprocessor
+	  machines, but will use only one CPU of a multiprocessor machine. If
+	  you say Y here, the kernel will run on many, but not all,
+	  uniprocessor machines. On a uniprocessor machine, the kernel
+	  will run faster if you say N here.
+
+	  People using multiprocessor machines who say Y here should also say
+	  Y to "Enhanced Real Time Clock Support", below.
+
+	  See also the SMP-HOWTO available at
+	  <http://www.tldp.org/docs.html#howto>.
+
+	  If you don't know what to do here, say N.
+
+config HOTPLUG_CPU
+	bool "Support for hot-pluggable CPUs"
+	depends on SMP && SYS_SUPPORTS_HOTPLUG_CPU
+	help
+	  Say Y here to allow turning CPUs off and on. CPUs can be
+	  controlled through /sys/devices/system/cpu.
+	  (Note: power management support will enable this option
+	    automatically on SMP systems. )
+	  Say N if you want to disable CPU hotplug.
+
+config SYS_SUPPORTS_SMP
+	bool
+
+config NR_CPUS_DEFAULT_4
+	bool
+
+config NR_CPUS_DEFAULT_8
+	bool
+
+config NR_CPUS_DEFAULT_16
+	bool
+
+config NR_CPUS_DEFAULT_32
+	bool
+
+config NR_CPUS_DEFAULT_64
+	bool
+
+config NR_CPUS
+	int "Maximum number of CPUs (2-256)"
+	range 2 256
+	depends on SMP
+	default "4" if NR_CPUS_DEFAULT_4
+	default "8" if NR_CPUS_DEFAULT_8
+	default "16" if NR_CPUS_DEFAULT_16
+	default "32" if NR_CPUS_DEFAULT_32
+	default "64" if NR_CPUS_DEFAULT_64
+	help
+	  This allows you to specify the maximum number of CPUs which this
+	  kernel will support.  The maximum supported value is 32 for 32-bit
+	  kernel and 64 for 64-bit kernels; the minimum value which makes
+	  sense is 1 for Qemu (useful only for kernel debugging purposes)
+	  and 2 for all others.
+
+	  This is purely to save memory - each supported CPU adds
+	  approximately eight kilobytes to the kernel image.  For best
+	  performance should round up your number of processors to the next
+	  power of two.
+
 source "kernel/Kconfig.hz"
 
 config SECCOMP
diff --git a/arch/loongarch/include/asm/atomic.h b/arch/loongarch/include/asm/atomic.h
index 51130035d943..9c8509c9dbce 100644
--- a/arch/loongarch/include/asm/atomic.h
+++ b/arch/loongarch/include/asm/atomic.h
@@ -125,6 +125,7 @@ static inline int arch_atomic_sub_if_positive(int i, atomic_t *v)
 		"	sc.w	%1, %2					\n"
 		"	beq	$zero, %1, 1b				\n"
 		"1:							\n"
+		__WEAK_LLSC_MB
 		: "=&r" (result), "=&r" (temp),
 		  "+" GCC_OFF_SMALL_ASM() (v->counter)
 		: "I" (-i));
@@ -137,6 +138,7 @@ static inline int arch_atomic_sub_if_positive(int i, atomic_t *v)
 		"	sc.w	%1, %2					\n"
 		"	beq	$zero, %1, 1b				\n"
 		"1:							\n"
+		__WEAK_LLSC_MB
 		: "=&r" (result), "=&r" (temp),
 		  "+" GCC_OFF_SMALL_ASM() (v->counter)
 		: "r" (i));
@@ -263,6 +265,7 @@ static inline long arch_atomic64_sub_if_positive(long i, atomic64_t *v)
 		"	sc.d	%1, %2					\n"
 		"	beq	%1, $zero, 1b				\n"
 		"1:							\n"
+		__WEAK_LLSC_MB
 		: "=&r" (result), "=&r" (temp),
 		  "+" GCC_OFF_SMALL_ASM() (v->counter)
 		: "I" (-i));
@@ -275,6 +278,7 @@ static inline long arch_atomic64_sub_if_positive(long i, atomic64_t *v)
 		"	sc.d	%1, %2					\n"
 		"	beq	%1, $zero, 1b				\n"
 		"1:							\n"
+		__WEAK_LLSC_MB
 		: "=&r" (result), "=&r" (temp),
 		  "+" GCC_OFF_SMALL_ASM() (v->counter)
 		: "r" (i));
diff --git a/arch/loongarch/include/asm/barrier.h b/arch/loongarch/include/asm/barrier.h
index 8ab8d8f15b88..ad09a3b31cba 100644
--- a/arch/loongarch/include/asm/barrier.h
+++ b/arch/loongarch/include/asm/barrier.h
@@ -20,6 +20,19 @@
 #define mb()		fast_mb()
 #define iob()		fast_iob()
 
+#define __smp_mb()	__asm__ __volatile__("dbar 0" : : : "memory")
+#define __smp_rmb()	__asm__ __volatile__("dbar 0" : : : "memory")
+#define __smp_wmb()	__asm__ __volatile__("dbar 0" : : : "memory")
+
+#ifdef CONFIG_SMP
+#define __WEAK_LLSC_MB		"	dbar 0  \n"
+#else
+#define __WEAK_LLSC_MB		"		\n"
+#endif
+
+#define __smp_mb__before_atomic()	barrier()
+#define __smp_mb__after_atomic()	__smp_mb()
+
 /**
  * array_index_mask_nospec() - generate a ~0 mask when index < size, 0 otherwise
  * @index: array element index
@@ -48,6 +61,112 @@ static inline unsigned long array_index_mask_nospec(unsigned long index,
 	return mask;
 }
 
+#define __smp_load_acquire(p)							\
+({										\
+	union { typeof(*p) __val; char __c[1]; } __u;				\
+	unsigned long __tmp = 0;							\
+	compiletime_assert_atomic_type(*p);					\
+	switch (sizeof(*p)) {							\
+	case 1:									\
+		*(__u8 *)__u.__c = *(volatile __u8 *)p;				\
+		__smp_mb();							\
+		break;								\
+	case 2:									\
+		*(__u16 *)__u.__c = *(volatile __u16 *)p;			\
+		__smp_mb();							\
+		break;								\
+	case 4:									\
+		__asm__ __volatile__(						\
+		"amor.w %[val], %[tmp], %[mem]	\n"				\
+		: [val] "=&r" (*(__u32 *)__u.__c)				\
+		: [mem] "ZB" (*(u32 *) p), [tmp] "r" (__tmp)			\
+		: "memory");							\
+		break;								\
+	case 8:									\
+		__asm__ __volatile__(						\
+		"amor.d %[val], %[tmp], %[mem]	\n"				\
+		: [val] "=&r" (*(__u64 *)__u.__c)				\
+		: [mem] "ZB" (*(u64 *) p), [tmp] "r" (__tmp)			\
+		: "memory");							\
+		break;								\
+	default:								\
+		barrier();							\
+		__builtin_memcpy((void *)__u.__c, (const void *)p, sizeof(*p));	\
+		__smp_mb();							\
+	}									\
+	__u.__val;								\
+})
+
+#define __smp_store_release(p, v)						\
+do {										\
+	union { typeof(*p) __val; char __c[1]; } __u =				\
+		{ .__val = (__force typeof(*p)) (v) };				\
+	unsigned long __tmp;							\
+	compiletime_assert_atomic_type(*p);					\
+	switch (sizeof(*p)) {							\
+	case 1:									\
+		__smp_mb();							\
+		*(volatile __u8 *)p = *(__u8 *)__u.__c;				\
+		break;								\
+	case 2:									\
+		__smp_mb();							\
+		*(volatile __u16 *)p = *(__u16 *)__u.__c;			\
+		break;								\
+	case 4:									\
+		__asm__ __volatile__(						\
+		"amswap.w %[tmp], %[val], %[mem]	\n"			\
+		: [mem] "+ZB" (*(u32 *)p), [tmp] "=&r" (__tmp)			\
+		: [val] "r" (*(__u32 *)__u.__c)					\
+		: );								\
+		break;								\
+	case 8:									\
+		__asm__ __volatile__(						\
+		"amswap.d %[tmp], %[val], %[mem]	\n"			\
+		: [mem] "+ZB" (*(u64 *)p), [tmp] "=&r" (__tmp)			\
+		: [val] "r" (*(__u64 *)__u.__c)					\
+		: );								\
+		break;								\
+	default:								\
+		__smp_mb();							\
+		__builtin_memcpy((void *)p, (const void *)__u.__c, sizeof(*p));	\
+		barrier();							\
+	}									\
+} while (0)
+
+#define __smp_store_mb(p, v)							\
+do {										\
+	union { typeof(p) __val; char __c[1]; } __u =				\
+		{ .__val = (__force typeof(p)) (v) };				\
+	unsigned long __tmp;							\
+	switch (sizeof(p)) {							\
+	case 1:									\
+		*(volatile __u8 *)&p = *(__u8 *)__u.__c;			\
+		__smp_mb();							\
+		break;								\
+	case 2:									\
+		*(volatile __u16 *)&p = *(__u16 *)__u.__c;			\
+		__smp_mb();							\
+		break;								\
+	case 4:									\
+		__asm__ __volatile__(						\
+		"amswap.w %[tmp], %[val], %[mem]	\n"			\
+		: [mem] "+ZB" (*(u32 *)&p), [tmp] "=&r" (__tmp)			\
+		: [val] "r" (*(__u32 *)__u.__c)					\
+		: );								\
+		break;								\
+	case 8:									\
+		__asm__ __volatile__(						\
+		"amswap.d %[tmp], %[val], %[mem]	\n"			\
+		: [mem] "+ZB" (*(u64 *)&p), [tmp] "=&r" (__tmp)			\
+		: [val] "r" (*(__u64 *)__u.__c)					\
+		: );								\
+		break;								\
+	default:								\
+		__builtin_memcpy((void *)&p, (const void *)__u.__c, sizeof(p));	\
+		__smp_mb();							\
+	}									\
+} while (0)
+
 #include <asm-generic/barrier.h>
 
 #endif /* __ASM_BARRIER_H */
diff --git a/arch/loongarch/include/asm/cmpxchg.h b/arch/loongarch/include/asm/cmpxchg.h
index a8adaacc73cc..e599e92cf090 100644
--- a/arch/loongarch/include/asm/cmpxchg.h
+++ b/arch/loongarch/include/asm/cmpxchg.h
@@ -82,6 +82,7 @@ static inline unsigned long __xchg(volatile void *ptr, unsigned long x,
 	"	" st "	$t0, %1				\n"		\
 	"	beq	$zero, $t0, 1b			\n"		\
 	"2:						\n"		\
+	__WEAK_LLSC_MB							\
 	: "=&r" (__ret), "=ZB"(*m)					\
 	: "ZB"(*m), "Jr" (old), "Jr" (new)				\
 	: "t0", "memory");						\
diff --git a/arch/loongarch/include/asm/futex.h b/arch/loongarch/include/asm/futex.h
index 6512feb041d1..22ddd3f7114b 100644
--- a/arch/loongarch/include/asm/futex.h
+++ b/arch/loongarch/include/asm/futex.h
@@ -86,6 +86,7 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32 newv
 	"2:	sc.w	$t0, %2					\n"
 	"	beq	$zero, $t0, 1b				\n"
 	"3:							\n"
+	__WEAK_LLSC_MB
 	"	.section .fixup,\"ax\"				\n"
 	"4:	li.d	%0, %6					\n"
 	"	b	3b					\n"
diff --git a/arch/loongarch/include/asm/hardirq.h b/arch/loongarch/include/asm/hardirq.h
index ccde14a45f67..d50298147a02 100644
--- a/arch/loongarch/include/asm/hardirq.h
+++ b/arch/loongarch/include/asm/hardirq.h
@@ -21,4 +21,6 @@ typedef struct {
 
 DECLARE_PER_CPU_ALIGNED(irq_cpustat_t, irq_stat);
 
+#define __ARCH_IRQ_STAT
+
 #endif /* _ASM_HARDIRQ_H */
diff --git a/arch/loongarch/include/asm/mach-loongson64/irq.h b/arch/loongarch/include/asm/mach-loongson64/irq.h
index 9e03a960e7b6..8e3ada606cc3 100644
--- a/arch/loongarch/include/asm/mach-loongson64/irq.h
+++ b/arch/loongarch/include/asm/mach-loongson64/irq.h
@@ -74,4 +74,7 @@ extern struct fwnode_handle *acpi_liointc_handle;
 extern struct fwnode_handle *acpi_msidomain_handle;
 extern struct fwnode_handle *acpi_picdomain_handle[MAX_PCH_PICS];
 
+extern void fixup_irqs(void);
+extern void loongson3_ipi_interrupt(int irq);
+
 #endif /* __ASM_MACH_LOONGSON64_IRQ_H_ */
diff --git a/arch/loongarch/include/asm/percpu.h b/arch/loongarch/include/asm/percpu.h
index ea5979872485..ea3c3e7808dc 100644
--- a/arch/loongarch/include/asm/percpu.h
+++ b/arch/loongarch/include/asm/percpu.h
@@ -5,6 +5,8 @@
 #ifndef __ASM_PERCPU_H
 #define __ASM_PERCPU_H
 
+#include <asm/cmpxchg.h>
+
 /* Use r21 for fast access */
 register unsigned long __my_cpu_offset __asm__("$r21");
 
@@ -15,6 +17,181 @@ static inline void set_my_cpu_offset(unsigned long off)
 }
 #define __my_cpu_offset __my_cpu_offset
 
+#define PERCPU_OP(op, asm_op, c_op)					\
+static inline unsigned long __percpu_##op(void *ptr,			\
+			unsigned long val, int size)			\
+{									\
+	unsigned long ret;						\
+									\
+	switch (size) {							\
+	case 4:								\
+		__asm__ __volatile__(					\
+		"am"#asm_op".w"	" %[ret], %[val], %[ptr]	\n"		\
+		: [ret] "=&r" (ret), [ptr] "+ZB"(*(u32 *)ptr)		\
+		: [val] "r" (val));					\
+		break;							\
+	case 8:								\
+		__asm__ __volatile__(					\
+		"am"#asm_op".d" " %[ret], %[val], %[ptr]	\n"		\
+		: [ret] "=&r" (ret), [ptr] "+ZB"(*(u64 *)ptr)		\
+		: [val] "r" (val));					\
+		break;							\
+	default:							\
+		ret = 0;						\
+		BUILD_BUG();						\
+	}								\
+									\
+	return ret c_op val;						\
+}
+
+PERCPU_OP(add, add, +)
+PERCPU_OP(and, and, &)
+PERCPU_OP(or, or, |)
+#undef PERCPU_OP
+
+static inline unsigned long __percpu_read(void *ptr, int size)
+{
+	unsigned long ret;
+
+	switch (size) {
+	case 1:
+		ret = READ_ONCE(*(u8 *)ptr);
+		break;
+	case 2:
+		ret = READ_ONCE(*(u16 *)ptr);
+		break;
+	case 4:
+		ret = READ_ONCE(*(u32 *)ptr);
+		break;
+	case 8:
+		ret = READ_ONCE(*(u64 *)ptr);
+		break;
+	default:
+		ret = 0;
+		BUILD_BUG();
+	}
+
+	return ret;
+}
+
+static inline void __percpu_write(void *ptr, unsigned long val, int size)
+{
+	switch (size) {
+	case 1:
+		WRITE_ONCE(*(u8 *)ptr, (u8)val);
+		break;
+	case 2:
+		WRITE_ONCE(*(u16 *)ptr, (u16)val);
+		break;
+	case 4:
+		WRITE_ONCE(*(u32 *)ptr, (u32)val);
+		break;
+	case 8:
+		WRITE_ONCE(*(u64 *)ptr, (u64)val);
+		break;
+	default:
+		BUILD_BUG();
+	}
+}
+
+static inline unsigned long __percpu_xchg(void *ptr, unsigned long val,
+						int size)
+{
+	switch (size) {
+	case 1:
+	case 2:
+		return __xchg_small((volatile void *)ptr, val, size);
+
+	case 4:
+		return __xchg_asm("amswap.w", (volatile u32 *)ptr, (u32)val);
+
+	case 8:
+		if (!IS_ENABLED(CONFIG_64BIT))
+			return __xchg_called_with_bad_pointer();
+
+		return __xchg_asm("amswap.d", (volatile u64 *)ptr, (u64)val);
+
+	default:
+		return __xchg_called_with_bad_pointer();
+	}
+}
+
+/* this_cpu_cmpxchg */
+#define _protect_cmpxchg_local(pcp, o, n)			\
+({								\
+	typeof(*raw_cpu_ptr(&(pcp))) __ret;			\
+	__ret = cmpxchg_local(raw_cpu_ptr(&(pcp)), o, n);	\
+	__ret;							\
+})
+
+#define this_cpu_cmpxchg_1(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
+#define this_cpu_cmpxchg_2(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
+#define this_cpu_cmpxchg_4(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
+#define this_cpu_cmpxchg_8(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
+
+#define _percpu_read(pcp)						\
+({									\
+	typeof(pcp) __retval;						\
+	__retval = (typeof(pcp))__percpu_read(raw_cpu_ptr(&(pcp)),	\
+					      sizeof(pcp));		\
+	__retval;							\
+})
+
+#define _percpu_write(pcp, val)						\
+do {									\
+	__percpu_write(raw_cpu_ptr(&(pcp)), (unsigned long)(val),	\
+				sizeof(pcp));				\
+} while (0)								\
+
+#define _pcp_protect(operation, pcp, val)			\
+({								\
+	typeof(pcp) __retval;					\
+	__retval = (typeof(pcp))operation(raw_cpu_ptr(&(pcp)),	\
+					  (val), sizeof(pcp));	\
+	__retval;						\
+})
+
+#define _percpu_add(pcp, val) \
+	_pcp_protect(__percpu_add, pcp, val)
+
+#define _percpu_add_return(pcp, val) _percpu_add(pcp, val)
+
+#define _percpu_and(pcp, val) \
+	_pcp_protect(__percpu_and, pcp, val)
+
+#define _percpu_or(pcp, val) \
+	_pcp_protect(__percpu_or, pcp, val)
+
+#define _percpu_xchg(pcp, val) ((typeof(pcp)) \
+	_pcp_protect(__percpu_xchg, pcp, (unsigned long)(val)))
+
+#define this_cpu_add_4(pcp, val) _percpu_add(pcp, val)
+#define this_cpu_add_8(pcp, val) _percpu_add(pcp, val)
+
+#define this_cpu_add_return_4(pcp, val) _percpu_add_return(pcp, val)
+#define this_cpu_add_return_8(pcp, val) _percpu_add_return(pcp, val)
+
+#define this_cpu_and_4(pcp, val) _percpu_and(pcp, val)
+#define this_cpu_and_8(pcp, val) _percpu_and(pcp, val)
+
+#define this_cpu_or_4(pcp, val) _percpu_or(pcp, val)
+#define this_cpu_or_8(pcp, val) _percpu_or(pcp, val)
+
+#define this_cpu_read_1(pcp) _percpu_read(pcp)
+#define this_cpu_read_2(pcp) _percpu_read(pcp)
+#define this_cpu_read_4(pcp) _percpu_read(pcp)
+#define this_cpu_read_8(pcp) _percpu_read(pcp)
+
+#define this_cpu_write_1(pcp, val) _percpu_write(pcp, val)
+#define this_cpu_write_2(pcp, val) _percpu_write(pcp, val)
+#define this_cpu_write_4(pcp, val) _percpu_write(pcp, val)
+#define this_cpu_write_8(pcp, val) _percpu_write(pcp, val)
+
+#define this_cpu_xchg_1(pcp, val) _percpu_xchg(pcp, val)
+#define this_cpu_xchg_2(pcp, val) _percpu_xchg(pcp, val)
+#define this_cpu_xchg_4(pcp, val) _percpu_xchg(pcp, val)
+#define this_cpu_xchg_8(pcp, val) _percpu_xchg(pcp, val)
+
 #include <asm-generic/percpu.h>
 
 #endif /* __ASM_PERCPU_H */
diff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/asm/pgtable.h
index 015e750a932e..0458684c3d24 100644
--- a/arch/loongarch/include/asm/pgtable.h
+++ b/arch/loongarch/include/asm/pgtable.h
@@ -92,8 +92,29 @@ static inline void set_pte(pte_t *ptep, pte_t pteval)
 		 * Make sure the buddy is global too (if it's !none,
 		 * it better already be global)
 		 */
+#ifdef CONFIG_SMP
+		/*
+		 * For SMP, multiple CPUs can race, so we need to do
+		 * this atomically.
+		 */
+		unsigned long page_global = _PAGE_GLOBAL;
+		unsigned long tmp;
+
+		__asm__ __volatile__ (
+		"1:"	__LL	"%[tmp], %[buddy]		\n"
+		"	bnez	%[tmp], 2f			\n"
+		"	 or	%[tmp], %[tmp], %[global]	\n"
+			__SC	"%[tmp], %[buddy]		\n"
+		"	beqz	%[tmp], 1b			\n"
+		"	nop					\n"
+		"2:						\n"
+		__WEAK_LLSC_MB
+		: [buddy] "+m" (buddy->pte), [tmp] "=&r" (tmp)
+		: [global] "r" (page_global));
+#else /* !CONFIG_SMP */
 		if (pte_none(*buddy))
 			pte_val(*buddy) = pte_val(*buddy) | _PAGE_GLOBAL;
+#endif /* CONFIG_SMP */
 	}
 }
 
diff --git a/arch/loongarch/include/asm/smp.h b/arch/loongarch/include/asm/smp.h
new file mode 100644
index 000000000000..102ac82b3d6e
--- /dev/null
+++ b/arch/loongarch/include/asm/smp.h
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef __ASM_SMP_H
+#define __ASM_SMP_H
+
+#include <linux/atomic.h>
+#include <linux/bitops.h>
+#include <linux/linkage.h>
+#include <linux/smp.h>
+#include <linux/threads.h>
+#include <linux/cpumask.h>
+
+#ifdef CONFIG_SMP
+
+struct task_struct;
+
+struct plat_smp_ops {
+	void (*send_ipi_single)(int cpu, unsigned int action);
+	void (*send_ipi_mask)(const struct cpumask *mask, unsigned int action);
+	void (*smp_setup)(void);
+	void (*prepare_cpus)(unsigned int max_cpus);
+	int (*boot_secondary)(int cpu, struct task_struct *idle);
+	void (*init_secondary)(void);
+	void (*smp_finish)(void);
+#ifdef CONFIG_HOTPLUG_CPU
+	int (*cpu_disable)(void);
+	void (*cpu_die)(unsigned int cpu);
+#endif
+};
+
+extern void register_smp_ops(const struct plat_smp_ops *ops);
+
+static inline void plat_smp_setup(void)
+{
+	extern const struct plat_smp_ops *mp_ops;	/* private */
+
+	mp_ops->smp_setup();
+}
+
+#else /* !CONFIG_SMP */
+
+struct plat_smp_ops;
+
+static inline void plat_smp_setup(void)
+{
+	/* UP, nothing to do ...  */
+}
+
+static inline void register_smp_ops(const struct plat_smp_ops *ops)
+{
+}
+
+#endif /* !CONFIG_SMP */
+
+extern int smp_num_siblings;
+extern int num_processors;
+extern int disabled_cpus;
+extern cpumask_t cpu_sibling_map[];
+extern cpumask_t cpu_core_map[];
+extern cpumask_t cpu_foreign_map[];
+
+static inline int raw_smp_processor_id(void)
+{
+#if defined(__VDSO__)
+	extern int vdso_smp_processor_id(void)
+		__compiletime_error("VDSO should not call smp_processor_id()");
+	return vdso_smp_processor_id();
+#else
+	return current_thread_info()->cpu;
+#endif
+}
+#define raw_smp_processor_id raw_smp_processor_id
+
+/* Map from cpu id to sequential logical cpu number.  This will only
+ * not be idempotent when cpus failed to come on-line.	*/
+extern int __cpu_number_map[NR_CPUS];
+#define cpu_number_map(cpu)  __cpu_number_map[cpu]
+
+/* The reverse map from sequential logical cpu number to cpu id.  */
+extern int __cpu_logical_map[NR_CPUS];
+#define cpu_logical_map(cpu)  __cpu_logical_map[cpu]
+
+#define cpu_physical_id(cpu)	cpu_logical_map(cpu)
+
+#define SMP_RESCHEDULE		0x1
+#define SMP_CALL_FUNCTION	0x2
+
+extern asmlinkage void smpboot_entry(void);
+
+extern void calculate_cpu_foreign_map(void);
+
+/*
+ * Generate IPI list text
+ */
+extern void show_ipi_list(struct seq_file *p, int prec);
+
+/*
+ * this function sends a 'reschedule' IPI to another CPU.
+ * it goes straight through and wastes no time serializing
+ * anything. Worst case is that we lose a reschedule ...
+ */
+static inline void smp_send_reschedule(int cpu)
+{
+	extern const struct plat_smp_ops *mp_ops;	/* private */
+
+	mp_ops->send_ipi_single(cpu, SMP_RESCHEDULE);
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+static inline int __cpu_disable(void)
+{
+	extern const struct plat_smp_ops *mp_ops;	/* private */
+
+	return mp_ops->cpu_disable();
+}
+
+static inline void __cpu_die(unsigned int cpu)
+{
+	extern const struct plat_smp_ops *mp_ops;	/* private */
+
+	mp_ops->cpu_die(cpu);
+}
+
+extern void play_dead(void);
+#endif
+
+static inline void arch_send_call_function_single_ipi(int cpu)
+{
+	extern const struct plat_smp_ops *mp_ops;	/* private */
+
+	mp_ops->send_ipi_single(cpu, SMP_CALL_FUNCTION);
+}
+
+static inline void arch_send_call_function_ipi_mask(const struct cpumask *mask)
+{
+	extern const struct plat_smp_ops *mp_ops;	/* private */
+
+	mp_ops->send_ipi_mask(mask, SMP_CALL_FUNCTION);
+}
+
+#endif /* __ASM_SMP_H */
diff --git a/arch/loongarch/include/asm/stackframe.h b/arch/loongarch/include/asm/stackframe.h
index bf0a0ad263b1..db462a457f1a 100644
--- a/arch/loongarch/include/asm/stackframe.h
+++ b/arch/loongarch/include/asm/stackframe.h
@@ -79,6 +79,25 @@
  * t0 and loads the new value in sp.  If not, it clobbers t0 and
  * stores the new value in t1, leaving sp unaffected.
  */
+#ifdef CONFIG_SMP
+	/* SMP variation */
+	.macro	get_saved_sp docfi=0 tosp=0
+	csrrd	t0, PERCPU_BASE_KS
+	la.abs	t1, kernelsp
+	LONG_ADDU	t1, t1, t0
+	.if \tosp
+	or	t0, sp, zero
+	LONG_L	sp, t1, 0
+	.endif
+	.endm
+
+	.macro	set_saved_sp stackp temp temp2
+	la.abs	\temp, kernelsp
+	LONG_ADDU	\temp, \temp, x0
+	LONG_S	\stackp, \temp, 0
+	.endm
+#else /* !CONFIG_SMP */
+	/* Uniprocessor variation */
 	.macro	get_saved_sp docfi=0 tosp=0
 	la.abs	t1, kernelsp
 	.if \tosp
@@ -96,6 +115,7 @@
 	la.abs	\temp, kernelsp
 	LONG_S	\stackp, \temp, 0
 	.endm
+#endif
 
 	.macro	SAVE_SOME docfi=0
 	csrrd	t1, LOONGARCH_CSR_PRMD
diff --git a/arch/loongarch/include/asm/tlbflush.h b/arch/loongarch/include/asm/tlbflush.h
index a5672367d966..32862e53b845 100644
--- a/arch/loongarch/include/asm/tlbflush.h
+++ b/arch/loongarch/include/asm/tlbflush.h
@@ -18,14 +18,22 @@
  */
 extern void local_flush_tlb_all(void);
 extern void local_flush_tlb_mm(struct mm_struct *mm);
-extern void local_flush_tlb_range(struct vm_area_struct *vma,
-	unsigned long start, unsigned long end);
-extern void local_flush_tlb_kernel_range(unsigned long start,
-	unsigned long end);
-extern void local_flush_tlb_page(struct vm_area_struct *vma,
-	unsigned long page);
+extern void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end);
+extern void local_flush_tlb_kernel_range(unsigned long start, unsigned long end);
+extern void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long page);
 extern void local_flush_tlb_one(unsigned long vaddr);
 
+#ifdef CONFIG_SMP
+
+extern void flush_tlb_all(void);
+extern void flush_tlb_mm(struct mm_struct *);
+extern void flush_tlb_range(struct vm_area_struct *vma, unsigned long, unsigned long);
+extern void flush_tlb_kernel_range(unsigned long, unsigned long);
+extern void flush_tlb_page(struct vm_area_struct *, unsigned long);
+extern void flush_tlb_one(unsigned long vaddr);
+
+#else /* CONFIG_SMP */
+
 #define flush_tlb_all()			local_flush_tlb_all()
 #define flush_tlb_mm(mm)		local_flush_tlb_mm(mm)
 #define flush_tlb_range(vma, vmaddr, end)	local_flush_tlb_range(vma, vmaddr, end)
@@ -33,4 +41,6 @@ extern void local_flush_tlb_one(unsigned long vaddr);
 #define flush_tlb_page(vma, page)	local_flush_tlb_page(vma, page)
 #define flush_tlb_one(vaddr)		local_flush_tlb_one(vaddr)
 
+#endif /* CONFIG_SMP */
+
 #endif /* __ASM_TLBFLUSH_H */
diff --git a/arch/loongarch/include/asm/topology.h b/arch/loongarch/include/asm/topology.h
index bf39f3911424..bc541a723f8a 100644
--- a/arch/loongarch/include/asm/topology.h
+++ b/arch/loongarch/include/asm/topology.h
@@ -7,7 +7,12 @@
 
 #include <linux/smp.h>
 
-#define cpu_logical_map(cpu)  0
+#ifdef CONFIG_SMP
+#define topology_physical_package_id(cpu)	(cpu_data[cpu].package)
+#define topology_core_id(cpu)			(cpu_core(&cpu_data[cpu]))
+#define topology_core_cpumask(cpu)		(&cpu_core_map[cpu])
+#define topology_sibling_cpumask(cpu)		(&cpu_sibling_map[cpu])
+#endif
 
 #include <asm-generic/topology.h>
 
diff --git a/arch/loongarch/include/asm/vdso/processor.h b/arch/loongarch/include/asm/vdso/processor.h
index 7fb46fd66512..8db952a616a6 100644
--- a/arch/loongarch/include/asm/vdso/processor.h
+++ b/arch/loongarch/include/asm/vdso/processor.h
@@ -7,7 +7,16 @@
 
 #ifndef __ASSEMBLY__
 
-#define cpu_relax()	barrier()
+/*
+ * Loongson-3's SFB (Store-Fill-Buffer) may buffer writes indefinitely when a
+ * tight read loop is executed, because reads take priority over writes & the
+ * hardware (incorrectly) doesn't ensure that writes will eventually occur.
+ *
+ * Since spin loops of any kind should have a cpu_relax() in them, force an SFB
+ * flush from cpu_relax() such that any pending writes will become visible as
+ * expected.
+ */
+#define cpu_relax()	smp_mb()
 
 #endif /* __ASSEMBLY__ */
 
diff --git a/arch/loongarch/kernel/Makefile b/arch/loongarch/kernel/Makefile
index e37f8016d93c..e8ac09514544 100644
--- a/arch/loongarch/kernel/Makefile
+++ b/arch/loongarch/kernel/Makefile
@@ -17,6 +17,8 @@ obj-$(CONFIG_MODULES)		+= module.o
 
 obj-$(CONFIG_CPU_HAS_FPU)	+= fpu.o
 
+obj-$(CONFIG_SMP)		+= smp.o
+
 obj-$(CONFIG_64BIT)		+= scall64.o
 
 obj-$(CONFIG_PROC_FS)		+= proc.o
diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c
index 632a2da2130c..3c300e53d724 100644
--- a/arch/loongarch/kernel/acpi.c
+++ b/arch/loongarch/kernel/acpi.c
@@ -128,6 +128,35 @@ void __init acpi_boot_table_init(void)
 	}
 }
 
+static int set_processor_mask(u32 id, u32 flags)
+{
+
+	int cpu, cpuid = id;
+
+	if (num_processors >= nr_cpu_ids) {
+		pr_warn("acpi: nr_cpus/possible_cpus limit of %i reached."
+			"processor 0x%x ignored.\n", nr_cpu_ids, cpuid);
+
+		return -ENODEV;
+
+	}
+	if (cpuid == loongson_sysconf.boot_cpu_id)
+		cpu = 0;
+	else
+		cpu = cpumask_next_zero(-1, cpu_present_mask);
+
+	if (flags & ACPI_MADT_ENABLED) {
+		num_processors++;
+		set_cpu_possible(cpu, true);
+		set_cpu_present(cpu, true);
+		__cpu_number_map[cpuid] = cpu;
+		__cpu_logical_map[cpu] = cpuid;
+	} else
+		disabled_cpus++;
+
+	return cpu;
+}
+
 static int __init
 acpi_parse_cpuintc(union acpi_subtable_headers *header, const unsigned long end)
 {
@@ -138,6 +167,7 @@ acpi_parse_cpuintc(union acpi_subtable_headers *header, const unsigned long end)
 		return -EINVAL;
 
 	acpi_table_print_madt_entry(&header->common);
+	set_processor_mask(processor->core_id, processor->flags);
 
 	return 0;
 }
@@ -235,7 +265,12 @@ acpi_parse_pch_pic(union acpi_subtable_headers *header, const unsigned long end)
 
 static void __init acpi_process_madt(void)
 {
-	int error;
+	int i, error;
+
+	for (i = 0; i < NR_CPUS; i++) {
+		__cpu_number_map[i] = -1;
+		__cpu_logical_map[i] = -1;
+	}
 
 	/* Parse MADT CPUINTC entries */
 	error = acpi_table_parse_madt(ACPI_MADT_TYPE_CORE_PIC, acpi_parse_cpuintc, MAX_CORE_PIC);
@@ -323,3 +358,36 @@ void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
 	loongson_mem_map->map[map_count].mem_type = ADDRESS_TYPE_ACPI;
 	loongson_mem_map->map_count++;
 }
+
+#ifdef CONFIG_ACPI_HOTPLUG_CPU
+
+#include <acpi/processor.h>
+
+int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id, int *pcpu)
+{
+	int cpu;
+
+	cpu = set_processor_mask(physid, ACPI_MADT_ENABLED);
+	if (cpu < 0) {
+		pr_info(PREFIX "Unable to map lapic to logical cpu number\n");
+		return cpu;
+	}
+
+	*pcpu = cpu;
+
+	return 0;
+}
+EXPORT_SYMBOL(acpi_map_cpu);
+
+int acpi_unmap_cpu(int cpu)
+{
+	set_cpu_present(cpu, false);
+	num_processors--;
+
+	pr_info("cpu%d hot remove!\n", cpu);
+
+	return 0;
+}
+EXPORT_SYMBOL(acpi_unmap_cpu);
+
+#endif /* CONFIG_ACPI_HOTPLUG_CPU */
diff --git a/arch/loongarch/kernel/head.S b/arch/loongarch/kernel/head.S
index 0658db1ecb56..4a25f03b0d29 100644
--- a/arch/loongarch/kernel/head.S
+++ b/arch/loongarch/kernel/head.S
@@ -65,3 +65,22 @@ SYM_CODE_START(kernel_entry)			# kernel entry point
 	b		start_kernel
 
 SYM_CODE_END(kernel_entry)
+
+#ifdef CONFIG_SMP
+
+/*
+ * SMP slave cpus entry point.	Board specific code for bootstrap calls this
+ * function after setting up the stack and tp registers.
+ */
+SYM_CODE_START(smpboot_entry)
+	li.d		t0, CSR_DMW0_INIT	# UC, PLV0
+	csrwr		t0, LOONGARCH_CSR_DMWIN0
+	li.d		t0, CSR_DMW1_INIT	# CA, PLV0
+	csrwr		t0, LOONGARCH_CSR_DMWIN1
+	li.w		t0, 0xb0		# PLV=0, IE=0, PG=1
+	csrwr		t0, LOONGARCH_CSR_CRMD
+
+	b		start_secondary
+SYM_CODE_END(smpboot_entry)
+
+#endif /* CONFIG_SMP */
diff --git a/arch/loongarch/kernel/irq.c b/arch/loongarch/kernel/irq.c
index e0912976ef31..cacf928b03f3 100644
--- a/arch/loongarch/kernel/irq.c
+++ b/arch/loongarch/kernel/irq.c
@@ -34,6 +34,7 @@ atomic_t irq_err_count;
 
 int arch_show_interrupts(struct seq_file *p, int prec)
 {
+	show_ipi_list(p, prec);
 	seq_printf(p, "%*s: %10u\n", prec, "ERR", atomic_read(&irq_err_count));
 	return 0;
 }
diff --git a/arch/loongarch/kernel/proc.c b/arch/loongarch/kernel/proc.c
index 14e8fff4321e..443ede08c386 100644
--- a/arch/loongarch/kernel/proc.c
+++ b/arch/loongarch/kernel/proc.c
@@ -35,6 +35,11 @@ static int show_cpuinfo(struct seq_file *m, void *v)
 	unsigned int fp_version = cpu_data[n].fpu_vers;
 	struct proc_cpuinfo_notifier_args proc_cpuinfo_notifier_args;
 
+#ifdef CONFIG_SMP
+	if (!cpu_online(n))
+		return 0;
+#endif
+
 	/*
 	 * For the first processor also print the system type
 	 */
diff --git a/arch/loongarch/kernel/reset.c b/arch/loongarch/kernel/reset.c
index cdbeeaa052b3..9b7ab918654b 100644
--- a/arch/loongarch/kernel/reset.c
+++ b/arch/loongarch/kernel/reset.c
@@ -36,16 +36,28 @@ EXPORT_SYMBOL(pm_power_off);
 
 void machine_halt(void)
 {
+#ifdef CONFIG_SMP
+	preempt_disable();
+	smp_send_stop();
+#endif
 	machine_hang();
 }
 
 void machine_power_off(void)
 {
+#ifdef CONFIG_SMP
+	preempt_disable();
+	smp_send_stop();
+#endif
 	pm_power_off();
 }
 
 void machine_restart(char *command)
 {
+#ifdef CONFIG_SMP
+	preempt_disable();
+	smp_send_stop();
+#endif
 	do_kernel_restart(command);
 	pm_restart();
 }
diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
index ba63b45ed82f..0ae24254c304 100644
--- a/arch/loongarch/kernel/setup.c
+++ b/arch/loongarch/kernel/setup.c
@@ -26,6 +26,7 @@
 #include <asm/dma.h>
 #include <asm/sections.h>
 #include <asm/setup.h>
+#include <asm/smp.h>
 
 struct cpuinfo_loongarch cpu_data[NR_CPUS] __read_mostly;
 
@@ -379,6 +380,29 @@ static void __init resource_init(void)
 	}
 }
 
+#ifdef CONFIG_SMP
+static void __init prefill_possible_map(void)
+{
+	int i, possible;
+
+	possible = num_processors + disabled_cpus;
+	if (possible > nr_cpu_ids)
+		possible = nr_cpu_ids;
+
+	pr_info("SMP: Allowing %d CPUs, %d hotplug CPUs\n",
+			possible, max((possible - num_processors), 0));
+
+	for (i = 0; i < possible; i++)
+		set_cpu_possible(i, true);
+	for (; i < NR_CPUS; i++)
+		set_cpu_possible(i, false);
+
+	nr_cpu_ids = possible;
+}
+#else
+static inline void prefill_possible_map(void) {}
+#endif
+
 void __init setup_arch(char **cmdline_p)
 {
 	cpu_probe();
@@ -403,6 +427,8 @@ void __init setup_arch(char **cmdline_p)
 	arch_mem_init(cmdline_p);
 
 	resource_init();
+	plat_smp_setup();
+	prefill_possible_map();
 
 	cpu_cache_init();
 	paging_init();
diff --git a/arch/loongarch/kernel/signal.c b/arch/loongarch/kernel/signal.c
index b722b9e7c380..2b1d88736244 100644
--- a/arch/loongarch/kernel/signal.c
+++ b/arch/loongarch/kernel/signal.c
@@ -557,6 +557,20 @@ asmlinkage void do_notify_resume(struct pt_regs *regs, void *unused,
 	user_enter();
 }
 
+#ifdef CONFIG_SMP
+static int smp_save_fp_context(void __user *sc)
+{
+	return save_hw_fp_context(sc);
+}
+
+static int smp_restore_fp_context(void __user *sc)
+{
+	return cpu_has_fpu
+	       ? restore_hw_fp_context(sc)
+	       : copy_fp_from_sigcontext(sc);
+}
+#endif
+
 static int signal_setup(void)
 {
 	/*
@@ -569,6 +583,11 @@ static int signal_setup(void)
 		     (offsetof(struct rt_sigframe, rs_uc.uc_extcontext) -
 		      offsetof(struct rt_sigframe, rs_uc.uc_mcontext)));
 
+#ifdef CONFIG_SMP
+	/* For now just do the cpu_has_fpu check when the functions are invoked */
+	save_fp_context = smp_save_fp_context;
+	restore_fp_context = smp_restore_fp_context;
+#else
 	if (cpu_has_fpu) {
 		save_fp_context = save_hw_fp_context;
 		restore_fp_context = restore_hw_fp_context;
@@ -576,6 +595,7 @@ static int signal_setup(void)
 		save_fp_context = copy_fp_to_sigcontext;
 		restore_fp_context = copy_fp_from_sigcontext;
 	}
+#endif /* CONFIG_SMP */
 
 	return 0;
 }
diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c
new file mode 100644
index 000000000000..7532b0b6f2f8
--- /dev/null
+++ b/arch/loongarch/kernel/smp.c
@@ -0,0 +1,435 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/cache.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/smp.h>
+#include <linux/spinlock.h>
+#include <linux/threads.h>
+#include <linux/export.h>
+#include <linux/time.h>
+#include <linux/timex.h>
+#include <linux/sched/mm.h>
+#include <linux/cpumask.h>
+#include <linux/cpu.h>
+#include <linux/err.h>
+#include <linux/ftrace.h>
+#include <linux/of.h>
+#include <linux/of_irq.h>
+
+#include <asm/cpu.h>
+#include <asm/processor.h>
+#include <asm/idle.h>
+#include <asm/mmu_context.h>
+#include <asm/time.h>
+#include <asm/setup.h>
+
+int __cpu_number_map[NR_CPUS];   /* Map physical to logical */
+EXPORT_SYMBOL(__cpu_number_map);
+
+int __cpu_logical_map[NR_CPUS];		/* Map logical to physical */
+EXPORT_SYMBOL(__cpu_logical_map);
+
+/* Number of threads (siblings) per CPU core */
+int smp_num_siblings = 1;
+EXPORT_SYMBOL(smp_num_siblings);
+
+/* Representing the threads (siblings) of each logical CPU */
+cpumask_t cpu_sibling_map[NR_CPUS] __read_mostly;
+EXPORT_SYMBOL(cpu_sibling_map);
+
+/* Representing the core map of multi-core chips of each logical CPU */
+cpumask_t cpu_core_map[NR_CPUS] __read_mostly;
+EXPORT_SYMBOL(cpu_core_map);
+
+static DECLARE_COMPLETION(cpu_starting);
+static DECLARE_COMPLETION(cpu_running);
+
+/*
+ * A logcal cpu mask containing only one VPE per core to
+ * reduce the number of IPIs on large MT systems.
+ */
+cpumask_t cpu_foreign_map[NR_CPUS] __read_mostly;
+EXPORT_SYMBOL(cpu_foreign_map);
+
+/* representing cpus for which sibling maps can be computed */
+static cpumask_t cpu_sibling_setup_map;
+
+/* representing cpus for which core maps can be computed */
+static cpumask_t cpu_core_setup_map;
+
+static inline void set_cpu_sibling_map(int cpu)
+{
+	int i;
+
+	cpumask_set_cpu(cpu, &cpu_sibling_setup_map);
+
+	if (smp_num_siblings > 1) {
+		for_each_cpu(i, &cpu_sibling_setup_map) {
+			if (cpus_are_siblings(cpu, i)) {
+				cpumask_set_cpu(i, &cpu_sibling_map[cpu]);
+				cpumask_set_cpu(cpu, &cpu_sibling_map[i]);
+			}
+		}
+	} else
+		cpumask_set_cpu(cpu, &cpu_sibling_map[cpu]);
+}
+
+static inline void set_cpu_core_map(int cpu)
+{
+	int i;
+
+	cpumask_set_cpu(cpu, &cpu_core_setup_map);
+
+	for_each_cpu(i, &cpu_core_setup_map) {
+		if (cpu_data[cpu].package == cpu_data[i].package) {
+			cpumask_set_cpu(i, &cpu_core_map[cpu]);
+			cpumask_set_cpu(cpu, &cpu_core_map[i]);
+		}
+	}
+}
+
+/*
+ * Calculate a new cpu_foreign_map mask whenever a
+ * new cpu appears or disappears.
+ */
+void calculate_cpu_foreign_map(void)
+{
+	int i, k, core_present;
+	cpumask_t temp_foreign_map;
+
+	/* Re-calculate the mask */
+	cpumask_clear(&temp_foreign_map);
+	for_each_online_cpu(i) {
+		core_present = 0;
+		for_each_cpu(k, &temp_foreign_map)
+			if (cpus_are_siblings(i, k))
+				core_present = 1;
+		if (!core_present)
+			cpumask_set_cpu(i, &temp_foreign_map);
+	}
+
+	for_each_online_cpu(i)
+		cpumask_andnot(&cpu_foreign_map[i],
+			       &temp_foreign_map, &cpu_sibling_map[i]);
+}
+
+const struct plat_smp_ops *mp_ops;
+EXPORT_SYMBOL(mp_ops);
+
+void register_smp_ops(const struct plat_smp_ops *ops)
+{
+	if (mp_ops)
+		pr_notice("Overriding previously set SMP ops\n");
+
+	mp_ops = ops;
+}
+
+/*
+ * First C code run on the secondary CPUs after being started up by
+ * the master.
+ */
+asmlinkage void start_secondary(void)
+{
+	unsigned int cpu;
+
+	sync_counter();
+	cpu = smp_processor_id();
+	set_my_cpu_offset(per_cpu_offset(cpu));
+
+	cpu_probe();
+	nonboot_cpu_trap_init();
+	constant_clockevent_init();
+	mp_ops->init_secondary();
+	cpu_report();
+
+	calibrate_delay();
+	if (!cpu_data[cpu].udelay_val)
+		cpu_data[cpu].udelay_val = loops_per_jiffy;
+
+	notify_cpu_starting(cpu);
+
+	/* Notify boot CPU that we're starting */
+	complete(&cpu_starting);
+
+	/* The CPU is running, now mark it online */
+	set_cpu_online(cpu, true);
+
+	set_cpu_sibling_map(cpu);
+	set_cpu_core_map(cpu);
+
+	calculate_cpu_foreign_map();
+
+	/*
+	 * Notify boot CPU that we're up & online and it can safely return
+	 * from __cpu_up
+	 */
+	complete(&cpu_running);
+
+	/*
+	 * irq will be enabled in ->smp_finish(), enabling it too early
+	 * is dangerous.
+	 */
+	WARN_ON_ONCE(!irqs_disabled());
+	mp_ops->smp_finish();
+
+	cpu_startup_entry(CPUHP_AP_ONLINE_IDLE);
+}
+
+static void stop_this_cpu(void *dummy)
+{
+	/*
+	 * Remove this CPU:
+	 */
+
+	set_cpu_online(smp_processor_id(), false);
+	calculate_cpu_foreign_map();
+	local_irq_disable();
+	while (1);
+}
+
+void smp_send_stop(void)
+{
+	smp_call_function(stop_this_cpu, NULL, 0);
+}
+
+void __init smp_cpus_done(unsigned int max_cpus)
+{
+}
+
+/* called from main before smp_init() */
+void __init smp_prepare_cpus(unsigned int max_cpus)
+{
+	init_new_context(current, &init_mm);
+	current_thread_info()->cpu = 0;
+	mp_ops->prepare_cpus(max_cpus);
+	set_cpu_sibling_map(0);
+	set_cpu_core_map(0);
+	calculate_cpu_foreign_map();
+#ifndef CONFIG_HOTPLUG_CPU
+	init_cpu_present(cpu_possible_mask);
+#endif
+}
+
+/* Preload SMP state for boot cpu */
+void smp_prepare_boot_cpu(void)
+{
+	unsigned int cpu;
+
+	set_cpu_possible(0, true);
+	set_cpu_online(0, true);
+	set_my_cpu_offset(per_cpu_offset(0));
+
+	for_each_possible_cpu(cpu)
+		set_cpu_numa_node(cpu, 0);
+}
+
+int __cpu_up(unsigned int cpu, struct task_struct *tidle)
+{
+	int err;
+
+	err = mp_ops->boot_secondary(cpu, tidle);
+	if (err)
+		return err;
+
+	/* Wait for CPU to start and be ready to sync counters */
+	if (!wait_for_completion_timeout(&cpu_starting,
+					 msecs_to_jiffies(1000))) {
+		pr_crit("CPU%u: failed to start\n", cpu);
+		return -EIO;
+	}
+
+	/* Wait for CPU to finish startup & mark itself online before return */
+	wait_for_completion(&cpu_running);
+	return 0;
+}
+
+/* Not really SMP stuff ... */
+int setup_profiling_timer(unsigned int multiplier)
+{
+	return 0;
+}
+
+static void flush_tlb_all_ipi(void *info)
+{
+	local_flush_tlb_all();
+}
+
+void flush_tlb_all(void)
+{
+	on_each_cpu(flush_tlb_all_ipi, NULL, 1);
+}
+
+static void flush_tlb_mm_ipi(void *mm)
+{
+	local_flush_tlb_mm((struct mm_struct *)mm);
+}
+
+/*
+ * Special Variant of smp_call_function for use by TLB functions:
+ *
+ *  o No return value
+ *  o collapses to normal function call on UP kernels
+ *  o collapses to normal function call on systems with a single shared
+ *    primary cache.
+ */
+static inline void smp_on_other_tlbs(void (*func) (void *info), void *info)
+{
+	smp_call_function(func, info, 1);
+}
+
+static inline void smp_on_each_tlb(void (*func) (void *info), void *info)
+{
+	preempt_disable();
+
+	smp_on_other_tlbs(func, info);
+	func(info);
+
+	preempt_enable();
+}
+
+/*
+ * The following tlb flush calls are invoked when old translations are
+ * being torn down, or pte attributes are changing. For single threaded
+ * address spaces, a new context is obtained on the current cpu, and tlb
+ * context on other cpus are invalidated to force a new context allocation
+ * at switch_mm time, should the mm ever be used on other cpus. For
+ * multithreaded address spaces, intercpu interrupts have to be sent.
+ * Another case where intercpu interrupts are required is when the target
+ * mm might be active on another cpu (eg debuggers doing the flushes on
+ * behalf of debugees, kswapd stealing pages from another process etc).
+ */
+
+void flush_tlb_mm(struct mm_struct *mm)
+{
+	preempt_disable();
+
+	if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) {
+		on_each_cpu_mask(mm_cpumask(mm), flush_tlb_mm_ipi, mm, 1);
+	} else {
+		unsigned int cpu;
+
+		for_each_online_cpu(cpu) {
+			if (cpu != smp_processor_id() && cpu_context(cpu, mm))
+				cpu_context(cpu, mm) = 0;
+		}
+		local_flush_tlb_mm(mm);
+	}
+
+	preempt_enable();
+}
+
+struct flush_tlb_data {
+	struct vm_area_struct *vma;
+	unsigned long addr1;
+	unsigned long addr2;
+};
+
+static void flush_tlb_range_ipi(void *info)
+{
+	struct flush_tlb_data *fd = info;
+
+	local_flush_tlb_range(fd->vma, fd->addr1, fd->addr2);
+}
+
+void flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end)
+{
+	struct mm_struct *mm = vma->vm_mm;
+
+	preempt_disable();
+	if ((atomic_read(&mm->mm_users) != 1) || (current->mm != mm)) {
+		struct flush_tlb_data fd = {
+			.vma = vma,
+			.addr1 = start,
+			.addr2 = end,
+		};
+
+		on_each_cpu_mask(mm_cpumask(mm), flush_tlb_range_ipi, &fd, 1);
+	} else {
+		unsigned int cpu;
+		int exec = vma->vm_flags & VM_EXEC;
+
+		for_each_online_cpu(cpu) {
+			/*
+			 * flush_cache_range() will only fully flush icache if
+			 * the VMA is executable, otherwise we must invalidate
+			 * ASID without it appearing to has_valid_asid() as if
+			 * mm has been completely unused by that CPU.
+			 */
+			if (cpu != smp_processor_id() && cpu_context(cpu, mm))
+				cpu_context(cpu, mm) = !exec;
+		}
+		local_flush_tlb_range(vma, start, end);
+	}
+	preempt_enable();
+}
+
+static void flush_tlb_kernel_range_ipi(void *info)
+{
+	struct flush_tlb_data *fd = info;
+
+	local_flush_tlb_kernel_range(fd->addr1, fd->addr2);
+}
+
+void flush_tlb_kernel_range(unsigned long start, unsigned long end)
+{
+	struct flush_tlb_data fd = {
+		.addr1 = start,
+		.addr2 = end,
+	};
+
+	on_each_cpu(flush_tlb_kernel_range_ipi, &fd, 1);
+}
+
+static void flush_tlb_page_ipi(void *info)
+{
+	struct flush_tlb_data *fd = info;
+
+	local_flush_tlb_page(fd->vma, fd->addr1);
+}
+
+void flush_tlb_page(struct vm_area_struct *vma, unsigned long page)
+{
+	preempt_disable();
+	if ((atomic_read(&vma->vm_mm->mm_users) != 1) || (current->mm != vma->vm_mm)) {
+		struct flush_tlb_data fd = {
+			.vma = vma,
+			.addr1 = page,
+		};
+
+		on_each_cpu_mask(mm_cpumask(vma->vm_mm), flush_tlb_page_ipi, &fd, 1);
+	} else {
+		unsigned int cpu;
+
+		for_each_online_cpu(cpu) {
+			/*
+			 * flush_cache_page() only does partial flushes, so
+			 * invalidate ASID without it appearing to
+			 * has_valid_asid() as if mm has been completely unused
+			 * by that CPU.
+			 */
+			if (cpu != smp_processor_id() && cpu_context(cpu, vma->vm_mm))
+				cpu_context(cpu, vma->vm_mm) = 1;
+		}
+		local_flush_tlb_page(vma, page);
+	}
+	preempt_enable();
+}
+
+static void flush_tlb_one_ipi(void *info)
+{
+	unsigned long vaddr = (unsigned long) info;
+
+	local_flush_tlb_one(vaddr);
+}
+
+void flush_tlb_one(unsigned long vaddr)
+{
+	smp_on_each_tlb(flush_tlb_one_ipi, (void *) vaddr);
+}
+
+EXPORT_SYMBOL(flush_tlb_page);
+EXPORT_SYMBOL(flush_tlb_one);
diff --git a/arch/loongarch/kernel/time.c b/arch/loongarch/kernel/time.c
index 2509a2e9fac3..542590f4bf70 100644
--- a/arch/loongarch/kernel/time.c
+++ b/arch/loongarch/kernel/time.c
@@ -105,6 +105,29 @@ static unsigned long __init get_loops_per_jiffy(void)
 	return lpj;
 }
 
+#ifdef CONFIG_SMP
+/*
+ * If we have a constant timer are using it for the delay loop, we can
+ * skip clock calibration if another cpu in the same socket has already
+ * been calibrated. This assumes that constant timer applies to all
+ * cpus in the socket - this should be a safe assumption.
+ */
+unsigned long calibrate_delay_is_known(void)
+{
+	int next, cpu = smp_processor_id();
+	const struct cpumask *mask = topology_core_cpumask(cpu);
+
+	if (!mask)
+		return 0;
+
+	next = cpumask_any_but(mask, cpu);
+	if (next < nr_cpu_ids)
+		return cpu_data[next].udelay_val;
+
+	return 0;
+}
+#endif
+
 static long init_timeval;
 
 void sync_counter(void)
diff --git a/arch/loongarch/kernel/topology.c b/arch/loongarch/kernel/topology.c
index 3b2cbb95875b..ab1a75c0b5a6 100644
--- a/arch/loongarch/kernel/topology.c
+++ b/arch/loongarch/kernel/topology.c
@@ -1,13 +1,52 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <linux/cpu.h>
+#include <linux/cpumask.h>
 #include <linux/init.h>
+#include <linux/node.h>
+#include <linux/nodemask.h>
 #include <linux/percpu.h>
 
-static struct cpu cpu_device;
+static DEFINE_PER_CPU(struct cpu, cpu_devices);
+
+#ifdef CONFIG_HOTPLUG_CPU
+int arch_register_cpu(int cpu)
+{
+	int ret;
+	struct cpu *c = &per_cpu(cpu_devices, cpu);
+
+	c->hotpluggable = 1;
+	ret = register_cpu(c, cpu);
+	if (ret < 0)
+		pr_warn("register_cpu %d failed (%d)\n", cpu, ret);
+
+	return ret;
+}
+EXPORT_SYMBOL(arch_register_cpu);
+
+void arch_unregister_cpu(int cpu)
+{
+	struct cpu *c = &per_cpu(cpu_devices, cpu);
+
+	c->hotpluggable = 0;
+	unregister_cpu(c);
+}
+EXPORT_SYMBOL(arch_unregister_cpu);
+#endif
 
 static int __init topology_init(void)
 {
-	return register_cpu(&cpu_device, 0);
+	int i, ret;
+
+	for_each_present_cpu(i) {
+		struct cpu *c = &per_cpu(cpu_devices, i);
+
+		c->hotpluggable = !!i;
+		ret = register_cpu(c, i);
+		if (ret < 0)
+			pr_warn("topology_init: register_cpu %d failed (%d)\n", i, ret);
+	}
+
+	return 0;
 }
 
 subsys_initcall(topology_init);
diff --git a/arch/loongarch/kernel/vmlinux.lds.S b/arch/loongarch/kernel/vmlinux.lds.S
index 3ae212100de5..9a919f512d06 100644
--- a/arch/loongarch/kernel/vmlinux.lds.S
+++ b/arch/loongarch/kernel/vmlinux.lds.S
@@ -70,6 +70,9 @@ SECTIONS
 	.exit.data : {
 		EXIT_DATA
 	}
+#ifdef CONFIG_SMP
+	PERCPU_SECTION(1 << CONFIG_L1_CACHE_SHIFT)
+#endif
 
 	/*
 	 * Align to 64K in attempt to eliminate holes before the
diff --git a/arch/loongarch/loongson64/Makefile b/arch/loongarch/loongson64/Makefile
index 42e92e5e12be..b7697cfa2775 100644
--- a/arch/loongarch/loongson64/Makefile
+++ b/arch/loongarch/loongson64/Makefile
@@ -5,4 +5,6 @@
 obj-y += setup.o init.o env.o reset.o irq.o mem.o \
 	 rtc.o boardinfo.o
 
+obj-$(CONFIG_SMP)	+= smp.o
+
 obj-$(CONFIG_PCI_MSI)	+= msi.o
diff --git a/arch/loongarch/loongson64/init.c b/arch/loongarch/loongson64/init.c
index 3fedebbbf665..40aa010c4976 100644
--- a/arch/loongarch/loongson64/init.c
+++ b/arch/loongarch/loongson64/init.c
@@ -13,6 +13,7 @@
 #include <asm/cacheflush.h>
 #include <asm/efi.h>
 #include <asm/fw.h>
+#include <asm/smp.h>
 #include <asm/time.h>
 
 #include <loongson.h>
@@ -135,4 +136,6 @@ void __init platform_init(void)
 	pr_info("The BIOS Version: %s\n", b_info.bios_version);
 
 	efi_runtime_init();
+
+	register_smp_ops(&loongson3_smp_ops);
 }
diff --git a/arch/loongarch/loongson64/irq.c b/arch/loongarch/loongson64/irq.c
index aabe457570ce..a1e0bdebdf30 100644
--- a/arch/loongarch/loongson64/irq.c
+++ b/arch/loongarch/loongson64/irq.c
@@ -73,12 +73,50 @@ void __init setup_IRQ(void)
 	pch_lpc_acpi_init(acpi_picdomain_handle[0], acpi_pchlpc);
 }
 
+#ifdef CONFIG_HOTPLUG_CPU
+void handle_irq_affinity(void)
+{
+	struct irq_desc *desc;
+	struct irq_chip *chip;
+	unsigned int irq;
+	unsigned long flags;
+	struct cpumask *affinity;
+
+	for_each_active_irq(irq) {
+		desc = irq_to_desc(irq);
+		if (!desc)
+			continue;
+
+		raw_spin_lock_irqsave(&desc->lock, flags);
+
+		affinity = desc->irq_data.common->affinity;
+		if (!cpumask_intersects(affinity, cpu_online_mask))
+			cpumask_copy(affinity, cpu_online_mask);
+
+		chip = irq_data_get_irq_chip(&desc->irq_data);
+		if (chip && chip->irq_set_affinity)
+			chip->irq_set_affinity(&desc->irq_data, desc->irq_data.common->affinity, true);
+		raw_spin_unlock_irqrestore(&desc->lock, flags);
+	}
+}
+
+void fixup_irqs(void)
+{
+	handle_irq_affinity();
+	irq_cpu_offline();
+	clear_csr_ecfg(ECFG0_IM);
+}
+#endif
+
 void __init arch_init_irq(void)
 {
 	clear_csr_ecfg(ECFG0_IM);
 	clear_csr_estat(ESTATF_IP);
 
 	setup_IRQ();
+#ifdef CONFIG_SMP
+	set_vi_handler(EXCCODE_IPI, loongson3_ipi_interrupt);
+#endif
 
 	set_csr_ecfg(ECFGF_IP0 | ECFGF_IP1 | ECFGF_IPI | ECFGF_PC);
 }
diff --git a/arch/loongarch/loongson64/smp.c b/arch/loongarch/loongson64/smp.c
new file mode 100644
index 000000000000..8b7d6dcf1315
--- /dev/null
+++ b/arch/loongarch/loongson64/smp.c
@@ -0,0 +1,510 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Author: Huacai Chen <chenhuacai@loongson.cn>
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#include <linux/init.h>
+#include <linux/cpu.h>
+#include <linux/sched.h>
+#include <linux/sched/hotplug.h>
+#include <linux/sched/task_stack.h>
+#include <linux/seq_file.h>
+#include <linux/smp.h>
+#include <linux/syscore_ops.h>
+#include <linux/tracepoint.h>
+#include <asm/processor.h>
+#include <asm/time.h>
+#include <asm/tlbflush.h>
+#include <asm/cacheflush.h>
+#include <loongson.h>
+
+DEFINE_PER_CPU(int, cpu_state);
+DEFINE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
+EXPORT_PER_CPU_SYMBOL(irq_stat);
+
+#define MAX_CPUS 64
+
+#define STATUS  0x00
+#define EN      0x04
+#define SET     0x08
+#define CLEAR   0x0c
+#define MBUF    0x20
+
+extern unsigned long long smp_group[MAX_PACKAGES];
+static u32 core_offsets[4] = {0x000, 0x100, 0x200, 0x300};
+
+static void *ipi_set_regs[MAX_CPUS];
+static void *ipi_clear_regs[MAX_CPUS];
+static void *ipi_status_regs[MAX_CPUS];
+static void *ipi_en_regs[MAX_CPUS];
+static void *ipi_mailbox_buf[MAX_CPUS];
+
+u32 (*ipi_read_clear)(int cpu);
+void (*ipi_write_action)(int cpu, u32 action);
+
+enum ipi_msg_type {
+	IPI_RESCHEDULE,
+	IPI_CALL_FUNCTION,
+};
+
+static const char *ipi_types[NR_IPI] __tracepoint_string = {
+	[IPI_RESCHEDULE] = "Rescheduling interrupts",
+	[IPI_CALL_FUNCTION] = "Call Function interrupts",
+};
+
+void show_ipi_list(struct seq_file *p, int prec)
+{
+	unsigned int cpu, i;
+
+	for (i = 0; i < NR_IPI; i++) {
+		seq_printf(p, "%*s%u:%s", prec - 1, "IPI", i, prec >= 4 ? " " : "");
+		for_each_online_cpu(cpu)
+			seq_printf(p, "%10u ", per_cpu(irq_stat, cpu).ipi_irqs[i]);
+		seq_printf(p, " LoongArch     %s\n", ipi_types[i]);
+	}
+}
+
+/* Send mail buffer via Mail_Send */
+static void csr_mail_send(uint64_t data, int cpu, int mailbox)
+{
+	uint64_t val;
+
+	/* Send high 32 bits */
+	val = IOCSR_MBUF_SEND_BLOCKING;
+	val |= (IOCSR_MBUF_SEND_BOX_HI(mailbox) << IOCSR_MBUF_SEND_BOX_SHIFT);
+	val |= (cpu << IOCSR_MBUF_SEND_CPU_SHIFT);
+	val |= (data & IOCSR_MBUF_SEND_H32_MASK);
+	iocsr_writeq(val, LOONGARCH_IOCSR_MBUF_SEND);
+
+	/* Send low 32 bits */
+	val = IOCSR_MBUF_SEND_BLOCKING;
+	val |= (IOCSR_MBUF_SEND_BOX_LO(mailbox) << IOCSR_MBUF_SEND_BOX_SHIFT);
+	val |= (cpu << IOCSR_MBUF_SEND_CPU_SHIFT);
+	val |= (data << IOCSR_MBUF_SEND_BUF_SHIFT);
+	iocsr_writeq(val, LOONGARCH_IOCSR_MBUF_SEND);
+};
+
+static u32 csr_ipi_read_clear(int cpu)
+{
+	u32 action;
+
+	/* Load the ipi register to figure out what we're supposed to do */
+	action = iocsr_readl(LOONGARCH_IOCSR_IPI_STATUS);
+	/* Clear the ipi register to clear the interrupt */
+	iocsr_writel(action, LOONGARCH_IOCSR_IPI_CLEAR);
+
+	return action;
+}
+
+static void csr_ipi_write_action(int cpu, u32 action)
+{
+	unsigned int irq = 0;
+
+	while ((irq = ffs(action))) {
+		uint32_t val = IOCSR_IPI_SEND_BLOCKING;
+
+		val |= (irq - 1);
+		val |= (cpu << IOCSR_IPI_SEND_CPU_SHIFT);
+		iocsr_writel(val, LOONGARCH_IOCSR_IPI_SEND);
+		action &= ~BIT(irq - 1);
+	}
+}
+
+static u32 legacy_ipi_read_clear(int cpu)
+{
+	u32 action;
+
+	/* Load the ipi register to figure out what we're supposed to do */
+	action = xconf_readl(ipi_status_regs[cpu]);
+	/* Clear the ipi register to clear the interrupt */
+	xconf_writel(action, ipi_clear_regs[cpu]);
+
+	return action;
+}
+
+static void legacy_ipi_write_action(int cpu, u32 action)
+{
+	xconf_writel((u32)action, ipi_set_regs[cpu]);
+}
+
+static void ipi_method_init(void)
+{
+	if (cpu_has_csripi) {
+		ipi_read_clear = csr_ipi_read_clear;
+		ipi_write_action = csr_ipi_write_action;
+	} else {
+		ipi_read_clear = legacy_ipi_read_clear;
+		ipi_write_action = legacy_ipi_write_action;
+	}
+}
+
+static void ipi_regaddrs_init(void)
+{
+	int i, node, core;
+
+	for (i = 0; i < MAX_CPUS; i++) {
+		node = i / 4;
+		core = i % 4;
+		ipi_set_regs[i] = (void *)
+			(smp_group[node] + core_offsets[core] + SET);
+		ipi_clear_regs[i] = (void *)
+			(smp_group[node] + core_offsets[core] + CLEAR);
+		ipi_status_regs[i] = (void *)
+			(smp_group[node] + core_offsets[core] + STATUS);
+		ipi_en_regs[i] = (void *)
+			(smp_group[node] + core_offsets[core] + EN);
+		ipi_mailbox_buf[i] = (void *)
+			(smp_group[node] + core_offsets[core] + MBUF);
+	}
+}
+
+/*
+ * Simple enough, just poke the appropriate ipi register
+ */
+static void loongson3_send_ipi_single(int cpu, unsigned int action)
+{
+	ipi_write_action(cpu_logical_map(cpu), (u32)action);
+}
+
+static void
+loongson3_send_ipi_mask(const struct cpumask *mask, unsigned int action)
+{
+	unsigned int i;
+
+	for_each_cpu(i, mask)
+		ipi_write_action(cpu_logical_map(i), (u32)action);
+}
+
+void loongson3_ipi_interrupt(int irq)
+{
+	unsigned int action;
+	unsigned int cpu = smp_processor_id();
+
+	action = ipi_read_clear(cpu_logical_map(cpu));
+
+	smp_mb();
+
+	if (action & SMP_RESCHEDULE) {
+		scheduler_ipi();
+		per_cpu(irq_stat, cpu).ipi_irqs[IPI_RESCHEDULE]++;
+	}
+
+	if (action & SMP_CALL_FUNCTION) {
+		irq_enter();
+		generic_smp_call_function_interrupt();
+		per_cpu(irq_stat, cpu).ipi_irqs[IPI_CALL_FUNCTION]++;
+		irq_exit();
+	}
+}
+
+/*
+ * SMP init and finish on secondary CPUs
+ */
+static void loongson3_init_secondary(void)
+{
+	unsigned int cpu = smp_processor_id();
+	unsigned int imask = ECFGF_PC | ECFGF_TIMER | ECFGF_IPI | ECFGF_IP1 | ECFGF_IP0;
+
+	/* Set interrupt mask, but don't enable */
+	change_csr_ecfg(ECFG0_IM, imask);
+
+	if (cpu_has_csripi)
+		iocsr_writel(0xffffffff, LOONGARCH_IOCSR_IPI_EN);
+	else
+		xconf_writel(0xffffffff, ipi_en_regs[cpu_logical_map(cpu)]);
+
+	per_cpu(cpu_state, cpu) = CPU_ONLINE;
+	cpu_set_core(&cpu_data[cpu],
+		     cpu_logical_map(cpu) % loongson_sysconf.cores_per_package);
+	cpu_set_cluster(&cpu_data[cpu],
+		     cpu_logical_map(cpu) / loongson_sysconf.cores_per_package);
+	cpu_data[cpu].package =
+		     cpu_logical_map(cpu) / loongson_sysconf.cores_per_package;
+}
+
+static void loongson3_smp_finish(void)
+{
+	int cpu = smp_processor_id();
+
+	local_irq_enable();
+
+	if (cpu_has_csripi)
+		iocsr_writeq(0, LOONGARCH_IOCSR_MBUF0);
+	else
+		xconf_writeq(0, (void *)(ipi_mailbox_buf[cpu_logical_map(cpu)]+0x0));
+
+	pr_info("CPU#%d finished\n", smp_processor_id());
+}
+
+static void __init loongson3_smp_setup(void)
+{
+	ipi_method_init();
+	ipi_regaddrs_init();
+
+	if (cpu_has_csripi)
+		iocsr_writel(0xffffffff, LOONGARCH_IOCSR_IPI_EN);
+	else
+		xconf_writel(0xffffffff, ipi_en_regs[cpu_logical_map(0)]);
+
+	pr_info("Detected %i available CPU(s)\n", loongson_sysconf.nr_cpus);
+
+	cpu_set_core(&cpu_data[0],
+		     cpu_logical_map(0) % loongson_sysconf.cores_per_package);
+	cpu_set_cluster(&cpu_data[0],
+		     cpu_logical_map(0) / loongson_sysconf.cores_per_package);
+	cpu_data[0].package = cpu_logical_map(0) / loongson_sysconf.cores_per_package;
+}
+
+static void __init loongson3_prepare_cpus(unsigned int max_cpus)
+{
+	int i = 0;
+
+	for (i = 0; i < loongson_sysconf.nr_cpus; i++) {
+		set_cpu_present(i, true);
+
+		if (cpu_has_csripi)
+			csr_mail_send(0, __cpu_logical_map[i], 0);
+		else
+			xconf_writeq(0, (void *)(ipi_mailbox_buf[__cpu_logical_map[i]]+0x0));
+	}
+
+	per_cpu(cpu_state, smp_processor_id()) = CPU_ONLINE;
+}
+
+/*
+ * Setup the PC, SP, and TP of a secondary processor and start it running!
+ */
+static int loongson3_boot_secondary(int cpu, struct task_struct *idle)
+{
+	unsigned long startargs[4];
+
+	pr_info("Booting CPU#%d...\n", cpu);
+
+	/* startargs[] are initial PC, SP and TP for secondary CPU */
+	startargs[0] = (unsigned long)&smpboot_entry;
+	startargs[1] = (unsigned long)__KSTK_TOS(idle);
+	startargs[2] = (unsigned long)task_thread_info(idle);
+	startargs[3] = 0;
+
+	pr_debug("CPU#%d, func_pc=%lx, sp=%lx, tp=%lx\n",
+			cpu, startargs[0], startargs[1], startargs[2]);
+
+	if (cpu_has_csripi) {
+		csr_mail_send(startargs[3], cpu_logical_map(cpu), 3);
+		csr_mail_send(startargs[2], cpu_logical_map(cpu), 2);
+		csr_mail_send(startargs[1], cpu_logical_map(cpu), 1);
+		csr_mail_send(startargs[0], cpu_logical_map(cpu), 0);
+	} else {
+		xconf_writeq(startargs[3], (void *)(ipi_mailbox_buf[cpu_logical_map(cpu)]+0x18));
+		xconf_writeq(startargs[2], (void *)(ipi_mailbox_buf[cpu_logical_map(cpu)]+0x10));
+		xconf_writeq(startargs[1], (void *)(ipi_mailbox_buf[cpu_logical_map(cpu)]+0x8));
+		xconf_writeq(startargs[0], (void *)(ipi_mailbox_buf[cpu_logical_map(cpu)]+0x0));
+	}
+	return 0;
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+
+static int loongson3_cpu_disable(void)
+{
+	unsigned long flags;
+	unsigned int cpu = smp_processor_id();
+
+	if (cpu == 0)
+		return -EBUSY;
+
+	set_cpu_online(cpu, false);
+	calculate_cpu_foreign_map();
+	local_irq_save(flags);
+	fixup_irqs();
+	local_irq_restore(flags);
+	local_flush_tlb_all();
+
+	return 0;
+}
+
+
+static void loongson3_cpu_die(unsigned int cpu)
+{
+	while (per_cpu(cpu_state, cpu) != CPU_DEAD)
+		cpu_relax();
+
+	mb();
+}
+
+/* To shutdown a core in Loongson 3, the target core should go to XKPRANGE
+ * and flush all L1 entries at first. Then, another core (usually Core 0)
+ * can safely disable the clock of the target core. loongson3_play_dead()
+ * is called via XKPRANGE (uncached and unmmaped) */
+static void loongson3_play_dead(int *state_addr)
+{
+	register int val;
+	register long cpuid, core, node, count;
+	register void *addr, *base, *initfunc;
+
+	__asm__ __volatile__(
+		"   li.d %[addr], 0x8000000000000000\n"
+		"1: cacop 0x8, %[addr], 0           \n" /* flush L1 ICache */
+		"   cacop 0x8, %[addr], 1           \n"
+		"   cacop 0x8, %[addr], 2           \n"
+		"   cacop 0x8, %[addr], 3           \n"
+		"   cacop 0x9, %[addr], 0           \n" /* flush L1 DCache */
+		"   cacop 0x9, %[addr], 1           \n"
+		"   cacop 0x9, %[addr], 2           \n"
+		"   cacop 0x9, %[addr], 3           \n"
+		"   addi.w %[sets], %[sets], -1     \n"
+		"   addi.d %[addr], %[addr], 0x40   \n"
+		"   bnez  %[sets], 1b               \n"
+		"   li.d %[addr], 0x8000000000000000\n"
+		"2: cacop 0xa, %[addr], 0           \n" /* flush L1 VCache */
+		"   cacop 0xa, %[addr], 1           \n"
+		"   cacop 0xa, %[addr], 2           \n"
+		"   cacop 0xa, %[addr], 3           \n"
+		"   cacop 0xa, %[addr], 4           \n"
+		"   cacop 0xa, %[addr], 5           \n"
+		"   cacop 0xa, %[addr], 6           \n"
+		"   cacop 0xa, %[addr], 7           \n"
+		"   cacop 0xa, %[addr], 8           \n"
+		"   cacop 0xa, %[addr], 9           \n"
+		"   cacop 0xa, %[addr], 10          \n"
+		"   cacop 0xa, %[addr], 11          \n"
+		"   cacop 0xa, %[addr], 12          \n"
+		"   cacop 0xa, %[addr], 13          \n"
+		"   cacop 0xa, %[addr], 14          \n"
+		"   cacop 0xa, %[addr], 15          \n"
+		"   addi.w %[vsets], %[vsets], -1   \n"
+		"   addi.d %[addr], %[addr], 0x40   \n"
+		"   bnez  %[vsets], 2b              \n"
+		"   li.w    %[val], 0x7             \n" /* *state_addr = CPU_DEAD; */
+		"   st.w  %[val], %[state_addr], 0  \n"
+		"   dbar 0                          \n"
+		"   cacop 0x11, %[state_addr], 0    \n" /* flush entry of *state_addr */
+		: [addr] "=&r" (addr), [val] "=&r" (val)
+		: [state_addr] "r" (state_addr),
+		  [sets] "r" (cpu_data[smp_processor_id()].dcache.sets),
+		  [vsets] "r" (cpu_data[smp_processor_id()].vcache.sets));
+
+	__asm__ __volatile__(
+		"   csrrd  %[cpuid], 0x20              \n"
+		"   andi   %[cpuid], %[cpuid], 0x1ff   \n"
+		"   li.d    %[base], 0x800000001fe01000\n"
+		"   andi   %[core], %[cpuid], 0x3      \n"
+		"   slli.w %[core], %[core], 8         \n" /* get core id */
+		"   or     %[base], %[base], %[core]   \n"
+		"   andi   %[node], %[cpuid], 0xc      \n"
+		"   slli.d %[node], %[node], 42        \n" /* get node id */
+		"   or     %[base], %[base], %[node]   \n"
+		"1: li.w     %[count], 0x100           \n" /* wait for init loop */
+		"2: addi.w %[count], %[count], -1      \n"
+		"   bnez   %[count], 2b                \n" /* limit mailbox access */
+		"   ld.w   %[initfunc], %[base], 0x20  \n" /* check PC */
+		"   beqz   %[initfunc], 1b             \n"
+		"   ld.d   $a1, %[base], 0x38          \n"
+		"   ld.d   $tp, %[base], 0x30          \n" /* get TP via mailbox */
+		"   ld.d   $sp, %[base], 0x28          \n" /* get SP via mailbox */
+		"   ld.d   %[initfunc], %[base], 0x20  \n" /* get PC via mailbox */
+		"   jirl   $zero, %[initfunc], 0       \n" /* jump to initial PC */
+		"   nop                                \n"
+		: [core] "=&r" (core), [node] "=&r" (node),
+		  [base] "=&r" (base), [cpuid] "=&r" (cpuid),
+		  [count] "=&r" (count), [initfunc] "=&r" (initfunc)
+		: /* No Input */
+		: "a1");
+
+	unreachable();
+}
+
+void play_dead(void)
+{
+	int *state_addr;
+	unsigned int cpu = smp_processor_id();
+	void (*play_dead_uncached)(int *s);
+
+	idle_task_exit();
+	play_dead_uncached = (void *)TO_UNCAC(__pa((unsigned long)loongson3_play_dead));
+	state_addr = &per_cpu(cpu_state, cpu);
+	mb();
+	play_dead_uncached(state_addr);
+}
+
+static int loongson3_disable_clock(unsigned int cpu)
+{
+	uint64_t core_id = cpu_core(&cpu_data[cpu]);
+	uint64_t package_id = cpu_data[cpu].package;
+
+	LOONGSON_FREQCTRL(package_id) &= ~(1 << (core_id * 4 + 3));
+
+	return 0;
+}
+
+static int loongson3_enable_clock(unsigned int cpu)
+{
+	uint64_t core_id = cpu_core(&cpu_data[cpu]);
+	uint64_t package_id = cpu_data[cpu].package;
+
+	LOONGSON_FREQCTRL(package_id) |= 1 << (core_id * 4 + 3);
+
+	return 0;
+}
+
+static int register_loongson3_notifier(void)
+{
+	return cpuhp_setup_state_nocalls(CPUHP_LOONGARCH_SOC_PREPARE,
+					 "loongarch/loongson:prepare",
+					 loongson3_enable_clock,
+					 loongson3_disable_clock);
+}
+early_initcall(register_loongson3_notifier);
+
+#endif
+
+const struct plat_smp_ops loongson3_smp_ops = {
+	.send_ipi_single = loongson3_send_ipi_single,
+	.send_ipi_mask = loongson3_send_ipi_mask,
+	.smp_setup = loongson3_smp_setup,
+	.prepare_cpus = loongson3_prepare_cpus,
+	.boot_secondary = loongson3_boot_secondary,
+	.init_secondary = loongson3_init_secondary,
+	.smp_finish = loongson3_smp_finish,
+#ifdef CONFIG_HOTPLUG_CPU
+	.cpu_disable = loongson3_cpu_disable,
+	.cpu_die = loongson3_cpu_die,
+#endif
+};
+
+/*
+ * Power management
+ */
+#ifdef CONFIG_PM
+
+static int loongson3_ipi_suspend(void)
+{
+	return 0;
+}
+
+static void loongson3_ipi_resume(void)
+{
+	if (cpu_has_csripi)
+		iocsr_writel(0xffffffff, LOONGARCH_IOCSR_IPI_EN);
+	else
+		xconf_writel(0xffffffff, ipi_en_regs[cpu_logical_map(0)]);
+}
+
+static struct syscore_ops loongson3_ipi_syscore_ops = {
+	.resume         = loongson3_ipi_resume,
+	.suspend        = loongson3_ipi_suspend,
+};
+
+/*
+ * Enable boot cpu ipi before enabling nonboot cpus
+ * during syscore_resume.
+ */
+static int __init ipi_pm_init(void)
+{
+	register_syscore_ops(&loongson3_ipi_syscore_ops);
+	return 0;
+}
+
+core_initcall(ipi_pm_init);
+#endif
diff --git a/arch/loongarch/mm/tlbex.S b/arch/loongarch/mm/tlbex.S
index 4ba35d6d7a49..d60c045dbc38 100644
--- a/arch/loongarch/mm/tlbex.S
+++ b/arch/loongarch/mm/tlbex.S
@@ -87,7 +87,14 @@ vmalloc_done_load:
 	slli.d	t0, t0, _PTE_T_LOG2
 	add.d	t1, ra, t0
 
+#ifdef CONFIG_SMP
+smp_pgtable_change_load:
+#endif
+#ifdef CONFIG_SMP
+	ll.d	t0, t1, 0
+#else
 	ld.d	t0, t1, 0
+#endif
 	tlbsrch
 
 	srli.d	ra, t0, _PAGE_PRESENT_SHIFT
@@ -95,7 +102,12 @@ vmalloc_done_load:
 	beq	ra, $r0, nopage_tlb_load
 
 	ori	t0, t0, _PAGE_VALID
+#ifdef CONFIG_SMP
+	sc.d	t0, t1, 0
+	beq	t0, $r0, smp_pgtable_change_load
+#else
 	st.d	t0, t1, 0
+#endif
 	ori	t1, t1, 8
 	xori	t1, t1, 8
 	ld.d	t0, t1, 0
@@ -119,14 +131,24 @@ vmalloc_load:
 	 * spots a huge page.
 	 */
 tlb_huge_update_load:
+#ifdef CONFIG_SMP
+	ll.d	t0, t1, 0
+#else
 	ld.d	t0, t1, 0
+#endif
 	srli.d	ra, t0, _PAGE_PRESENT_SHIFT
 	andi	ra, ra, 1
 	beq	ra, $r0, nopage_tlb_load
 	tlbsrch
 
 	ori	t0, t0, _PAGE_VALID
+#ifdef CONFIG_SMP
+	sc.d	t0, t1, 0
+	beq	t0, $r0, tlb_huge_update_load
+	ld.d	t0, t1, 0
+#else
 	st.d	t0, t1, 0
+#endif
 	addu16i.d	t1, $r0, -(CSR_TLBIDX_EHINV >> 16)
 	addi.d	ra, t1, 0
 	csrxchg	ra, t1, LOONGARCH_CSR_TLBIDX
@@ -172,6 +194,7 @@ tlb_huge_update_load:
 	csrxchg		t1, t0, LOONGARCH_CSR_TLBIDX
 
 nopage_tlb_load:
+	dbar	0
 	csrrd	ra, EXCEPTION_KS2
 	la.abs	t0, tlb_do_page_fault_0
 	jirl	$r0, t0, 0
@@ -228,7 +251,14 @@ vmalloc_done_store:
 	slli.d	t0, t0, _PTE_T_LOG2
 	add.d	t1, ra, t0
 
+#ifdef CONFIG_SMP
+smp_pgtable_change_store:
+#endif
+#ifdef CONFIG_SMP
+	ll.d	t0, t1, 0
+#else
 	ld.d	t0, t1, 0
+#endif
 	tlbsrch
 
 	srli.d	ra, t0, _PAGE_PRESENT_SHIFT
@@ -237,7 +267,12 @@ vmalloc_done_store:
 	bne	ra, $r0, nopage_tlb_store
 
 	ori	t0, t0, (_PAGE_VALID | _PAGE_DIRTY)
+#ifdef CONFIG_SMP
+	sc.d	t0, t1, 0
+	beq	t0, $r0, smp_pgtable_change_store
+#else
 	st.d	t0, t1, 0
+#endif
 
 	ori	t1, t1, 8
 	xori	t1, t1, 8
@@ -262,7 +297,11 @@ vmalloc_store:
 	 * spots a huge page.
 	 */
 tlb_huge_update_store:
+#ifdef CONFIG_SMP
+	ll.d	t0, t1, 0
+#else
 	ld.d	t0, t1, 0
+#endif
 	srli.d	ra, t0, _PAGE_PRESENT_SHIFT
 	andi	ra, ra, ((_PAGE_PRESENT | _PAGE_WRITE) >> _PAGE_PRESENT_SHIFT)
 	xori	ra, ra, ((_PAGE_PRESENT | _PAGE_WRITE) >> _PAGE_PRESENT_SHIFT)
@@ -271,7 +310,13 @@ tlb_huge_update_store:
 	tlbsrch
 	ori	t0, t0, (_PAGE_VALID | _PAGE_DIRTY)
 
+#ifdef CONFIG_SMP
+	sc.d	t0, t1, 0
+	beq	t0, $r0, tlb_huge_update_store
+	ld.d	t0, t1, 0
+#else
 	st.d	t0, t1, 0
+#endif
 	addu16i.d	t1, $r0, -(CSR_TLBIDX_EHINV >> 16)
 	addi.d	ra, t1, 0
 	csrxchg	ra, t1, LOONGARCH_CSR_TLBIDX
@@ -317,6 +362,7 @@ tlb_huge_update_store:
 	csrxchg		t1, t0, LOONGARCH_CSR_TLBIDX
 
 nopage_tlb_store:
+	dbar	0
 	csrrd	ra, EXCEPTION_KS2
 	la.abs	t0, tlb_do_page_fault_1
 	jirl	$r0, t0, 0
@@ -372,7 +418,14 @@ vmalloc_done_modify:
 	slli.d	t0, t0, _PTE_T_LOG2
 	add.d	t1, ra, t0
 
+#ifdef CONFIG_SMP
+smp_pgtable_change_modify:
+#endif
+#ifdef CONFIG_SMP
+	ll.d	t0, t1, 0
+#else
 	ld.d	t0, t1, 0
+#endif
 	tlbsrch
 
 	srli.d	ra, t0, _PAGE_WRITE_SHIFT
@@ -380,7 +433,12 @@ vmalloc_done_modify:
 	beq	ra, $r0, nopage_tlb_modify
 
 	ori	t0, t0, (_PAGE_VALID | _PAGE_DIRTY)
+#ifdef CONFIG_SMP
+	sc.d	t0, t1, 0
+	beq	t0, $r0, smp_pgtable_change_modify
+#else
 	st.d	t0, t1, 0
+#endif
 	ori	t1, t1, 8
 	xori	t1, t1, 8
 	ld.d	t0, t1, 0
@@ -404,7 +462,11 @@ vmalloc_modify:
 	 * build_tlbchange_handler_head spots a huge page.
 	 */
 tlb_huge_update_modify:
+#ifdef CONFIG_SMP
+	ll.d	t0, t1, 0
+#else
 	ld.d	t0, t1, 0
+#endif
 
 	srli.d	ra, t0, _PAGE_WRITE_SHIFT
 	andi	ra, ra, 1
@@ -413,7 +475,13 @@ tlb_huge_update_modify:
 	tlbsrch
 	ori	t0, t0, (_PAGE_VALID | _PAGE_DIRTY)
 
+#ifdef CONFIG_SMP
+	sc.d	t0, t1, 0
+	beq	t0, $r0, tlb_huge_update_modify
+	ld.d	t0, t1, 0
+#else
 	st.d	t0, t1, 0
+#endif
 	/*
 	 * A huge PTE describes an area the size of the
 	 * configured huge page size. This is twice the
@@ -453,6 +521,7 @@ tlb_huge_update_modify:
 	csrxchg	t1, t0, LOONGARCH_CSR_TLBIDX
 
 nopage_tlb_modify:
+	dbar	0
 	csrrd	ra, EXCEPTION_KS2
 	la.abs	t0, tlb_do_page_fault_1
 	jirl	$r0, t0, 0
diff --git a/include/linux/cpuhotplug.h b/include/linux/cpuhotplug.h
index 45244c1ded6a..185127748c94 100644
--- a/include/linux/cpuhotplug.h
+++ b/include/linux/cpuhotplug.h
@@ -90,6 +90,7 @@ enum cpuhp_state {
 	CPUHP_ZCOMP_PREPARE,
 	CPUHP_TIMERS_PREPARE,
 	CPUHP_MIPS_SOC_PREPARE,
+	CPUHP_LOONGARCH_SOC_PREPARE,
 	CPUHP_BP_PREPARE_DYN,
 	CPUHP_BP_PREPARE_DYN_END		= CPUHP_BP_PREPARE_DYN + 20,
 	CPUHP_BRINGUP_CPU,
-- 
2.27.0


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

* [PATCH 18/19] LoongArch: Add Non-Uniform Memory Access (NUMA) support
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (15 preceding siblings ...)
  2021-07-06  4:18 ` [PATCH 17/19] LoongArch: Add multi-processor (SMP) support Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06 10:18   ` Arnd Bergmann
  2021-07-06  4:18 ` [PATCH 19/19] LoongArch: Add Loongson-3 default config file Huacai Chen
                   ` (3 subsequent siblings)
  20 siblings, 1 reply; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

This patch adds Non-Uniform Memory Access (NUMA) support for LoongArch.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 arch/loongarch/Kconfig                        |  43 ++
 arch/loongarch/include/asm/bootinfo.h         |  12 +
 .../include/asm/mach-loongson64/topology.h    |  26 +
 arch/loongarch/include/asm/mmzone.h           |  18 +
 arch/loongarch/include/asm/numa.h             |  69 +++
 arch/loongarch/include/asm/pgtable.h          |  12 +
 arch/loongarch/include/asm/topology.h         |   1 +
 arch/loongarch/kernel/acpi.c                  |  96 ++++
 arch/loongarch/kernel/module.c                |   1 +
 arch/loongarch/kernel/setup.c                 |   5 +-
 arch/loongarch/kernel/smp.c                   |  29 +-
 arch/loongarch/kernel/topology.c              |   5 +
 arch/loongarch/loongson64/Makefile            |   4 +-
 arch/loongarch/loongson64/dma.c               |  59 +++
 arch/loongarch/loongson64/init.c              |   4 +
 arch/loongarch/loongson64/irq.c               |   1 +
 arch/loongarch/loongson64/numa.c              | 488 ++++++++++++++++++
 arch/loongarch/loongson64/smp.c               |   6 +
 arch/loongarch/mm/init.c                      |  30 ++
 arch/loongarch/pci/acpi.c                     |   5 +-
 arch/loongarch/pci/pci-loongson.c             |  41 +-
 21 files changed, 945 insertions(+), 10 deletions(-)
 create mode 100644 arch/loongarch/include/asm/mach-loongson64/topology.h
 create mode 100644 arch/loongarch/include/asm/mmzone.h
 create mode 100644 arch/loongarch/include/asm/numa.h
 create mode 100644 arch/loongarch/loongson64/dma.c
 create mode 100644 arch/loongarch/loongson64/numa.c

diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
index 74beb370d943..4ecc98b16fee 100644
--- a/arch/loongarch/Kconfig
+++ b/arch/loongarch/Kconfig
@@ -37,6 +37,7 @@ config LOONGARCH
 	select ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE if !PREEMPTION
 	select ARCH_SUPPORTS_ACPI
 	select ARCH_SUPPORTS_HUGETLBFS
+	select ARCH_SUPPORTS_NUMA_BALANCING
 	select ARCH_USE_BUILTIN_BSWAP
 	select ARCH_USE_CMPXCHG_LOCKREF if 64BIT
 	select ARCH_USE_QUEUED_RWLOCKS
@@ -120,6 +121,7 @@ config MACH_LOONGSON64
 	select SYS_HAS_CPU_LOONGSON64
 	select SYS_SUPPORTS_SMP
 	select SYS_SUPPORTS_HOTPLUG_CPU
+	select SYS_SUPPORTS_NUMA
 	select SYS_SUPPORTS_64BIT_KERNEL
 	select ZONE_DMA32
 	help
@@ -165,6 +167,7 @@ choice
 config CPU_LOONGSON64
 	bool "Loongson 64-bit CPU"
 	depends on SYS_HAS_CPU_LOONGSON64
+	select ARCH_HAS_PHYS_TO_DMA
 	select CPU_SUPPORTS_64BIT_KERNEL
 	select GPIOLIB
 	select SWIOTLB
@@ -292,6 +295,7 @@ config ARCH_SELECT_MEMORY_MODEL
 
 config ARCH_FLATMEM_ENABLE
 	def_bool y
+	depends on !NUMA
 
 config ARCH_SPARSEMEM_ENABLE
 	def_bool y
@@ -302,6 +306,41 @@ config ARCH_SPARSEMEM_ENABLE
 	  or have huge holes in the physical address space for other reasons.
 	  See <file:Documentation/vm/numa.rst> for more.
 
+config NUMA
+	bool "NUMA Support"
+	depends on SYS_SUPPORTS_NUMA
+	select ACPI_NUMA if ACPI
+	help
+	  Say Y to compile the kernel to support NUMA (Non-Uniform Memory
+	  Access).  This option improves performance on systems with more
+	  than two nodes; on two node systems it is generally better to
+	  leave it disabled; on single node systems disable this option
+	  disabled.
+
+config SYS_SUPPORTS_NUMA
+	bool
+
+config NODES_SHIFT
+	int
+	default "6"
+	depends on NUMA
+
+config USE_PERCPU_NUMA_NODE_ID
+	def_bool y
+	depends on NUMA
+
+config HAVE_SETUP_PER_CPU_AREA
+	def_bool y
+	depends on NUMA
+
+config NEED_PER_CPU_EMBED_FIRST_CHUNK
+	def_bool y
+	depends on NUMA
+
+config NEED_PER_CPU_PAGE_FIRST_CHUNK
+	def_bool y
+	depends on NUMA
+
 config DMI
 	bool "Enable DMI scanning"
 	select DMI_SCAN_MACHINE_NON_EFI_FALLBACK
@@ -431,6 +470,10 @@ config ARCH_MEMORY_PROBE
 	def_bool y
 	depends on MEMORY_HOTPLUG
 
+config HAVE_ARCH_NODEDATA_EXTENSION
+	def_bool y
+	depends on NUMA && ARCH_ENABLE_MEMORY_HOTPLUG
+
 config LOCKDEP_SUPPORT
 	bool
 	default y
diff --git a/arch/loongarch/include/asm/bootinfo.h b/arch/loongarch/include/asm/bootinfo.h
index aa9915a56f66..a6a3dbc03b8c 100644
--- a/arch/loongarch/include/asm/bootinfo.h
+++ b/arch/loongarch/include/asm/bootinfo.h
@@ -35,4 +35,16 @@ extern unsigned long initrd_start, initrd_end;
  */
 extern void plat_mem_setup(void);
 
+#ifdef CONFIG_SWIOTLB
+/*
+ * Optional platform hook to call swiotlb_setup().
+ */
+extern void plat_swiotlb_setup(void);
+
+#else
+
+static inline void plat_swiotlb_setup(void) {}
+
+#endif /* CONFIG_SWIOTLB */
+
 #endif /* _ASM_BOOTINFO_H */
diff --git a/arch/loongarch/include/asm/mach-loongson64/topology.h b/arch/loongarch/include/asm/mach-loongson64/topology.h
new file mode 100644
index 000000000000..2bfad6a6ce30
--- /dev/null
+++ b/arch/loongarch/include/asm/mach-loongson64/topology.h
@@ -0,0 +1,26 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _ASM_MACH_TOPOLOGY_H
+#define _ASM_MACH_TOPOLOGY_H
+
+#ifdef CONFIG_NUMA
+
+extern cpumask_t cpus_on_node[];
+
+#define cpumask_of_node(node)  (&cpus_on_node[node])
+
+struct pci_bus;
+extern int pcibus_to_node(struct pci_bus *);
+
+#define cpumask_of_pcibus(bus)	(cpu_online_mask)
+
+extern unsigned char __node_distances[MAX_NUMNODES][MAX_NUMNODES];
+
+void numa_set_distance(int from, int to, int distance);
+
+#define node_distance(from, to)	(__node_distances[(from)][(to)])
+
+#else
+#define pcibus_to_node(bus)	0
+#endif
+
+#endif /* _ASM_MACH_TOPOLOGY_H */
diff --git a/arch/loongarch/include/asm/mmzone.h b/arch/loongarch/include/asm/mmzone.h
new file mode 100644
index 000000000000..772c3baa5bb9
--- /dev/null
+++ b/arch/loongarch/include/asm/mmzone.h
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Author: Huacai Chen (chenhuacai@loongson.cn)
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#ifndef _ASM_MMZONE_H_
+#define _ASM_MMZONE_H_
+
+#include <asm/page.h>
+#include <asm/numa.h>
+
+extern struct pglist_data *node_data[];
+
+#define NODE_DATA(nid)	(node_data[(nid)])
+
+extern void setup_zero_pages(void);
+
+#endif /* _ASM_MMZONE_H_ */
diff --git a/arch/loongarch/include/asm/numa.h b/arch/loongarch/include/asm/numa.h
new file mode 100644
index 000000000000..79a7ed4bdaba
--- /dev/null
+++ b/arch/loongarch/include/asm/numa.h
@@ -0,0 +1,69 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * LoongArch specific ACPICA environments and implementation
+ *
+ * Author: Jianmin Lv <lvjianmin@loongson.cn>
+ *         Huacai Chen <chenhuacai@loongson.cn>
+ *
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+
+#ifndef _ASM_LOONGARCH_NUMA_H
+#define _ASM_LOONGARCH_NUMA_H
+
+#include <linux/nodemask.h>
+
+#define NODE_ADDRSPACE_SHIFT 44
+
+#define pa_to_nid(addr)		(((addr) & 0xf00000000000) >> NODE_ADDRSPACE_SHIFT)
+#define nid_to_addrbase(nid)	(_ULCAST_(nid) << NODE_ADDRSPACE_SHIFT)
+
+#ifdef CONFIG_NUMA
+
+extern int numa_off;
+extern s16 __cpuid_to_node[CONFIG_NR_CPUS];
+extern nodemask_t numa_nodes_parsed __initdata;
+
+struct numa_memblk {
+	u64			start;
+	u64			end;
+	int			nid;
+};
+
+#define NR_NODE_MEMBLKS		(MAX_NUMNODES*2)
+struct numa_meminfo {
+	int			nr_blks;
+	struct numa_memblk	blk[NR_NODE_MEMBLKS];
+};
+
+extern int __init numa_add_memblk(int nodeid, u64 start, u64 end);
+
+extern void __init early_numa_add_cpu(int cpuid, s16 node);
+extern void numa_add_cpu(unsigned int cpu);
+extern void numa_remove_cpu(unsigned int cpu);
+
+static inline void numa_clear_node(int cpu)
+{
+}
+
+static inline void set_cpuid_to_node(int cpuid, s16 node)
+{
+	__cpuid_to_node[cpuid] = node;
+}
+
+extern int early_cpu_to_node(int cpu);
+
+#else
+
+static inline void early_numa_add_cpu(int cpuid, s16 node)	{ }
+static inline void numa_add_cpu(unsigned int cpu)		{ }
+static inline void numa_remove_cpu(unsigned int cpu)		{ }
+
+static inline int early_cpu_to_node(int cpu)
+{
+	return 0;
+}
+
+#endif	/* CONFIG_NUMA */
+
+#endif	/* _ASM_LOONGARCH_NUMA_H */
diff --git a/arch/loongarch/include/asm/pgtable.h b/arch/loongarch/include/asm/pgtable.h
index 0458684c3d24..bc9d1f114a10 100644
--- a/arch/loongarch/include/asm/pgtable.h
+++ b/arch/loongarch/include/asm/pgtable.h
@@ -433,6 +433,18 @@ static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm,
 
 #endif /* CONFIG_TRANSPARENT_HUGEPAGE */
 
+#ifdef CONFIG_NUMA_BALANCING
+static inline long pte_protnone(pte_t pte)
+{
+	return (pte_val(pte) & _PAGE_PROTNONE);
+}
+
+static inline long pmd_protnone(pmd_t pmd)
+{
+	return (pmd_val(pmd) & _PAGE_PROTNONE);
+}
+#endif /* CONFIG_NUMA_BALANCING */
+
 /*
  * We provide our own get_unmapped area to cope with the virtual aliasing
  * constraints placed on us by the cache architecture.
diff --git a/arch/loongarch/include/asm/topology.h b/arch/loongarch/include/asm/topology.h
index bc541a723f8a..1bb5aacf4801 100644
--- a/arch/loongarch/include/asm/topology.h
+++ b/arch/loongarch/include/asm/topology.h
@@ -5,6 +5,7 @@
 #ifndef __ASM_TOPOLOGY_H
 #define __ASM_TOPOLOGY_H
 
+#include <topology.h>
 #include <linux/smp.h>
 
 #ifdef CONFIG_SMP
diff --git a/arch/loongarch/kernel/acpi.c b/arch/loongarch/kernel/acpi.c
index 3c300e53d724..ee334b60e652 100644
--- a/arch/loongarch/kernel/acpi.c
+++ b/arch/loongarch/kernel/acpi.c
@@ -13,6 +13,7 @@
 #include <linux/irqdomain.h>
 #include <linux/memblock.h>
 #include <asm/io.h>
+#include <asm/numa.h>
 #include <loongson.h>
 
 int acpi_disabled;
@@ -349,6 +350,80 @@ int __init acpi_boot_init(void)
 	return 0;
 }
 
+#ifdef CONFIG_ACPI_NUMA
+
+static __init int setup_node(int pxm)
+{
+	return acpi_map_pxm_to_node(pxm);
+}
+
+/*
+ * Callback for SLIT parsing.  pxm_to_node() returns NUMA_NO_NODE for
+ * I/O localities since SRAT does not list them.  I/O localities are
+ * not supported at this point.
+ */
+extern unsigned char __node_distances[MAX_NUMNODES][MAX_NUMNODES];
+unsigned int numa_distance_cnt;
+
+static inline unsigned int get_numa_distances_cnt(struct acpi_table_slit *slit)
+{
+	return slit->locality_count;
+}
+
+void __init numa_set_distance(int from, int to, int distance)
+{
+	if ((u8)distance != distance || (from == to && distance != LOCAL_DISTANCE)) {
+		pr_warn_once("Warning: invalid distance parameter, from=%d to=%d distance=%d\n",
+				from, to, distance);
+		return;
+	}
+
+	__node_distances[from][to] = distance;
+}
+
+/* Callback for Proximity Domain -> CPUID mapping */
+void __init
+acpi_numa_processor_affinity_init(struct acpi_srat_cpu_affinity *pa)
+{
+	int pxm, node;
+
+	if (srat_disabled())
+		return;
+	if (pa->header.length != sizeof(struct acpi_srat_cpu_affinity)) {
+		bad_srat();
+		return;
+	}
+	if ((pa->flags & ACPI_SRAT_CPU_ENABLED) == 0)
+		return;
+	pxm = pa->proximity_domain_lo;
+	if (acpi_srat_revision >= 2) {
+		pxm |= (pa->proximity_domain_hi[0] << 8);
+		pxm |= (pa->proximity_domain_hi[1] << 16);
+		pxm |= (pa->proximity_domain_hi[2] << 24);
+	}
+	node = setup_node(pxm);
+	if (node < 0) {
+		pr_err("SRAT: Too many proximity domains %x\n", pxm);
+		bad_srat();
+		return;
+	}
+
+	if (pa->apic_id >= CONFIG_NR_CPUS) {
+		pr_info("SRAT: PXM %u -> CPU 0x%02x -> Node %u skipped apicid that is too big\n",
+				pxm, pa->apic_id, node);
+		return;
+	}
+
+	early_numa_add_cpu(pa->apic_id, node);
+
+	set_cpuid_to_node(pa->apic_id, node);
+	node_set(node, numa_nodes_parsed);
+	pr_info("SRAT: PXM %u -> CPU 0x%02x -> Node %u\n", pxm, pa->apic_id, node);
+}
+
+void __init acpi_numa_arch_fixup(void) {}
+#endif
+
 void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
 {
 	u8 map_count = loongson_mem_map->map_count;
@@ -363,6 +438,22 @@ void __init arch_reserve_mem_area(acpi_physical_address addr, size_t size)
 
 #include <acpi/processor.h>
 
+static int __ref acpi_map_cpu2node(acpi_handle handle, int cpu, int physid)
+{
+#ifdef CONFIG_ACPI_NUMA
+	int nid;
+
+	nid = acpi_get_node(handle);
+	if (nid != NUMA_NO_NODE) {
+		set_cpuid_to_node(physid, nid);
+		node_set(nid, numa_nodes_parsed);
+		set_cpu_numa_node(cpu, nid);
+		cpumask_set_cpu(cpu, cpumask_of_node(nid));
+	}
+#endif
+	return 0;
+}
+
 int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id, int *pcpu)
 {
 	int cpu;
@@ -373,6 +464,8 @@ int acpi_map_cpu(acpi_handle handle, phys_cpuid_t physid, u32 acpi_id, int *pcpu
 		return cpu;
 	}
 
+	acpi_map_cpu2node(handle, cpu, physid);
+
 	*pcpu = cpu;
 
 	return 0;
@@ -381,6 +474,9 @@ EXPORT_SYMBOL(acpi_map_cpu);
 
 int acpi_unmap_cpu(int cpu)
 {
+#ifdef CONFIG_ACPI_NUMA
+	set_cpuid_to_node(cpu_logical_map(cpu), NUMA_NO_NODE);
+#endif
 	set_cpu_present(cpu, false);
 	num_processors--;
 
diff --git a/arch/loongarch/kernel/module.c b/arch/loongarch/kernel/module.c
index af7c403b032b..cb87486cacb6 100644
--- a/arch/loongarch/kernel/module.c
+++ b/arch/loongarch/kernel/module.c
@@ -11,6 +11,7 @@
 #include <linux/moduleloader.h>
 #include <linux/elf.h>
 #include <linux/mm.h>
+#include <linux/numa.h>
 #include <linux/vmalloc.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
diff --git a/arch/loongarch/kernel/setup.c b/arch/loongarch/kernel/setup.c
index 0ae24254c304..ed5b3680e49e 100644
--- a/arch/loongarch/kernel/setup.c
+++ b/arch/loongarch/kernel/setup.c
@@ -302,8 +302,9 @@ static void __init arch_mem_init(char **cmdline_p)
 
 	check_kernel_sections_mem();
 
+#ifndef CONFIG_NUMA
 	memblock_set_node(0, PHYS_ADDR_MAX, &memblock.memory, 0);
-
+#endif
 	bootmem_init();
 
 	/*
@@ -325,7 +326,7 @@ static void __init arch_mem_init(char **cmdline_p)
 	sparse_init();
 	memblock_set_bottom_up(true);
 
-	swiotlb_init(1);
+	plat_swiotlb_setup();
 
 	dma_contiguous_reserve(PFN_PHYS(max_low_pfn));
 
diff --git a/arch/loongarch/kernel/smp.c b/arch/loongarch/kernel/smp.c
index 7532b0b6f2f8..bb55cc2a0d4a 100644
--- a/arch/loongarch/kernel/smp.c
+++ b/arch/loongarch/kernel/smp.c
@@ -24,6 +24,7 @@
 #include <asm/processor.h>
 #include <asm/idle.h>
 #include <asm/mmu_context.h>
+#include <asm/numa.h>
 #include <asm/time.h>
 #include <asm/setup.h>
 
@@ -217,14 +218,36 @@ void __init smp_prepare_cpus(unsigned int max_cpus)
 /* Preload SMP state for boot cpu */
 void smp_prepare_boot_cpu(void)
 {
-	unsigned int cpu;
+	unsigned int cpu, node, rr_node;
 
 	set_cpu_possible(0, true);
 	set_cpu_online(0, true);
 	set_my_cpu_offset(per_cpu_offset(0));
 
-	for_each_possible_cpu(cpu)
-		set_cpu_numa_node(cpu, 0);
+	rr_node = first_node(node_online_map);
+	for_each_possible_cpu(cpu) {
+		node = early_cpu_to_node(cpu);
+
+		/*
+		 * The mapping between present cpus and nodes has been
+		 * built during MADT and SRAT parsing.
+		 *
+		 * If possible cpus = present cpus here, early_cpu_to_node
+		 * will return valid node.
+		 *
+		 * If possible cpus > present cpus here (e.g. some possible
+		 * cpus will be added by cpu-hotplug later), for possible but
+		 * not present cpus, early_cpu_to_node will return NUMA_NO_NODE,
+		 * and we just map them to online nodes in round-robin way.
+		 * Once hotplugged, new correct mapping will be built for them.
+		 */
+		if (node != NUMA_NO_NODE)
+			set_cpu_numa_node(cpu, node);
+		else {
+			set_cpu_numa_node(cpu, rr_node);
+			rr_node = next_node_in(rr_node, node_online_map);
+		}
+	}
 }
 
 int __cpu_up(unsigned int cpu, struct task_struct *tidle)
diff --git a/arch/loongarch/kernel/topology.c b/arch/loongarch/kernel/topology.c
index ab1a75c0b5a6..ae5f0e5414a3 100644
--- a/arch/loongarch/kernel/topology.c
+++ b/arch/loongarch/kernel/topology.c
@@ -37,6 +37,11 @@ static int __init topology_init(void)
 {
 	int i, ret;
 
+#ifdef CONFIG_NUMA
+	for_each_online_node(i)
+		register_one_node(i);
+#endif /* CONFIG_NUMA */
+
 	for_each_present_cpu(i) {
 		struct cpu *c = &per_cpu(cpu_devices, i);
 
diff --git a/arch/loongarch/loongson64/Makefile b/arch/loongarch/loongson64/Makefile
index b7697cfa2775..48f44abb88ff 100644
--- a/arch/loongarch/loongson64/Makefile
+++ b/arch/loongarch/loongson64/Makefile
@@ -3,8 +3,10 @@
 #
 
 obj-y += setup.o init.o env.o reset.o irq.o mem.o \
-	 rtc.o boardinfo.o
+	 dma.o rtc.o boardinfo.o
 
 obj-$(CONFIG_SMP)	+= smp.o
 
+obj-$(CONFIG_NUMA)	+= numa.o
+
 obj-$(CONFIG_PCI_MSI)	+= msi.o
diff --git a/arch/loongarch/loongson64/dma.c b/arch/loongarch/loongson64/dma.c
new file mode 100644
index 000000000000..f259f70c75fa
--- /dev/null
+++ b/arch/loongarch/loongson64/dma.c
@@ -0,0 +1,59 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
+ */
+#include <linux/init.h>
+#include <linux/dma-direct.h>
+#include <linux/dma-mapping.h>
+#include <linux/dma-map-ops.h>
+#include <linux/swiotlb.h>
+
+#include <asm/bootinfo.h>
+#include <asm/dma.h>
+#include <loongson.h>
+
+/*
+ * We extract 4bit node id (bit 44~47) from Loongson-3's
+ * 48bit physical address space and embed it into 40bit.
+ */
+
+static int node_id_offset;
+
+static dma_addr_t loongson_phys_to_dma(struct device *dev, phys_addr_t paddr)
+{
+	long nid = (paddr >> 44) & 0xf;
+
+	return ((nid << 44) ^ paddr) | (nid << node_id_offset);
+}
+
+static phys_addr_t loongson_dma_to_phys(struct device *dev, dma_addr_t daddr)
+{
+	long nid = (daddr >> node_id_offset) & 0xf;
+
+	return ((nid << node_id_offset) ^ daddr) | (nid << 44);
+}
+
+struct loongson_addr_xlate_ops {
+	dma_addr_t (*phys_to_dma)(struct device *dev, phys_addr_t paddr);
+	phys_addr_t (*dma_to_phys)(struct device *dev, dma_addr_t daddr);
+};
+struct loongson_addr_xlate_ops xlate_ops;
+
+dma_addr_t phys_to_dma(struct device *dev, phys_addr_t paddr)
+{
+	return xlate_ops.phys_to_dma(dev, paddr);
+}
+
+phys_addr_t dma_to_phys(struct device *dev, dma_addr_t daddr)
+{
+	return xlate_ops.dma_to_phys(dev, daddr);
+}
+
+void __init plat_swiotlb_setup(void)
+{
+	swiotlb_init(1);
+	node_id_offset = ((readl(LS7A_DMA_CFG) & LS7A_DMA_NODE_MASK) >> LS7A_DMA_NODE_SHF) + 36;
+
+	xlate_ops.phys_to_dma = loongson_phys_to_dma;
+	xlate_ops.dma_to_phys = loongson_dma_to_phys;
+}
diff --git a/arch/loongarch/loongson64/init.c b/arch/loongarch/loongson64/init.c
index 40aa010c4976..e0f528e69d97 100644
--- a/arch/loongarch/loongson64/init.c
+++ b/arch/loongarch/loongson64/init.c
@@ -130,7 +130,11 @@ void __init platform_init(void)
 #endif
 	loongarch_pci_ops = &ls7a_pci_ops;
 
+#ifndef CONFIG_NUMA
 	fw_init_memory();
+#else
+	fw_init_numa_memory();
+#endif
 	dmi_setup();
 	smbios_parse();
 	pr_info("The BIOS Version: %s\n", b_info.bios_version);
diff --git a/arch/loongarch/loongson64/irq.c b/arch/loongarch/loongson64/irq.c
index a1e0bdebdf30..997c2dfed898 100644
--- a/arch/loongarch/loongson64/irq.c
+++ b/arch/loongarch/loongson64/irq.c
@@ -12,6 +12,7 @@
 #include <linux/module.h>
 #include <linux/stddef.h>
 #include <asm/irq.h>
+#include <asm/numa.h>
 #include <asm/setup.h>
 #include <asm/loongarchregs.h>
 #include <loongson.h>
diff --git a/arch/loongarch/loongson64/numa.c b/arch/loongarch/loongson64/numa.c
new file mode 100644
index 000000000000..6009a2946e24
--- /dev/null
+++ b/arch/loongarch/loongson64/numa.c
@@ -0,0 +1,488 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2020 Loongson Technology Co., Ltd.
+ *
+ * Author:  Xiang Gao, gaoxiang@loongson.cn
+ *          Huacai Chen, chenhuacai@loongson.cn
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/mm.h>
+#include <linux/mmzone.h>
+#include <linux/export.h>
+#include <linux/nodemask.h>
+#include <linux/swap.h>
+#include <linux/memblock.h>
+#include <linux/pfn.h>
+#include <linux/acpi.h>
+#include <linux/highmem.h>
+#include <linux/pci.h>
+#include <asm/page.h>
+#include <asm/pgalloc.h>
+#include <asm/sections.h>
+#include <linux/irq.h>
+#include <asm/bootinfo.h>
+#include <asm/time.h>
+#include <boot_param.h>
+#include <loongson.h>
+#include <asm/numa.h>
+
+int numa_off;
+struct pglist_data *node_data[MAX_NUMNODES];
+unsigned char __node_distances[MAX_NUMNODES][MAX_NUMNODES];
+
+EXPORT_SYMBOL(node_data);
+EXPORT_SYMBOL(__node_distances);
+
+static struct numa_meminfo numa_meminfo;
+cpumask_t cpus_on_node[MAX_NUMNODES];
+cpumask_t phys_cpus_on_node[MAX_NUMNODES];
+EXPORT_SYMBOL(cpus_on_node);
+
+/*
+ * apicid, cpu, node mappings
+ */
+s16 __cpuid_to_node[CONFIG_NR_CPUS] = {
+	[0 ... CONFIG_NR_CPUS - 1] = NUMA_NO_NODE
+};
+EXPORT_SYMBOL(__cpuid_to_node);
+
+nodemask_t numa_nodes_parsed __initdata;
+
+#ifdef CONFIG_HAVE_SETUP_PER_CPU_AREA
+unsigned long __per_cpu_offset[NR_CPUS] __read_mostly;
+EXPORT_SYMBOL(__per_cpu_offset);
+
+static int __init pcpu_cpu_distance(unsigned int from, unsigned int to)
+{
+	if (early_cpu_to_node(from) == early_cpu_to_node(to))
+		return LOCAL_DISTANCE;
+	else
+		return REMOTE_DISTANCE;
+}
+
+static void * __init pcpu_fc_alloc(unsigned int cpu, size_t size,
+				       size_t align)
+{
+	return memblock_alloc_try_nid(size, align, __pa(MAX_DMA_ADDRESS),
+				      MEMBLOCK_ALLOC_ACCESSIBLE, early_cpu_to_node(cpu));
+}
+
+static void __init pcpu_fc_free(void *ptr, size_t size)
+{
+	memblock_free_early(__pa(ptr), size);
+}
+
+static void __init pcpu_populate_pte(unsigned long addr)
+{
+	pgd_t *pgd = pgd_offset_k(addr);
+	p4d_t *p4d = p4d_offset(pgd, addr);
+	pud_t *pud;
+	pmd_t *pmd;
+
+	if (pgd_none(*pgd)) {
+		pud_t *new;
+
+		new = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
+		pgd_populate(&init_mm, pgd, new);
+#ifndef __PAGETABLE_PUD_FOLDED
+		pud_init((unsigned long)new, (unsigned long)invalid_pmd_table);
+#endif
+	}
+
+	pud = pud_offset(p4d, addr);
+	if (pud_none(*pud)) {
+		pmd_t *new;
+
+		new = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
+		pud_populate(&init_mm, pud, new);
+#ifndef __PAGETABLE_PMD_FOLDED
+		pmd_init((unsigned long)new, (unsigned long)invalid_pte_table);
+#endif
+	}
+
+	pmd = pmd_offset(pud, addr);
+	if (!pmd_present(*pmd)) {
+		pte_t *new;
+
+		new = memblock_alloc(PAGE_SIZE, PAGE_SIZE);
+		pmd_populate_kernel(&init_mm, pmd, new);
+	}
+}
+
+void __init setup_per_cpu_areas(void)
+{
+	unsigned long delta;
+	unsigned int cpu;
+	int rc = -EINVAL;
+
+	if (pcpu_chosen_fc == PCPU_FC_AUTO) {
+		if (nr_node_ids >= 8)
+			pcpu_chosen_fc = PCPU_FC_PAGE;
+		else
+			pcpu_chosen_fc = PCPU_FC_EMBED;
+	}
+
+	/*
+	 * Always reserve area for module percpu variables.  That's
+	 * what the legacy allocator did.
+	 */
+	if (pcpu_chosen_fc != PCPU_FC_PAGE) {
+		rc = pcpu_embed_first_chunk(PERCPU_MODULE_RESERVE,
+					    PERCPU_DYNAMIC_RESERVE, PMD_SIZE,
+					    pcpu_cpu_distance,
+					    pcpu_fc_alloc, pcpu_fc_free);
+		if (rc < 0)
+			pr_warn("%s allocator failed (%d), falling back to page size\n",
+				pcpu_fc_names[pcpu_chosen_fc], rc);
+	}
+	if (rc < 0)
+		rc = pcpu_page_first_chunk(PERCPU_MODULE_RESERVE,
+					   pcpu_fc_alloc, pcpu_fc_free,
+					   pcpu_populate_pte);
+	if (rc < 0)
+		panic("cannot initialize percpu area (err=%d)", rc);
+
+	delta = (unsigned long)pcpu_base_addr - (unsigned long)__per_cpu_start;
+	for_each_possible_cpu(cpu)
+		__per_cpu_offset[cpu] = delta + pcpu_unit_offsets[cpu];
+}
+#endif
+
+/*
+ * Get nodeid by logical cpu number.
+ * __cpuid_to_node maps phyical cpu id to node, so we
+ * should use cpu_logical_map(cpu) to index it.
+ *
+ * This routine is only used in early phase during
+ * booting, after setup_per_cpu_areas calling and numa_node
+ * initialization, cpu_to_node will be used instead.
+ */
+int early_cpu_to_node(int cpu)
+{
+	int physid = cpu_logical_map(cpu);
+
+	if (physid < 0)
+		return NUMA_NO_NODE;
+
+	return __cpuid_to_node[physid];
+}
+
+void __init early_numa_add_cpu(int cpuid, s16 node)
+{
+	int cpu = __cpu_number_map[cpuid];
+
+	if (cpu < 0)
+		return;
+
+	cpumask_set_cpu(cpu, &cpus_on_node[node]);
+	cpumask_set_cpu(cpuid, &phys_cpus_on_node[node]);
+}
+
+void numa_add_cpu(unsigned int cpu)
+{
+	int nid = cpu_to_node(cpu);
+	cpumask_set_cpu(cpu, &cpus_on_node[nid]);
+}
+
+void numa_remove_cpu(unsigned int cpu)
+{
+	int nid = cpu_to_node(cpu);
+	cpumask_clear_cpu(cpu, &cpus_on_node[nid]);
+}
+
+static int __init numa_add_memblk_to(int nid, u64 start, u64 end,
+				     struct numa_meminfo *mi)
+{
+	/* ignore zero length blks */
+	if (start == end)
+		return 0;
+
+	/* whine about and ignore invalid blks */
+	if (start > end || nid < 0 || nid >= MAX_NUMNODES) {
+		pr_warn("NUMA: Warning: invalid memblk node %d [mem %#010Lx-%#010Lx]\n",
+			   nid, start, end - 1);
+		return 0;
+	}
+
+	if (mi->nr_blks >= NR_NODE_MEMBLKS) {
+		pr_err("NUMA: too many memblk ranges\n");
+		return -EINVAL;
+	}
+
+	mi->blk[mi->nr_blks].start = PFN_ALIGN(start);
+	mi->blk[mi->nr_blks].end = PFN_ALIGN(end - PAGE_SIZE + 1);
+	mi->blk[mi->nr_blks].nid = nid;
+	mi->nr_blks++;
+	return 0;
+}
+
+/**
+ * numa_add_memblk - Add one numa_memblk to numa_meminfo
+ * @nid: NUMA node ID of the new memblk
+ * @start: Start address of the new memblk
+ * @end: End address of the new memblk
+ *
+ * Add a new memblk to the default numa_meminfo.
+ *
+ * RETURNS:
+ * 0 on success, -errno on failure.
+ */
+int __init numa_add_memblk(int nid, u64 start, u64 end)
+{
+	return numa_add_memblk_to(nid, start, end, &numa_meminfo);
+}
+
+static void __init alloc_node_data(int nid)
+{
+	void *nd;
+	unsigned long nd_pa;
+	size_t nd_sz = roundup(sizeof(pg_data_t), PAGE_SIZE);
+
+	nd_pa = memblock_phys_alloc_try_nid(nd_sz, SMP_CACHE_BYTES, nid);
+	if (!nd_pa) {
+		pr_err("Cannot find %zu Byte for node_data (initial node: %d)\n", nd_sz, nid);
+		return;
+	}
+
+	nd = __va(nd_pa);
+
+	node_data[nid] = nd;
+	memset(nd, 0, sizeof(pg_data_t));
+}
+
+static void __init node_mem_init(unsigned int node)
+{
+	unsigned long start_pfn, end_pfn;
+	unsigned long node_addrspace_offset;
+
+	node_addrspace_offset = nid_to_addrbase(node);
+	pr_info("Node%d's addrspace_offset is 0x%lx\n",
+			node, node_addrspace_offset);
+
+	get_pfn_range_for_nid(node, &start_pfn, &end_pfn);
+	pr_info("Node%d: start_pfn=0x%lx, end_pfn=0x%lx\n",
+		node, start_pfn, end_pfn);
+
+	alloc_node_data(node);
+
+	NODE_DATA(node)->node_start_pfn = start_pfn;
+	NODE_DATA(node)->node_spanned_pages = end_pfn - start_pfn;
+
+	if (node == 0) {
+		/* used by finalize_initrd() */
+		max_low_pfn = end_pfn;
+
+		/* Reserve the first 2MB */
+		memblock_reserve(PHYS_OFFSET, 0x200000);
+
+		/* Reserve the kernel text/data/bss */
+		memblock_reserve(__pa_symbol(&_text),
+				 __pa_symbol(&_end) - __pa_symbol(&_text));
+	}
+}
+
+#ifdef CONFIG_ACPI_NUMA
+
+/*
+ * Sanity check to catch more bad NUMA configurations (they are amazingly
+ * common).  Make sure the nodes cover all memory.
+ */
+static bool __init numa_meminfo_cover_memory(const struct numa_meminfo *mi)
+{
+	int i;
+	u64 numaram, biosram;
+
+	numaram = 0;
+	for (i = 0; i < mi->nr_blks; i++) {
+		u64 s = mi->blk[i].start >> PAGE_SHIFT;
+		u64 e = mi->blk[i].end >> PAGE_SHIFT;
+
+		numaram += e - s;
+		numaram -= __absent_pages_in_range(mi->blk[i].nid, s, e);
+		if ((s64)numaram < 0)
+			numaram = 0;
+	}
+	max_pfn = max_low_pfn;
+	biosram = max_pfn - absent_pages_in_range(0, max_pfn);
+
+	BUG_ON((s64)(biosram - numaram) >= (1 << (20 - PAGE_SHIFT)));
+	return true;
+}
+
+static void __init add_node_intersection(u32 node, u64 start, u64 size)
+{
+	static unsigned long num_physpages;
+
+	num_physpages += (size >> PAGE_SHIFT);
+	pr_info("Node%d: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx Bytes\n",
+		node, ADDRESS_TYPE_SYSRAM, start, size);
+	pr_info("       start_pfn:0x%llx, end_pfn:0x%llx, num_physpages:0x%lx\n",
+		start >> PAGE_SHIFT, (start + size) >> PAGE_SHIFT, num_physpages);
+	memblock_add_node(start, size, node);
+	memblock_set_node(start, size, &memblock.memory, node);
+}
+
+/*
+ * add_mem_region
+ *
+ * Add a uasable memory region described by BIOS. The
+ * routine gets each intersection between BIOS's region
+ * and node's region, and adds them into node's memblock
+ * pool.
+ *
+ */
+static void __init add_numamem_region(u64 start, u64 end)
+{
+	u32 i;
+	u64 tmp = start;
+
+	for (i = 0; i < numa_meminfo.nr_blks; i++) {
+		struct numa_memblk *mb = &numa_meminfo.blk[i];
+
+		if (tmp > mb->end)
+			continue;
+
+		if (end > mb->end) {
+			add_node_intersection(mb->nid, tmp, mb->end - tmp);
+			tmp = mb->end;
+		} else {
+			add_node_intersection(mb->nid, tmp, end - tmp);
+			break;
+		}
+	}
+}
+
+static void __init init_node_memblock(void)
+{
+	u32 i, mem_type;
+	u64 mem_end, mem_start, mem_size;
+
+	/* Parse memory information and activate */
+	for (i = 0; i < loongson_mem_map->map_count; i++) {
+		mem_type = loongson_mem_map->map[i].mem_type;
+		mem_start = loongson_mem_map->map[i].mem_start;
+		mem_size = loongson_mem_map->map[i].mem_size;
+		mem_end = loongson_mem_map->map[i].mem_start + mem_size;
+		switch (mem_type) {
+		case ADDRESS_TYPE_SYSRAM:
+			mem_start = PFN_ALIGN(mem_start);
+			mem_end = PFN_ALIGN(mem_end - PAGE_SIZE + 1);
+			add_numamem_region(mem_start, mem_end);
+			break;
+		case ADDRESS_TYPE_ACPI:
+		case ADDRESS_TYPE_RESERVED:
+			pr_info("Resvd: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx Bytes\n",
+					mem_type, mem_start, mem_size);
+			memblock_reserve(mem_start, mem_size);
+			break;
+		}
+	}
+}
+
+static void __init numa_default_distance(void)
+{
+	int row, col;
+
+	for (row = 0; row < MAX_NUMNODES; row++)
+		for (col = 0; col < MAX_NUMNODES; col++) {
+
+			if (col == row)
+				__node_distances[row][col] = 0;
+			else
+				/* We assume that one node per package here!
+				 *
+				 * A SLIT should be used for multiple nodes per
+				 * package to override default setting.
+				 */
+				__node_distances[row][col] = 200;
+	}
+}
+
+static int __init numa_mem_init(int (*init_func)(void))
+{
+	int i;
+	int ret;
+	int node;
+
+	for (i = 0; i < NR_CPUS; i++)
+		set_cpuid_to_node(i, NUMA_NO_NODE);
+
+	nodes_clear(numa_nodes_parsed);
+	nodes_clear(node_possible_map);
+	nodes_clear(node_online_map);
+	memset(&numa_meminfo, 0, sizeof(numa_meminfo));
+	numa_default_distance();
+
+	/* Parse SRAT and SLIT if provided by firmware. */
+	ret = init_func();
+	if (ret < 0)
+		return ret;
+
+	node_possible_map = numa_nodes_parsed;
+	if (WARN_ON(nodes_empty(node_possible_map)))
+		return -EINVAL;
+
+	init_node_memblock();
+	if (numa_meminfo_cover_memory(&numa_meminfo) == false)
+		return -EINVAL;
+
+	for_each_node_mask(node, node_possible_map) {
+		node_mem_init(node);
+		node_set_online(node);
+	}
+	max_low_pfn = PHYS_PFN(memblock_end_of_DRAM());
+
+	return 0;
+}
+#endif
+void __init paging_init(void)
+{
+	unsigned int node;
+	unsigned long zones_size[MAX_NR_ZONES] = {0, };
+
+	pagetable_init();
+
+	for_each_online_node(node) {
+		unsigned long start_pfn, end_pfn;
+
+		get_pfn_range_for_nid(node, &start_pfn, &end_pfn);
+
+		if (end_pfn > max_low_pfn)
+			max_low_pfn = end_pfn;
+	}
+#ifdef CONFIG_ZONE_DMA32
+	zones_size[ZONE_DMA32] = MAX_DMA32_PFN;
+#endif
+	zones_size[ZONE_NORMAL] = max_low_pfn;
+	free_area_init(zones_size);
+}
+
+void __init mem_init(void)
+{
+	high_memory = (void *) __va(get_num_physpages() << PAGE_SHIFT);
+	memblock_free_all();
+	setup_zero_pages();	/* This comes from node 0 */
+}
+
+int pcibus_to_node(struct pci_bus *bus)
+{
+	struct pci_controller *pc = bus->sysdata;
+
+	return pc->node;
+}
+EXPORT_SYMBOL(pcibus_to_node);
+
+void __init fw_init_numa_memory(void)
+{
+	numa_mem_init(acpi_numa_init);
+	setup_nr_node_ids();
+	loongson_sysconf.nr_nodes = nr_node_ids;
+	loongson_sysconf.cores_per_node = cpumask_weight(&phys_cpus_on_node[0]);
+}
+EXPORT_SYMBOL(fw_init_numa_memory);
diff --git a/arch/loongarch/loongson64/smp.c b/arch/loongarch/loongson64/smp.c
index 8b7d6dcf1315..2487769c7850 100644
--- a/arch/loongarch/loongson64/smp.c
+++ b/arch/loongarch/loongson64/smp.c
@@ -214,6 +214,9 @@ static void loongson3_init_secondary(void)
 	else
 		xconf_writel(0xffffffff, ipi_en_regs[cpu_logical_map(cpu)]);
 
+#ifdef CONFIG_NUMA
+	numa_add_cpu(cpu);
+#endif
 	per_cpu(cpu_state, cpu) = CPU_ONLINE;
 	cpu_set_core(&cpu_data[cpu],
 		     cpu_logical_map(cpu) % loongson_sysconf.cores_per_package);
@@ -314,6 +317,9 @@ static int loongson3_cpu_disable(void)
 	if (cpu == 0)
 		return -EBUSY;
 
+#ifdef CONFIG_NUMA
+	numa_remove_cpu(cpu);
+#endif
 	set_cpu_online(cpu, false);
 	calculate_cpu_foreign_map();
 	local_irq_save(flags);
diff --git a/arch/loongarch/mm/init.c b/arch/loongarch/mm/init.c
index e661017ca23e..774312ffc75e 100644
--- a/arch/loongarch/mm/init.c
+++ b/arch/loongarch/mm/init.c
@@ -93,6 +93,7 @@ void copy_from_user_page(struct vm_area_struct *vma,
 }
 EXPORT_SYMBOL_GPL(copy_from_user_page);
 
+#ifndef CONFIG_NUMA
 void __init paging_init(void)
 {
 	unsigned long max_zone_pfns[MAX_NR_ZONES];
@@ -118,6 +119,7 @@ void __init mem_init(void)
 	memblock_free_all();
 	setup_zero_pages();	/* Setup zeroed pages.  */
 }
+#endif /* !CONFIG_NUMA */
 
 void free_init_pages(const char *what, unsigned long begin, unsigned long end)
 {
@@ -162,6 +164,34 @@ int arch_add_memory(int nid, u64 start, u64 size, struct mhp_params *params)
 	return ret;
 }
 
+#ifdef CONFIG_HAVE_ARCH_NODEDATA_EXTENSION
+pg_data_t *arch_alloc_nodedata(int nid)
+{
+	return kzalloc(sizeof(pg_data_t), GFP_KERNEL);
+}
+
+void arch_free_nodedata(pg_data_t *pgdat)
+{
+	kfree(pgdat);
+}
+
+void arch_refresh_nodedata(int nid, pg_data_t *pgdat)
+{
+	BUG();
+}
+#endif
+
+#ifdef CONFIG_NUMA
+int memory_add_physaddr_to_nid(u64 start)
+{
+	int nid;
+
+	nid = pa_to_nid(start);
+	return nid;
+}
+EXPORT_SYMBOL_GPL(memory_add_physaddr_to_nid);
+#endif
+
 #ifdef CONFIG_MEMORY_HOTREMOVE
 void arch_remove_memory(int nid, u64 start,
 		u64 size, struct vmem_altmap *altmap)
diff --git a/arch/loongarch/pci/acpi.c b/arch/loongarch/pci/acpi.c
index 68e4c3f5e88f..f65f7e787bb9 100644
--- a/arch/loongarch/pci/acpi.c
+++ b/arch/loongarch/pci/acpi.c
@@ -11,6 +11,7 @@
 #include <linux/pci-acpi.h>
 
 #include <asm/pci.h>
+#include <asm/numa.h>
 #include <loongson.h>
 
 struct pci_root_info {
@@ -115,7 +116,7 @@ static void init_controller_resources(struct pci_controller *controller,
 	struct resource_entry *entry, *tmp;
 	struct resource *res;
 
-	controller->io_map_base = loongarch_io_port_base;
+	controller->io_map_base = loongarch_io_port_base | nid_to_addrbase(controller->node);
 
 	resource_list_for_each_entry_safe(entry, tmp, &bus->resources) {
 		res = entry->res;
@@ -155,7 +156,7 @@ struct pci_bus *pci_acpi_scan_root(struct acpi_pci_root *root)
 	if (!controller->mcfg_addr)
 		controller->mcfg_addr = mcfg_addr_init(controller->index);
 
-	controller->node = 0;
+	controller->node = pa_to_nid(controller->mcfg_addr);
 
 	bus = pci_find_bus(domain, busnum);
 	if (bus) {
diff --git a/arch/loongarch/pci/pci-loongson.c b/arch/loongarch/pci/pci-loongson.c
index e74f887a330e..bc1bc2b36293 100644
--- a/arch/loongarch/pci/pci-loongson.c
+++ b/arch/loongarch/pci/pci-loongson.c
@@ -8,6 +8,7 @@
 #include <linux/pci.h>
 #include <linux/vgaarb.h>
 
+#include <asm/numa.h>
 #include <loongson.h>
 
 #define PCI_ACCESS_READ  0
@@ -17,6 +18,7 @@ static int ls7a_pci_config_access(unsigned char access_type,
 		struct pci_bus *bus, unsigned int devfn,
 		int where, u32 *data)
 {
+	int node = pcibus_to_node(bus);
 	unsigned char busnum = bus->number;
 	int device = PCI_SLOT(devfn);
 	int function = PCI_FUNC(devfn);
@@ -29,7 +31,7 @@ static int ls7a_pci_config_access(unsigned char access_type,
 	 * device 2: misc, device 21: confbus
 	 */
 	if (where < PCI_CFG_SPACE_SIZE) { /* standard config */
-		addr = (busnum << 16) | (device << 11) | (function << 8) | reg;
+		addr = nid_to_addrbase(node) | (busnum << 16) | (device << 11) | (function << 8) | reg;
 		if (busnum == 0) {
 			if (device > 23 || (device >= 9 && device <= 20 && function == 1))
 				return PCIBIOS_DEVICE_NOT_FOUND;
@@ -39,7 +41,7 @@ static int ls7a_pci_config_access(unsigned char access_type,
 		}
 	} else if (where < PCI_CFG_SPACE_EXP_SIZE) {  /* extended config */
 		reg = (reg & 0xff) | ((reg & 0xf00) << 16);
-		addr = (busnum << 16) | (device << 11) | (function << 8) | reg;
+		addr = nid_to_addrbase(node) | (busnum << 16) | (device << 11) | (function << 8) | reg;
 		if (busnum == 0) {
 			if (device > 23 || (device >= 9 && device <= 20 && function == 1))
 				return PCIBIOS_DEVICE_NOT_FOUND;
@@ -114,6 +116,41 @@ struct pci_ops ls7a_pci_ops = {
 	.write = ls7a_pci_pcibios_write
 };
 
+static void pci_root_fix_resbase(struct pci_dev *dev)
+{
+	struct resource *res;
+	struct resource_entry *entry, *tmp;
+
+	resource_list_for_each_entry_safe(entry, tmp, &dev->bus->resources) {
+		res = entry->res;
+
+		if (res->flags & IORESOURCE_MEM) {
+			res->start &= GENMASK_ULL(40, 0);
+			res->end   &= GENMASK_ULL(40, 0);
+		}
+	}
+}
+DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_LOONGSON, PCI_DEVICE_ID_LOONGSON_HOST, pci_root_fix_resbase);
+
+static void pci_device_fix_resbase(struct pci_dev *dev)
+{
+	int i, node = pcibus_to_node(dev->bus);
+
+	for (i = 0; i < PCI_NUM_RESOURCES; i++) {
+		struct resource *r = &dev->resource[i];
+
+		if (!node && (r->flags & IORESOURCE_IO))
+			continue;
+
+		if (r->flags & (IORESOURCE_MEM | IORESOURCE_IO)) {
+			r->start |= nid_to_addrbase(node) | HT1LO_OFFSET;
+			r->end   |= nid_to_addrbase(node) | HT1LO_OFFSET;
+		}
+
+	}
+}
+DECLARE_PCI_FIXUP_ENABLE(PCI_ANY_ID, PCI_ANY_ID, pci_device_fix_resbase);
+
 static void pci_fixup_vgadev(struct pci_dev *pdev)
 {
 	struct pci_dev *devp = NULL;
-- 
2.27.0


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

* [PATCH 19/19] LoongArch: Add Loongson-3 default config file
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (16 preceding siblings ...)
  2021-07-06  4:18 ` [PATCH 18/19] LoongArch: Add Non-Uniform Memory Access (NUMA) support Huacai Chen
@ 2021-07-06  4:18 ` Huacai Chen
  2021-07-06 10:18   ` Arnd Bergmann
  2021-07-06 10:11 ` [PATCH 00/19] arch: Add basic LoongArch support Arnd Bergmann
                   ` (2 subsequent siblings)
  20 siblings, 1 reply; 131+ messages in thread
From: Huacai Chen @ 2021-07-06  4:18 UTC (permalink / raw)
  To: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

This patch adds a default config file for LoongArch-based Loongson-3
platform.

Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
---
 arch/loongarch/Makefile                    |   2 +
 arch/loongarch/configs/loongson3_defconfig | 702 +++++++++++++++++++++
 2 files changed, 704 insertions(+)
 create mode 100644 arch/loongarch/configs/loongson3_defconfig

diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile
index d339049e9022..6303bb338cae 100644
--- a/arch/loongarch/Makefile
+++ b/arch/loongarch/Makefile
@@ -3,6 +3,8 @@
 # Author: Huacai Chen <chenhuacai@loongson.cn>
 # Copyright (C) 2020-2021 Loongson Technology Corporation Limited
 
+KBUILD_DEFCONFIG := loongson3_defconfig
+
 #
 # Select the object file format to substitute into the linker script.
 #
diff --git a/arch/loongarch/configs/loongson3_defconfig b/arch/loongarch/configs/loongson3_defconfig
new file mode 100644
index 000000000000..b70403e893c5
--- /dev/null
+++ b/arch/loongarch/configs/loongson3_defconfig
@@ -0,0 +1,702 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_KERNEL_LZMA=y
+CONFIG_SYSVIPC=y
+CONFIG_LOONGARCH=y
+CONFIG_MACH_LOONGSON64=y
+CONFIG_CPU_LOONGSON64=y
+CONFIG_64BIT=y
+CONFIG_DMI=y
+CONFIG_POSIX_MQUEUE=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_BPF_SYSCALL=y
+CONFIG_PREEMPT=y
+CONFIG_BSD_PROCESS_ACCT=y
+CONFIG_BSD_PROCESS_ACCT_V3=y
+CONFIG_TASKSTATS=y
+CONFIG_TASK_DELAY_ACCT=y
+CONFIG_TASK_XACCT=y
+CONFIG_TASK_IO_ACCOUNTING=y
+CONFIG_LOG_BUF_SHIFT=18
+CONFIG_NUMA_BALANCING=y
+CONFIG_MEMCG=y
+CONFIG_BLK_CGROUP=y
+CONFIG_CFS_BANDWIDTH=y
+CONFIG_RT_GROUP_SCHED=y
+CONFIG_CGROUP_PIDS=y
+CONFIG_CGROUP_FREEZER=y
+CONFIG_CGROUP_HUGETLB=y
+CONFIG_CPUSETS=y
+CONFIG_CGROUP_DEVICE=y
+CONFIG_CGROUP_CPUACCT=y
+CONFIG_CGROUP_PERF=y
+CONFIG_CGROUP_BPF=y
+CONFIG_NAMESPACES=y
+CONFIG_USER_NS=y
+CONFIG_CHECKPOINT_RESTORE=y
+CONFIG_SCHED_AUTOGROUP=y
+CONFIG_SYSFS_DEPRECATED=y
+CONFIG_RELAY=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_USERFAULTFD=y
+CONFIG_EMBEDDED=y
+CONFIG_PERF_EVENTS=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_PAGE_SIZE_16KB=y
+CONFIG_NUMA=y
+CONFIG_EFI=y
+CONFIG_SMP=y
+CONFIG_NR_CPUS=64
+CONFIG_HZ_250=y
+CONFIG_ACPI=y
+CONFIG_ACPI_HOTPLUG_CPU=y
+CONFIG_ACPI_HOTPLUG_MEMORY=y
+CONFIG_EFI_CAPSULE_LOADER=m
+CONFIG_EFI_TEST=m
+CONFIG_MODULES=y
+CONFIG_MODULE_FORCE_LOAD=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_MODULE_FORCE_UNLOAD=y
+CONFIG_MODVERSIONS=y
+CONFIG_BLK_DEV_THROTTLING=y
+CONFIG_PARTITION_ADVANCED=y
+CONFIG_IOSCHED_BFQ=y
+CONFIG_BFQ_GROUP_IOSCHED=y
+CONFIG_BINFMT_MISC=m
+CONFIG_MEMORY_HOTPLUG=y
+CONFIG_MEMORY_HOTPLUG_DEFAULT_ONLINE=y
+CONFIG_MEMORY_HOTREMOVE=y
+CONFIG_KSM=y
+CONFIG_TRANSPARENT_HUGEPAGE=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_XFRM_USER=y
+CONFIG_NET_KEY=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_MULTIPATH=y
+CONFIG_IP_ROUTE_VERBOSE=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+CONFIG_IP_PNP_RARP=y
+CONFIG_INET_ESP=m
+CONFIG_INET_UDP_DIAG=y
+CONFIG_TCP_CONG_ADVANCED=y
+CONFIG_TCP_CONG_BBR=m
+CONFIG_NETWORK_PHY_TIMESTAMPING=y
+CONFIG_NETFILTER=y
+CONFIG_NETFILTER_NETLINK_LOG=m
+CONFIG_NF_CONNTRACK=m
+CONFIG_NF_LOG_NETDEV=m
+CONFIG_NF_CONNTRACK_AMANDA=m
+CONFIG_NF_CONNTRACK_FTP=m
+CONFIG_NF_CONNTRACK_TFTP=m
+CONFIG_NF_CT_NETLINK=m
+CONFIG_NF_TABLES=m
+CONFIG_NFT_COUNTER=m
+CONFIG_NFT_CONNLIMIT=m
+CONFIG_NFT_LOG=m
+CONFIG_NFT_LIMIT=m
+CONFIG_NFT_MASQ=m
+CONFIG_NFT_REDIR=m
+CONFIG_NFT_NAT=m
+CONFIG_NFT_TUNNEL=m
+CONFIG_NFT_OBJREF=m
+CONFIG_NFT_QUEUE=m
+CONFIG_NFT_QUOTA=m
+CONFIG_NFT_REJECT=m
+CONFIG_NFT_COMPAT=m
+CONFIG_NFT_HASH=m
+CONFIG_NFT_SOCKET=m
+CONFIG_NFT_OSF=m
+CONFIG_NFT_TPROXY=m
+CONFIG_NETFILTER_XT_SET=m
+CONFIG_NETFILTER_XT_TARGET_AUDIT=m
+CONFIG_NETFILTER_XT_TARGET_CHECKSUM=m
+CONFIG_NETFILTER_XT_TARGET_CLASSIFY=m
+CONFIG_NETFILTER_XT_TARGET_CONNMARK=m
+CONFIG_NETFILTER_XT_TARGET_CT=m
+CONFIG_NETFILTER_XT_TARGET_DSCP=m
+CONFIG_NETFILTER_XT_TARGET_HMARK=m
+CONFIG_NETFILTER_XT_TARGET_IDLETIMER=m
+CONFIG_NETFILTER_XT_TARGET_LED=m
+CONFIG_NETFILTER_XT_TARGET_LOG=m
+CONFIG_NETFILTER_XT_TARGET_MARK=m
+CONFIG_NETFILTER_XT_TARGET_NFQUEUE=m
+CONFIG_NETFILTER_XT_TARGET_TRACE=m
+CONFIG_NETFILTER_XT_TARGET_SECMARK=m
+CONFIG_NETFILTER_XT_TARGET_TCPMSS=m
+CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP=m
+CONFIG_NETFILTER_XT_MATCH_ADDRTYPE=m
+CONFIG_NETFILTER_XT_MATCH_BPF=m
+CONFIG_NETFILTER_XT_MATCH_CGROUP=m
+CONFIG_NETFILTER_XT_MATCH_CLUSTER=m
+CONFIG_NETFILTER_XT_MATCH_COMMENT=m
+CONFIG_NETFILTER_XT_MATCH_CONNBYTES=m
+CONFIG_NETFILTER_XT_MATCH_CONNLABEL=m
+CONFIG_NETFILTER_XT_MATCH_CONNLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_CONNMARK=m
+CONFIG_NETFILTER_XT_MATCH_CONNTRACK=m
+CONFIG_NETFILTER_XT_MATCH_CPU=m
+CONFIG_NETFILTER_XT_MATCH_DCCP=m
+CONFIG_NETFILTER_XT_MATCH_DEVGROUP=m
+CONFIG_NETFILTER_XT_MATCH_DSCP=m
+CONFIG_NETFILTER_XT_MATCH_ESP=m
+CONFIG_NETFILTER_XT_MATCH_HASHLIMIT=m
+CONFIG_NETFILTER_XT_MATCH_HELPER=m
+CONFIG_NETFILTER_XT_MATCH_IPCOMP=m
+CONFIG_NETFILTER_XT_MATCH_IPRANGE=m
+CONFIG_NETFILTER_XT_MATCH_IPVS=m
+CONFIG_NETFILTER_XT_MATCH_LENGTH=m
+CONFIG_NETFILTER_XT_MATCH_LIMIT=m
+CONFIG_NETFILTER_XT_MATCH_MAC=m
+CONFIG_NETFILTER_XT_MATCH_MARK=m
+CONFIG_NETFILTER_XT_MATCH_MULTIPORT=m
+CONFIG_NETFILTER_XT_MATCH_NFACCT=m
+CONFIG_NETFILTER_XT_MATCH_OSF=m
+CONFIG_NETFILTER_XT_MATCH_OWNER=m
+CONFIG_NETFILTER_XT_MATCH_POLICY=m
+CONFIG_NETFILTER_XT_MATCH_PKTTYPE=m
+CONFIG_NETFILTER_XT_MATCH_QUOTA=m
+CONFIG_NETFILTER_XT_MATCH_RATEEST=m
+CONFIG_NETFILTER_XT_MATCH_REALM=m
+CONFIG_NETFILTER_XT_MATCH_SOCKET=m
+CONFIG_NETFILTER_XT_MATCH_STATE=m
+CONFIG_NETFILTER_XT_MATCH_STATISTIC=m
+CONFIG_NETFILTER_XT_MATCH_STRING=m
+CONFIG_NETFILTER_XT_MATCH_TCPMSS=m
+CONFIG_NETFILTER_XT_MATCH_TIME=m
+CONFIG_NETFILTER_XT_MATCH_U32=m
+CONFIG_IP_SET=m
+CONFIG_IP_VS=m
+CONFIG_IP_VS_PROTO_TCP=y
+CONFIG_IP_VS_PROTO_UDP=y
+CONFIG_IP_VS_RR=m
+CONFIG_IP_VS_NFCT=y
+CONFIG_NF_TABLES_IPV4=y
+CONFIG_NFT_DUP_IPV4=m
+CONFIG_NFT_FIB_IPV4=m
+CONFIG_NF_TABLES_ARP=y
+CONFIG_NF_LOG_ARP=m
+CONFIG_IP_NF_IPTABLES=m
+CONFIG_IP_NF_MATCH_AH=m
+CONFIG_IP_NF_MATCH_ECN=m
+CONFIG_IP_NF_MATCH_RPFILTER=m
+CONFIG_IP_NF_MATCH_TTL=m
+CONFIG_IP_NF_FILTER=m
+CONFIG_IP_NF_TARGET_REJECT=m
+CONFIG_IP_NF_TARGET_SYNPROXY=m
+CONFIG_IP_NF_NAT=m
+CONFIG_IP_NF_TARGET_MASQUERADE=m
+CONFIG_IP_NF_TARGET_NETMAP=m
+CONFIG_IP_NF_TARGET_REDIRECT=m
+CONFIG_IP_NF_MANGLE=m
+CONFIG_IP_NF_TARGET_CLUSTERIP=m
+CONFIG_IP_NF_TARGET_ECN=m
+CONFIG_IP_NF_TARGET_TTL=m
+CONFIG_IP_NF_RAW=m
+CONFIG_IP_NF_SECURITY=m
+CONFIG_IP_NF_ARPTABLES=m
+CONFIG_IP_NF_ARPFILTER=m
+CONFIG_IP_NF_ARP_MANGLE=m
+CONFIG_IP6_NF_IPTABLES=y
+CONFIG_IP6_NF_FILTER=y
+CONFIG_NF_TABLES_BRIDGE=m
+CONFIG_BRIDGE_NF_EBTABLES=m
+CONFIG_BRIDGE_EBT_BROUTE=m
+CONFIG_BRIDGE_EBT_T_FILTER=m
+CONFIG_BRIDGE_EBT_T_NAT=m
+CONFIG_IP_SCTP=m
+CONFIG_RDS=y
+CONFIG_L2TP=m
+CONFIG_BRIDGE=m
+CONFIG_VLAN_8021Q=m
+CONFIG_VLAN_8021Q_GVRP=y
+CONFIG_VLAN_8021Q_MVRP=y
+CONFIG_NET_SCHED=y
+CONFIG_NET_SCH_PRIO=m
+CONFIG_NET_SCH_TBF=m
+CONFIG_NET_SCH_INGRESS=m
+CONFIG_NET_CLS_BASIC=m
+CONFIG_NET_CLS_U32=m
+CONFIG_NET_CLS_CGROUP=m
+CONFIG_NET_CLS_ACT=y
+CONFIG_NET_ACT_POLICE=m
+CONFIG_NET_ACT_GACT=m
+CONFIG_NET_ACT_MIRRED=m
+CONFIG_OPENVSWITCH=m
+CONFIG_NETLINK_DIAG=y
+CONFIG_CGROUP_NET_PRIO=y
+CONFIG_BT=m
+CONFIG_BT_HCIBTUSB=m
+# CONFIG_BT_HCIBTUSB_BCM is not set
+# CONFIG_BT_HCIBTUSB_RTL is not set
+CONFIG_CFG80211=m
+CONFIG_CFG80211_WEXT=y
+CONFIG_MAC80211=m
+CONFIG_RFKILL=m
+CONFIG_RFKILL_INPUT=y
+CONFIG_NET_9P=y
+CONFIG_CAIF=y
+CONFIG_CEPH_LIB=m
+CONFIG_PCIEPORTBUS=y
+CONFIG_HOTPLUG_PCI_PCIE=y
+CONFIG_PCIEAER=y
+# CONFIG_PCIEASPM is not set
+CONFIG_PCI_IOV=y
+CONFIG_HOTPLUG_PCI=y
+CONFIG_HOTPLUG_PCI_SHPC=y
+CONFIG_PCCARD=m
+CONFIG_YENTA=m
+CONFIG_RAPIDIO=y
+CONFIG_RAPIDIO_TSI721=y
+CONFIG_RAPIDIO_ENABLE_RX_TX_PORTS=y
+CONFIG_RAPIDIO_ENUM_BASIC=m
+CONFIG_RAPIDIO_CHMAN=m
+CONFIG_RAPIDIO_MPORT_CDEV=m
+CONFIG_UEVENT_HELPER=y
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+CONFIG_MTD=m
+CONFIG_MTD_BLOCK=m
+CONFIG_MTD_CFI=m
+CONFIG_MTD_JEDECPROBE=m
+CONFIG_MTD_CFI_INTELEXT=m
+CONFIG_MTD_CFI_AMDSTD=m
+CONFIG_MTD_CFI_STAA=m
+CONFIG_MTD_RAM=m
+CONFIG_MTD_ROM=m
+CONFIG_PARPORT=y
+CONFIG_PARPORT_PC=y
+CONFIG_PARPORT_SERIAL=y
+CONFIG_PARPORT_PC_FIFO=y
+CONFIG_BLK_DEV_LOOP=y
+CONFIG_BLK_DEV_CRYPTOLOOP=y
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_BLK_DEV_RBD=m
+CONFIG_BLK_DEV_NVME=y
+CONFIG_EEPROM_AT24=m
+CONFIG_BLK_DEV_SD=y
+CONFIG_BLK_DEV_SR=y
+CONFIG_CHR_DEV_SG=y
+CONFIG_CHR_DEV_SCH=m
+CONFIG_SCSI_CONSTANTS=y
+CONFIG_SCSI_LOGGING=y
+CONFIG_SCSI_SPI_ATTRS=m
+CONFIG_SCSI_FC_ATTRS=m
+CONFIG_SCSI_SAS_ATA=y
+CONFIG_ISCSI_TCP=m
+CONFIG_SCSI_MVSAS=y
+# CONFIG_SCSI_MVSAS_DEBUG is not set
+CONFIG_SCSI_MVSAS_TASKLET=y
+CONFIG_SCSI_MVUMI=y
+CONFIG_MEGARAID_NEWGEN=y
+CONFIG_MEGARAID_MM=y
+CONFIG_MEGARAID_MAILBOX=y
+CONFIG_MEGARAID_LEGACY=y
+CONFIG_MEGARAID_SAS=y
+CONFIG_SCSI_MPT2SAS=y
+CONFIG_LIBFC=m
+CONFIG_LIBFCOE=m
+CONFIG_FCOE=m
+CONFIG_SCSI_QLOGIC_1280=m
+CONFIG_SCSI_QLA_FC=m
+CONFIG_TCM_QLA2XXX=m
+CONFIG_SCSI_QLA_ISCSI=m
+CONFIG_SCSI_LPFC=m
+CONFIG_ATA=y
+CONFIG_SATA_AHCI=y
+CONFIG_SATA_AHCI_PLATFORM=y
+CONFIG_PATA_ATIIXP=y
+CONFIG_PATA_PCMCIA=m
+CONFIG_MD=y
+CONFIG_BLK_DEV_MD=m
+CONFIG_MD_LINEAR=m
+CONFIG_MD_RAID0=m
+CONFIG_MD_RAID1=m
+CONFIG_MD_RAID10=m
+CONFIG_MD_RAID456=m
+CONFIG_MD_MULTIPATH=m
+CONFIG_BLK_DEV_DM=y
+CONFIG_DM_CRYPT=m
+CONFIG_DM_SNAPSHOT=m
+CONFIG_DM_THIN_PROVISIONING=m
+CONFIG_DM_MIRROR=m
+CONFIG_DM_ZERO=m
+CONFIG_DM_MULTIPATH=m
+CONFIG_DM_MULTIPATH_QL=m
+CONFIG_DM_MULTIPATH_ST=m
+CONFIG_TARGET_CORE=m
+CONFIG_TCM_IBLOCK=m
+CONFIG_TCM_FILEIO=m
+CONFIG_TCM_PSCSI=m
+CONFIG_TCM_USER2=m
+CONFIG_LOOPBACK_TARGET=m
+CONFIG_ISCSI_TARGET=m
+CONFIG_NETDEVICES=y
+CONFIG_BONDING=m
+CONFIG_DUMMY=y
+CONFIG_MACVLAN=m
+CONFIG_MACVTAP=m
+CONFIG_IPVLAN=m
+CONFIG_VXLAN=y
+CONFIG_RIONET=m
+CONFIG_TUN=m
+CONFIG_VETH=m
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_NET_VENDOR_ADAPTEC is not set
+# CONFIG_NET_VENDOR_AGERE is not set
+# CONFIG_NET_VENDOR_ALACRITECH is not set
+# CONFIG_NET_VENDOR_ALTEON is not set
+# CONFIG_NET_VENDOR_AMAZON is not set
+# CONFIG_NET_VENDOR_AMD is not set
+# CONFIG_NET_VENDOR_AQUANTIA is not set
+# CONFIG_NET_VENDOR_ARC is not set
+# CONFIG_NET_VENDOR_ATHEROS is not set
+CONFIG_BNX2=y
+# CONFIG_NET_VENDOR_BROCADE is not set
+# CONFIG_NET_VENDOR_CAVIUM is not set
+CONFIG_CHELSIO_T1=m
+CONFIG_CHELSIO_T1_1G=y
+CONFIG_CHELSIO_T3=m
+CONFIG_CHELSIO_T4=m
+# CONFIG_NET_VENDOR_CIRRUS is not set
+# CONFIG_NET_VENDOR_CISCO is not set
+# CONFIG_NET_VENDOR_DEC is not set
+# CONFIG_NET_VENDOR_DLINK is not set
+# CONFIG_NET_VENDOR_EMULEX is not set
+# CONFIG_NET_VENDOR_EZCHIP is not set
+# CONFIG_NET_VENDOR_I825XX is not set
+CONFIG_E1000=y
+CONFIG_E1000E=y
+CONFIG_IGB=y
+CONFIG_IXGB=y
+CONFIG_IXGBE=y
+# CONFIG_NET_VENDOR_MARVELL is not set
+# CONFIG_NET_VENDOR_MELLANOX is not set
+# CONFIG_NET_VENDOR_MICREL is not set
+# CONFIG_NET_VENDOR_MYRI is not set
+# CONFIG_NET_VENDOR_NATSEMI is not set
+# CONFIG_NET_VENDOR_NETRONOME is not set
+# CONFIG_NET_VENDOR_NVIDIA is not set
+# CONFIG_NET_VENDOR_OKI is not set
+# CONFIG_NET_VENDOR_QLOGIC is not set
+# CONFIG_NET_VENDOR_QUALCOMM is not set
+# CONFIG_NET_VENDOR_RDC is not set
+CONFIG_8139CP=m
+CONFIG_8139TOO=m
+CONFIG_R8169=y
+# CONFIG_NET_VENDOR_RENESAS is not set
+# CONFIG_NET_VENDOR_ROCKER is not set
+# CONFIG_NET_VENDOR_SAMSUNG is not set
+# CONFIG_NET_VENDOR_SEEQ is not set
+# CONFIG_NET_VENDOR_SOLARFLARE is not set
+# CONFIG_NET_VENDOR_SILAN is not set
+# CONFIG_NET_VENDOR_SIS is not set
+# CONFIG_NET_VENDOR_SMSC is not set
+CONFIG_STMMAC_ETH=y
+# CONFIG_NET_VENDOR_SUN is not set
+# CONFIG_NET_VENDOR_TEHUTI is not set
+# CONFIG_NET_VENDOR_TI is not set
+# CONFIG_NET_VENDOR_VIA is not set
+# CONFIG_NET_VENDOR_WIZNET is not set
+# CONFIG_NET_VENDOR_XILINX is not set
+CONFIG_PPP=m
+CONFIG_PPP_BSDCOMP=m
+CONFIG_PPP_DEFLATE=m
+CONFIG_PPP_FILTER=y
+CONFIG_PPP_MPPE=m
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPPOE=m
+CONFIG_PPPOL2TP=m
+CONFIG_PPP_ASYNC=m
+CONFIG_PPP_SYNC_TTY=m
+# CONFIG_USB_NET_AX8817X is not set
+# CONFIG_USB_NET_AX88179_178A is not set
+CONFIG_USB_NET_CDC_EEM=m
+CONFIG_USB_NET_HUAWEI_CDC_NCM=m
+CONFIG_USB_NET_CDC_MBIM=m
+# CONFIG_USB_NET_NET1080 is not set
+# CONFIG_USB_BELKIN is not set
+# CONFIG_USB_ARMLINUX is not set
+# CONFIG_USB_NET_ZAURUS is not set
+CONFIG_ATH9K=m
+CONFIG_ATH9K_HTC=m
+CONFIG_IWLWIFI=m
+CONFIG_IWLDVM=m
+CONFIG_IWLMVM=m
+CONFIG_IWLWIFI_BCAST_FILTERING=y
+CONFIG_HOSTAP=m
+CONFIG_MT7601U=m
+CONFIG_RT2X00=m
+CONFIG_RT2800USB=m
+CONFIG_RTL8192CE=m
+CONFIG_RTL8192SE=m
+CONFIG_RTL8192DE=m
+CONFIG_RTL8723AE=m
+CONFIG_RTL8723BE=m
+CONFIG_RTL8188EE=m
+CONFIG_RTL8192EE=m
+CONFIG_RTL8821AE=m
+CONFIG_RTL8192CU=m
+# CONFIG_RTLWIFI_DEBUG is not set
+CONFIG_RTL8XXXU=m
+CONFIG_ZD1211RW=m
+CONFIG_USB_NET_RNDIS_WLAN=m
+CONFIG_INPUT_POLLDEV=m
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_PSAUX=y
+CONFIG_KEYBOARD_XTKBD=m
+CONFIG_MOUSE_PS2_ELANTECH=y
+CONFIG_MOUSE_PS2_SENTELIC=y
+CONFIG_MOUSE_SERIAL=m
+CONFIG_INPUT_MISC=y
+CONFIG_INPUT_UINPUT=m
+CONFIG_SERIO_SERPORT=m
+CONFIG_SERIO_RAW=m
+CONFIG_LEGACY_PTY_COUNT=16
+CONFIG_SERIAL_8250=y
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_NR_UARTS=16
+CONFIG_SERIAL_8250_RUNTIME_UARTS=16
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_MANY_PORTS=y
+CONFIG_SERIAL_8250_SHARE_IRQ=y
+CONFIG_SERIAL_8250_RSA=y
+CONFIG_SERIAL_OF_PLATFORM=y
+CONFIG_SERIAL_NONSTANDARD=y
+CONFIG_PRINTER=m
+CONFIG_IPMI_HANDLER=m
+CONFIG_IPMI_DEVICE_INTERFACE=m
+CONFIG_IPMI_SI=m
+CONFIG_HW_RANDOM=y
+CONFIG_RAW_DRIVER=m
+CONFIG_I2C_CHARDEV=y
+CONFIG_I2C_PIIX4=y
+CONFIG_I2C_GPIO=y
+CONFIG_SPI=y
+CONFIG_GPIO_SYSFS=y
+CONFIG_GPIO_LOONGSON=y
+CONFIG_SENSORS_LM75=m
+CONFIG_SENSORS_LM93=m
+CONFIG_SENSORS_W83795=m
+CONFIG_SENSORS_W83627HF=m
+CONFIG_RC_CORE=m
+CONFIG_LIRC=y
+CONFIG_RC_DECODERS=y
+CONFIG_IR_NEC_DECODER=m
+CONFIG_IR_RC5_DECODER=m
+CONFIG_IR_RC6_DECODER=m
+CONFIG_IR_JVC_DECODER=m
+CONFIG_IR_SONY_DECODER=m
+CONFIG_IR_SANYO_DECODER=m
+CONFIG_IR_SHARP_DECODER=m
+CONFIG_IR_MCE_KBD_DECODER=m
+CONFIG_IR_XMP_DECODER=m
+CONFIG_IR_IMON_DECODER=m
+CONFIG_MEDIA_SUPPORT=m
+CONFIG_MEDIA_USB_SUPPORT=y
+CONFIG_USB_VIDEO_CLASS=m
+CONFIG_MEDIA_PCI_SUPPORT=y
+CONFIG_VIDEO_BT848=m
+CONFIG_DVB_BT8XX=m
+CONFIG_DRM=y
+CONFIG_DRM_RADEON=m
+CONFIG_DRM_RADEON_USERPTR=y
+CONFIG_DRM_AMDGPU=m
+CONFIG_DRM_AMDGPU_SI=y
+CONFIG_DRM_AMDGPU_CIK=y
+CONFIG_DRM_AMDGPU_USERPTR=y
+CONFIG_DRM_AST=y
+CONFIG_FB_EFI=y
+CONFIG_FB_RADEON=y
+CONFIG_LCD_PLATFORM=m
+# CONFIG_VGA_CONSOLE is not set
+CONFIG_FRAMEBUFFER_CONSOLE=y
+CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
+CONFIG_LOGO=y
+CONFIG_SOUND=y
+CONFIG_SND=y
+CONFIG_SND_SEQUENCER=m
+CONFIG_SND_SEQ_DUMMY=m
+# CONFIG_SND_ISA is not set
+CONFIG_SND_BT87X=m
+CONFIG_SND_BT87X_OVERCLOCK=y
+CONFIG_SND_HDA_INTEL=y
+CONFIG_SND_HDA_HWDEP=y
+CONFIG_SND_HDA_INPUT_BEEP=y
+CONFIG_SND_HDA_PATCH_LOADER=y
+CONFIG_SND_HDA_CODEC_REALTEK=y
+CONFIG_SND_HDA_CODEC_SIGMATEL=y
+CONFIG_SND_HDA_CODEC_HDMI=y
+CONFIG_SND_HDA_CODEC_CONEXANT=y
+CONFIG_SND_USB_AUDIO=m
+CONFIG_HIDRAW=y
+CONFIG_HID_A4TECH=m
+CONFIG_HID_CHERRY=m
+CONFIG_HID_LOGITECH=m
+CONFIG_HID_LOGITECH_DJ=m
+CONFIG_LOGITECH_FF=y
+CONFIG_LOGIRUMBLEPAD2_FF=y
+CONFIG_LOGIG940_FF=y
+CONFIG_HID_MICROSOFT=m
+CONFIG_HID_MULTITOUCH=m
+CONFIG_HID_SUNPLUS=m
+CONFIG_USB_HIDDEV=y
+CONFIG_USB=y
+CONFIG_USB_OTG=y
+CONFIG_USB_MON=y
+CONFIG_USB_XHCI_HCD=y
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_EHCI_ROOT_HUB_TT=y
+CONFIG_USB_EHCI_HCD_PLATFORM=y
+CONFIG_USB_OHCI_HCD=y
+CONFIG_USB_OHCI_HCD_PLATFORM=y
+CONFIG_USB_UHCI_HCD=m
+CONFIG_USB_ACM=m
+CONFIG_USB_PRINTER=m
+CONFIG_USB_STORAGE=m
+CONFIG_USB_STORAGE_REALTEK=m
+CONFIG_USB_UAS=m
+CONFIG_USB_DWC2=y
+CONFIG_USB_DWC2_HOST=y
+CONFIG_USB_SERIAL=m
+CONFIG_USB_SERIAL_CH341=m
+CONFIG_USB_SERIAL_CP210X=m
+CONFIG_USB_SERIAL_FTDI_SIO=m
+CONFIG_USB_SERIAL_PL2303=m
+CONFIG_USB_SERIAL_OPTION=m
+CONFIG_USB_GADGET=y
+CONFIG_INFINIBAND=m
+CONFIG_RTC_CLASS=y
+CONFIG_RTC_DRV_EFI=y
+CONFIG_UIO=m
+# CONFIG_VIRTIO_MENU is not set
+CONFIG_COMEDI=m
+CONFIG_COMEDI_PCI_DRIVERS=m
+CONFIG_COMEDI_8255_PCI=m
+CONFIG_COMEDI_ADL_PCI6208=m
+CONFIG_COMEDI_ADL_PCI7X3X=m
+CONFIG_COMEDI_ADL_PCI8164=m
+CONFIG_COMEDI_ADL_PCI9111=m
+CONFIG_COMEDI_ADL_PCI9118=m
+CONFIG_COMEDI_ADV_PCI1710=m
+CONFIG_COMEDI_ADV_PCI1720=m
+CONFIG_COMEDI_ADV_PCI1723=m
+CONFIG_COMEDI_ADV_PCI1724=m
+CONFIG_COMEDI_ADV_PCI1760=m
+CONFIG_COMEDI_ADV_PCI_DIO=m
+CONFIG_COMEDI_NI_LABPC_PCI=m
+CONFIG_COMEDI_NI_PCIDIO=m
+CONFIG_COMEDI_NI_PCIMIO=m
+CONFIG_STAGING=y
+CONFIG_R8188EU=m
+# CONFIG_88EU_AP_MODE is not set
+CONFIG_PM_DEVFREQ=y
+CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y
+CONFIG_DEVFREQ_GOV_PERFORMANCE=y
+CONFIG_DEVFREQ_GOV_POWERSAVE=y
+CONFIG_DEVFREQ_GOV_USERSPACE=y
+CONFIG_PWM=y
+CONFIG_EXT2_FS=y
+CONFIG_EXT2_FS_XATTR=y
+CONFIG_EXT2_FS_POSIX_ACL=y
+CONFIG_EXT2_FS_SECURITY=y
+CONFIG_EXT3_FS=y
+CONFIG_EXT3_FS_POSIX_ACL=y
+CONFIG_EXT3_FS_SECURITY=y
+CONFIG_XFS_FS=y
+CONFIG_XFS_QUOTA=y
+CONFIG_BTRFS_FS=y
+CONFIG_FANOTIFY=y
+CONFIG_FANOTIFY_ACCESS_PERMISSIONS=y
+CONFIG_QUOTA=y
+# CONFIG_PRINT_QUOTA_WARNING is not set
+CONFIG_QFMT_V1=m
+CONFIG_QFMT_V2=m
+CONFIG_AUTOFS4_FS=y
+CONFIG_FUSE_FS=m
+CONFIG_OVERLAY_FS=y
+CONFIG_OVERLAY_FS_INDEX=y
+CONFIG_OVERLAY_FS_XINO_AUTO=y
+CONFIG_OVERLAY_FS_METACOPY=y
+CONFIG_FSCACHE=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+CONFIG_UDF_FS=y
+CONFIG_MSDOS_FS=m
+CONFIG_VFAT_FS=m
+CONFIG_FAT_DEFAULT_CODEPAGE=936
+CONFIG_FAT_DEFAULT_IOCHARSET="gb2312"
+CONFIG_PROC_KCORE=y
+CONFIG_TMPFS=y
+CONFIG_TMPFS_POSIX_ACL=y
+CONFIG_HUGETLBFS=y
+CONFIG_CONFIGFS_FS=y
+# CONFIG_EFIVAR_FS is not set
+CONFIG_CRAMFS=m
+CONFIG_SQUASHFS=y
+CONFIG_SQUASHFS_XATTR=y
+CONFIG_SQUASHFS_LZ4=y
+CONFIG_SQUASHFS_LZO=y
+CONFIG_SQUASHFS_XZ=y
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3_ACL=y
+CONFIG_NFS_V4=y
+CONFIG_NFS_V4_1=y
+CONFIG_NFS_V4_2=y
+CONFIG_ROOT_NFS=y
+CONFIG_NFSD=y
+CONFIG_NFSD_V3_ACL=y
+CONFIG_NFSD_V4=y
+CONFIG_NFSD_BLOCKLAYOUT=y
+CONFIG_CIFS=m
+# CONFIG_CIFS_DEBUG is not set
+CONFIG_9P_FS=y
+CONFIG_NLS_CODEPAGE_437=y
+CONFIG_NLS_CODEPAGE_936=y
+CONFIG_NLS_ASCII=y
+CONFIG_NLS_UTF8=y
+CONFIG_KEY_DH_OPERATIONS=y
+CONFIG_SECURITY=y
+CONFIG_SECURITY_SELINUX=y
+CONFIG_SECURITY_SELINUX_BOOTPARAM=y
+CONFIG_SECURITY_SELINUX_DISABLE=y
+CONFIG_SECURITY_APPARMOR=y
+CONFIG_DEFAULT_SECURITY_DAC=y
+CONFIG_CRYPTO_USER=m
+# CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set
+CONFIG_CRYPTO_PCRYPT=m
+CONFIG_CRYPTO_CRYPTD=m
+CONFIG_CRYPTO_CHACHA20POLY1305=m
+CONFIG_CRYPTO_HMAC=y
+CONFIG_CRYPTO_VMAC=m
+CONFIG_CRYPTO_TGR192=m
+CONFIG_CRYPTO_WP512=m
+CONFIG_CRYPTO_ANUBIS=m
+CONFIG_CRYPTO_BLOWFISH=m
+CONFIG_CRYPTO_CAST5=m
+CONFIG_CRYPTO_CAST6=m
+CONFIG_CRYPTO_KHAZAD=m
+CONFIG_CRYPTO_SALSA20=m
+CONFIG_CRYPTO_SEED=m
+CONFIG_CRYPTO_SERPENT=m
+CONFIG_CRYPTO_TEA=m
+CONFIG_CRYPTO_TWOFISH=m
+CONFIG_CRYPTO_DEFLATE=m
+CONFIG_CRYPTO_USER_API_HASH=m
+CONFIG_CRYPTO_USER_API_SKCIPHER=m
+CONFIG_CRYPTO_USER_API_RNG=m
+CONFIG_CRYPTO_USER_API_AEAD=m
+CONFIG_PRINTK_TIME=y
+CONFIG_FRAME_WARN=1024
+CONFIG_STRIP_ASM_SYMS=y
+CONFIG_MAGIC_SYSRQ=y
+# CONFIG_SCHED_DEBUG is not set
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_FTRACE is not set
-- 
2.27.0


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

* Re: [PATCH 00/19] arch: Add basic LoongArch support
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (17 preceding siblings ...)
  2021-07-06  4:18 ` [PATCH 19/19] LoongArch: Add Loongson-3 default config file Huacai Chen
@ 2021-07-06 10:11 ` Arnd Bergmann
  2021-07-07  3:04   ` Huacai Chen
  2021-07-06 10:33 ` Arnd Bergmann
       [not found] ` <20210706041820.1536502-5-chenhuacai@loongson.cn>
  20 siblings, 1 reply; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:11 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang, Chen Zhu

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
>
> LoongArch is a new RISC ISA, which is a bit like MIPS or RISC-V.
> LoongArch includes a reduced 32-bit version (LA32R), a standard 32-bit
> version (LA32S) and a 64-bit version (LA64). LoongArch use ACPI as its
> boot protocol LoongArch-specific interrupt controllers (similar to APIC)
> are already added in the next revision of ACPI Specification (current
> revision is 6.4).
>
> This patchset is adding basic LoongArch support in mainline kernel, we
> can see a complete snapshot here:
> https://github.com/loongson/linux/tree/loongarch-next
>
> Cross-compile tool chain to build kernel:
> https://github.com/loongson/build-tools/releases
>
> Loongson and LoongArch documentations:
> https://github.com/loongson/LoongArch-Documentation
>
> LoongArch-specific interrupt controllers:
> https://mantis.uefi.org/mantis/view.php?id=2203

Thanks a lot for your submission, I've been waiting for this, and have just
completed an initial quick review, will send out the individual replies in a
bit.

I have a few high-level comments about things that need to be improved
before merging:

1. Copyright statements: I can see that you copied a lot of code from arch/mips,
 and then a bit from arch/arm64 and possibly arch/riscv/, but every file just
 lists loongson as the copyright holder and one or more employees as authors.
 This clearly has to be resolved by listing the original authors as well.

2. Reducing the amount of copied code: my impression is that the bits you
   wrote yourself are generally of higher quality than the bits you copied from
   mips, usually because those have grown over a long time to accommodate
   cases you should not worry about. In cases where there was no generic
   implementation, we should try to come up with a version that can be shared
   instead of adding another copy.

3. 32-bit support: There are small bits of code for 32-bit support everywhere
   throughout the code base, but there is also code that does not look like
   it would work in that configuration. I would suggest you split out all 32-bit
   code into a separate patch, the way you have done for SMP and NUMA
   support. That way it can be reviewed separately, or deferred until when
   you actually add support for a 32-bit platform. Please also clarify which
   modes you plan to support: should every 64-bit kernel be able to run
   LA32R and LA32S user space binaries, or do you require running the same
   instruction set in both kernel and user space as RISC-V does?
   Do you plan to have MIPS N32 style user space on 64-bit kernels?

4. Toolchain sources: I see you only have binaries for an older gcc-8.3
    release and no sources so far. I assume you are already busy porting
    the internal patches to the latest gcc and will post that soon, but my
    feeling is that the kernel port should not get merged until we have a
    confirmation from the toolchain maintainers that they plan to merge
    support for their following release. We should however make sure that
    your kernel port is completely reviewed and can just get merged once
    we get there.

5. Platform support: You have copied the underlying logic from arch/mips,
    but that still uses a method where most platforms (not the new
    "generic" version) are mutually exclusive. Since you only support
    one platform right now, it would be best to just simplify it to the point
    where no platform specific code is needed in arch/loongarch/ and
    it just works. If you add 32-bit support later on, that obviously requires
    making a choice between two or three incompatible options.

        Arnd

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

* Re: [PATCH 03/19] LoongArch: Add build infrastructure
  2021-07-06  4:18 ` [PATCH 03/19] LoongArch: Add build infrastructure Huacai Chen
@ 2021-07-06 10:12   ` Arnd Bergmann
  2021-07-19  1:26     ` Huacai Chen
  2021-07-06 10:35   ` Arnd Bergmann
  2021-07-07  0:00   ` Randy Dunlap
  2 siblings, 1 reply; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:12 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

 Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> --- /dev/null
> +++ b/arch/loongarch/Kbuild
> @@ -0,0 +1,21 @@
> +# Fail on warnings - also for files referenced in subdirs
> +# -Werror can be disabled for specific files using:
> +# CFLAGS_<file.o> := -Wno-error
> +ifeq ($(W),)
> +subdir-ccflags-y := -Werror
> +endif

This tends to break building with 'make W=12' or similar, I would recommend not
adding -Werror. It is a good idea though to have your CI systems build
with -Werror
enabled on the command line though.

> +# platform specific definitions
> +include arch/loongarch/Kbuild.platforms
> +obj-y := $(platform-y)
> +

I would recommend not planning on having platform specific files. The way
we do it on modern platforms (arm64, rv64) is to have all the device drivers
in the driver subsystems instead.

> +       select GENERIC_IOMAP

GENERIC_IOMAP is one of those you probably don't want here. If PCI I/O
space is mapped into MMIO registers, just do what arm64 has and make
ioport_map() return a pointer, then make ioread32() an alias for readl().

> +       select HAVE_CBPF_JIT if !64BIT
> +       select HAVE_EBPF_JIT if 64BIT

These look like you incorrectly copied them from MIPS. I don't see any EBPF_JIT
implemementation (I may have missed that, as I'm still starting), but
you probably
don't want the CBPF version for 32-bit anyway.

> +       select HAVE_IDE

drivers/ide/ was just removed,so this line can go as well.

> +       select VIRT_TO_BUS

Remove this: if you still require a driver that fails to use the dma-mapping
API, fix the driver instead so it can work with IOMMUs.

> +menu "Machine selection"
> +
> +choice
> +       prompt "System type"
> +       default MACH_LOONGSON64
> +
> +config MACH_LOONGSON64
> +       bool "Loongson 64-bit family of machines"

There should generally not be a 'choice' menu to select between
different machine types, but instead the kernel should be able
to run on all platforms that have the same instruction set.

Maybe just make this a silent option and use 'select
MACH_LOONGSON64' so you can pick up the device drivers
that mips uses.

> +       select ARCH_MIGHT_HAVE_PC_PARPORT
> +       select ARCH_MIGHT_HAVE_PC_SERIO
> +       select GENERIC_ISA_DMA
> +       select ISA

Do you actually have ISA-style add-on cards? If not, then
remove the ISA options. If you do, which drivers are they?

> +config CPU_LOONGSON64
> +       bool "Loongson 64-bit CPU"
> +       depends on SYS_HAS_CPU_LOONGSON64
> +       select CPU_SUPPORTS_64BIT_KERNEL
> +       select GPIOLIB
> +       select SWIOTLB
> +       select ARCH_SUPPORTS_ATOMIC_RMW
> +       help
> +         The Loongson 64-bit processor implements the LoongArch64 (the 64-bit
> +         version of LoongArch) instruction set.
> +
> +endchoice
> +
> +config SYS_HAS_CPU_LOONGSON64
> +       bool
> +
> +endmenu
> +
> +config SYS_SUPPORTS_32BIT_KERNEL
> +       bool
> +config SYS_SUPPORTS_64BIT_KERNEL
> +       bool
> +config CPU_SUPPORTS_32BIT_KERNEL
> +       bool
> +config CPU_SUPPORTS_64BIT_KERNEL
> +       bool

Same for the CPUs: I would suggest you keep this simple until you get
to the point of actually having multiple CPUs that you need to distinguish.

Different 64-bit CPUs are hopefully going to be compatible with one
another, so they should not be listed as mutually exclusive in a
'choice' statement. The complexity with two levels of 32/64 support
is probably not going to be helpful here either.

The 'select' statements that you have under CPU_LOONGSON64
ook like they should just be in the top level.

GPIOLIB and SWIOTLB could just be left user selectable, if turning
them off results only in run-time loss of functionality but not a
build failure.

> +menu "Kernel type"
> +
> +choice
> +       prompt "Kernel code model"
> +       help
> +         You should only select this option if you have a workload that
> +         actually benefits from 64-bit processing or if your machine has
> +         large memory.  You will only be presented a single option in this
> +         menu if your system does not support both 32-bit and 64-bit kernels.
> +
> +config 32BIT
> +       bool "32-bit kernel"
> +       depends on CPU_SUPPORTS_32BIT_KERNEL && SYS_SUPPORTS_32BIT_KERNEL
> +       help
> +         Select this option if you want to build a 32-bit kernel.
> +
> +config 64BIT
> +       bool "64-bit kernel"
> +       depends on CPU_SUPPORTS_64BIT_KERNEL && SYS_SUPPORTS_64BIT_KERNEL
> +       help
> +         Select this option if you want to build a 64-bit kernel.
> +
> +endchoice

Since you don't support any 32-bit target at the moment, just make CONFIG_64BIT
a default-on statement, and make it user-selectable only when you add a 32-bit
target.

> +choice
> +       prompt "Kernel page size"
> +       default PAGE_SIZE_16KB
> +
> +config PAGE_SIZE_4KB
> +       bool "4kB"
> +       help
> +         This option select the standard 4kB Linux page size.
> +
> +config PAGE_SIZE_16KB
> +       bool "16kB"
> +       help
> +         This option select the standard 16kB Linux page size.
> +
> +config PAGE_SIZE_64KB
> +       bool "64kB"
> +       help
> +         This option select the standard 64kB Linux page size.
> +
> +endchoice
> +
> +choice
> +       prompt "Virtual memory address space bits"
> +       default VA_BITS_40
> +       help
> +         Allows choosing one of multiple possible virtual memory
> +         address space bits for applications. The level of page
> +         translation table is determined by a combination of page
> +         size and virtual memory address space bits.
> +
> +config VA_BITS_40
> +       bool "40-bits"
> +       depends on 64BIT
> +       help
> +         Support a maximum at least 40 bits of application virtual memory.
> +
> +config VA_BITS_48
> +       bool "48-bits"
> +       depends on 64BIT
> +       help
> +         Support a maximum at least 48 bits of application virtual memory.
> +
> +endchoice

Isn't the size of the address space tied to the page size?

         Arnd

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

* Re: [PATCH 04/19] LoongArch: Add common headers
       [not found] ` <20210706041820.1536502-5-chenhuacai@loongson.cn>
@ 2021-07-06 10:16   ` Arnd Bergmann
  2021-08-12 11:05     ` Huacai Chen
  2021-07-06 10:54   ` Arnd Bergmann
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:16 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

 On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
> This patch adds common headers for basic LoongArch support.
>

This patch is really too long to properly review, as it puts lots of
unrelated headers
into one commit. Please split it up into logical chunks, ideally
together with the
corresponding C files. Some obvious categories are:

- memory management
- atomics and locking
- CPU register specifics

> --- /dev/null
> +++ b/arch/loongarch/include/asm/abi.h
> @@ -0,0 +1,33 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef _ASM_ABI_H
> +#define _ASM_ABI_H
> +
> +#include <linux/signal_types.h>
> +
> +#include <asm/signal.h>
> +#include <asm/siginfo.h>
> +#include <asm/vdso.h>
> +
> +struct loongarch_abi {
> +       int (* const setup_frame)(void *sig_return, struct ksignal *ksig,
> +                                 struct pt_regs *regs, sigset_t *set);
> +       int (* const setup_rt_frame)(void *sig_return, struct ksignal *ksig,
> +                                    struct pt_regs *regs, sigset_t *set);

I can see you copied this from mips, but I don't see why you would need it here.
Can you just call the functions directly?

> +/*
> + * Return the bit position (0..63) of the most significant 1 bit in a word
> + * Returns -1 if no 1 bit exists
> + */
> +static inline unsigned long __fls(unsigned long word)
> +{

Can you use the compiler builtins here? Since you know that you
have a modern compiler, you can probably expect it to produce better
output than the open-coded inline.

> diff --git a/arch/loongarch/include/asm/bootinfo.h b/arch/loongarch/include/asm/bootinfo.h
> new file mode 100644
> index 000000000000..aa9915a56f66
> --- /dev/null
> +++ b/arch/loongarch/include/asm/bootinfo.h
> @@ -0,0 +1,38 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef _ASM_BOOTINFO_H
> +#define _ASM_BOOTINFO_H
> +
> +#include <linux/types.h>
> +#include <asm/setup.h>
> +
> +const char *get_system_type(void);
> +
> +extern void early_memblock_init(void);
> +extern void detect_memory_region(phys_addr_t start, phys_addr_t sz_min,  phys_addr_t sz_max);
> +
> +extern void early_init(void);
> +extern void platform_init(void);
> +
> +extern void free_init_pages(const char *what, unsigned long begin, unsigned long end);
> +
> +/*
> + * Initial kernel command line, usually setup by fw_init_cmdline()
> + */
> +extern char arcs_cmdline[COMMAND_LINE_SIZE];
> +
> +/*
> + * Registers a0, a1, a3 and a4 as passed to the kernel entry by firmware
> + */
> +extern unsigned long fw_arg0, fw_arg1, fw_arg2, fw_arg3;
> +
> +extern unsigned long initrd_start, initrd_end;
> +
> +/*
> + * Platform memory detection hook called by setup_arch
> + */
> +extern void plat_mem_setup(void);

I think the platform specific options should all be removed and replaced
with  a well-defined firmware interface, so remove get_system_type()/
platform_init()/plat_mem_setup() etc.

> +
> +static inline void check_bugs(void)
> +{
> +       unsigned int cpu = smp_processor_id();
> +
> +       cpu_data[cpu].udelay_val = loops_per_jiffy;
> +}

This needs a comment to explain what bug you are working around.

> diff --git a/arch/loongarch/include/asm/dma.h b/arch/loongarch/include/asm/dma.h
> new file mode 100644
> index 000000000000..a8a58dc93422
> --- /dev/null
> +++ b/arch/loongarch/include/asm/dma.h
> @@ -0,0 +1,13 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef __ASM_DMA_H
> +#define __ASM_DMA_H
> +
> +#define MAX_DMA_ADDRESS        PAGE_OFFSET
> +#define MAX_DMA32_PFN  (1UL << (32 - PAGE_SHIFT))
> +
> +extern int isa_dma_bridge_buggy;
> +
> +#endif

Can you send a cleanup patch for this? I think we should get rid of the
isa_dma_bridge_buggy variable for architectures that do not have a VIA
VP2 style ISA bridge.

> diff --git a/arch/loongarch/include/asm/dmi.h b/arch/loongarch/include/asm/dmi.h
> new file mode 100644
> index 000000000000..0fcfbba93363
> --- /dev/null
> +++ b/arch/loongarch/include/asm/dmi.h
> @@ -0,0 +1,26 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef _ASM_DMI_H
> +#define _ASM_DMI_H
> +
> +#include <linux/io.h>
> +#include <linux/memblock.h>
> +
> +#define dmi_early_remap                early_ioremap
> +#define dmi_early_unmap                early_iounmap
> +#define dmi_remap              dmi_ioremap
> +#define dmi_unmap              dmi_iounmap
> +#define dmi_alloc(l)           memblock_alloc_low(l, PAGE_SIZE)
> +
> +void __init __iomem *dmi_ioremap(u64 phys_addr, unsigned long size)
> +{
> +       return ((void *)TO_CAC(phys_addr));
> +}

This will cause a warning from sparse about a mismatched address space.
Maybe check all of your sources with 'make C=1' to see what other warnings
you missed.

> diff --git a/arch/loongarch/include/asm/fw.h b/arch/loongarch/include/asm/fw.h
> new file mode 100644
> index 000000000000..8a0e8e7c625f
> --- /dev/null
> +++ b/arch/loongarch/include/asm/fw.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef __ASM_FW_H_
> +#define __ASM_FW_H_
> +
> +#include <asm/bootinfo.h>
> +
> +extern int fw_argc;
> +extern long *_fw_argv, *_fw_envp;
> +
> +#define fw_argv(index)         ((char *)(long)_fw_argv[(index)])
> +#define fw_envp(index)         ((char *)(long)_fw_envp[(index)])
> +
> +extern void fw_init_cmdline(void);
> +
> +#endif /* __ASM_FW_H_ */

You write that the system is booted using UEFI. Doesn't that mean you
pass the command line in UEFI structures as well?

> +/*
> + * Dummy values to fill the table in mmap.c
> + * The real values will be generated at runtime
> + * See setup_protection_map
> + */
> +#define __P000 __pgprot(0)
> +#define __P001 __pgprot(0)
> +#define __P010 __pgprot(0)
> +#define __P011 __pgprot(0)
> +#define __P100 __pgprot(0)
> +#define __P101 __pgprot(0)
> +#define __P110 __pgprot(0)
> +#define __P111 __pgprot(0)
> +
> +#define __S000 __pgprot(0)
> +#define __S001 __pgprot(0)
> +#define __S010 __pgprot(0)
> +#define __S011 __pgprot(0)
> +#define __S100 __pgprot(0)
> +#define __S101 __pgprot(0)
> +#define __S110 __pgprot(0)
> +#define __S111 __pgprot(0)

Why does this have to be a runtime thing? If you only support one CPU
implementation, are these not all constant?

> +#ifdef CONFIG_64BIT
> +#define TASK_SIZE32    0x80000000UL

Why is the task size for compat tasks limited to 2GB? Shouldn't these
be able to use the full 32-bit address space?

> +#ifdef CONFIG_VA_BITS_40
> +#define TASK_SIZE64     (0x1UL << ((cpu_vabits > 40)?40:cpu_vabits))
> +#endif
> +#ifdef CONFIG_VA_BITS_48
> +#define TASK_SIZE64     (0x1UL << ((cpu_vabits > 48)?48:cpu_vabits))
> +#endif

Why would the CPU have fewer VA bits than the page table layout allows?

> diff --git a/arch/loongarch/include/asm/reg.h b/arch/loongarch/include/asm/reg.h
> new file mode 100644
> index 000000000000..8315e6fc8079
> --- /dev/null
> +++ b/arch/loongarch/include/asm/reg.h
> @@ -0,0 +1,5 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#include <uapi/asm/reg.h>

Empty files like this one should not be needed, since the uapi directory comes
next in the search path.

> --- /dev/null
> +++ b/arch/loongarch/include/asm/spinlock.h
> @@ -0,0 +1,12 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef _ASM_SPINLOCK_H
> +#define _ASM_SPINLOCK_H
> +
> +#include <asm/processor.h>
> +#include <asm/qspinlock.h>
> +#include <asm/qrwlock.h>
> +
> +#endif /* _ASM_SPINLOCK_H */

qspinlock is usually not ideal, unless you have large NUMA systems.
As I see that the instruction set does not have the required 16-bit
xchg() instruction that you emulate using a cmpxchg() loop, it seems
likely that you cannot actually use qspinlock in a way that guarantees
forward progress.

We just had that discussion regarding risc-v qspinlock, see
https://lore.kernel.org/linux-riscv/1616868399-82848-4-git-send-email-guoren@kernel.org/
for Peter's explanations.

> +
> +static inline cycles_t get_cycles(void)
> +{
> +       return drdtime();
> +}
> +
> +static inline unsigned long random_get_entropy(void)
> +{
> +       return drdtime();
> +}
> +#endif /* __KERNEL__ */

The second function here is not used since it's not a macro. Just remove
it and use the identical default.

> +/*
> + * Subprogram calling convention
> + */
> +#define _LOONGARCH_SIM_ABILP32         1
> +#define _LOONGARCH_SIM_ABILPX32                2
> +#define _LOONGARCH_SIM_ABILP64         3

What is the difference between ILP32 and ILPX32 here?

What is the ILP64 support for, do you support both the regular LP64 and ILP64
with 64-bit 'int'?

           Arnd

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

* Re: [PATCH 05/19] LoongArch: Add boot and setup routines
  2021-07-06  4:18 ` [PATCH 05/19] LoongArch: Add boot and setup routines Huacai Chen
@ 2021-07-06 10:16   ` Arnd Bergmann
  2021-07-27 11:53     ` Huacai Chen
  2021-07-06 10:55   ` Arnd Bergmann
  1 sibling, 1 reply; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:16 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> +
> +#ifdef CONFIG_64BIT
> +       /* Guess if the sign extension was forgotten by bootloader */
> +       if (start < CAC_BASE)
> +               start = (int)start;
> +#endif
> +       initrd_start = start;
> +       initrd_end += start;
> +       return 0;
> +}
> +early_param("rd_start", rd_start_early);
> +
> +static int __init rd_size_early(char *p)
> +{
> +       initrd_end += memparse(p, &p);
> +       return 0;
> +}
> +early_param("rd_size", rd_size_early);

The early parameters should not be used for this, I'm fairly sure the UEFI
boot protocol already has ways to communicate all necessary information.

> +
> +#ifdef CONFIG_ACPI
> +       init_initrd();
> +#endif

Why is the initrd support tied to ACPI? Can you actually boot without ACPI?

> +#if defined(CONFIG_VT)
> +#if defined(CONFIG_VGA_CONSOLE)
> +       conswitchp = &vga_con;
> +#elif defined(CONFIG_DUMMY_CONSOLE)
> +       conswitchp = &dummy_con;
> +#endif
> +#endif

The VGA console seems rather outdated. If you have UEFI, why not use
the provided framebuffer for the console?

> +u64 cpu_clock_freq;
> +EXPORT_SYMBOL(cpu_clock_freq);
> +u64 const_clock_freq;
> +EXPORT_SYMBOL(const_clock_freq);

You should generally not rely on the CPU clock frequency being fixed
like this, as this breaks down as soon as you add a drivers/cpufreq/ driver.

What code uses these?

> +void __init time_init(void)
> +{
> +       if (!cpu_has_cpucfg)
> +               const_clock_freq = cpu_clock_freq;
> +       else
> +               const_clock_freq = calc_const_freq();
> +
> +       init_timeval = drdtime() - csr_readq(LOONGARCH_CSR_CNTC);
> +
> +       constant_clockevent_init();
> +       constant_clocksource_init();
> +}

Clocksource and clockevents drivers should be located in drivers/clocksource
and reviewed by its maintainers.

         Arnd

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

* Re: [PATCH 06/19] LoongArch: Add exception/interrupt handling
  2021-07-06  4:18 ` [PATCH 06/19] LoongArch: Add exception/interrupt handling Huacai Chen
@ 2021-07-06 10:16   ` Arnd Bergmann
  2021-07-06 10:56     ` Arnd Bergmann
  2021-07-06 11:06   ` Peter Zijlstra
  1 sibling, 1 reply; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:16 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> diff --git a/arch/loongarch/include/asm/break.h b/arch/loongarch/include/asm/break.h
> new file mode 100644
> index 000000000000..109d0c85c582
> --- /dev/null
> +++ b/arch/loongarch/include/asm/break.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef __ASM_BREAK_H
> +#define __ASM_BREAK_H
> +
> +#include <uapi/asm/break.h>
> +
> +#endif /* __ASM_BREAK_H */

The file can be removed.

> --- /dev/null
> +++ b/arch/loongarch/include/asm/debug.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +
> +#ifndef __LOONGARCH_ASM_DEBUG_H__
> +#define __LOONGARCH_ASM_DEBUG_H__
> +
> +#include <linux/dcache.h>
> +
> +/*
> + * loongarch_debugfs_dir corresponds to the "loongarch" directory at the top
> + * level of the DebugFS hierarchy. LoongArch-specific DebugFS entries should
> + * be placed beneath this directory.
> + */
> +extern struct dentry *loongarch_debugfs_dir;

I see this one is used for the alignment trap handling, which on other
architectures
is part of sysctl. Try to see what the most common implementation is
across architectures
and use that instead.

I think there also needs to be a discussion about how to handle alignment traps
in general, so maybe split out all alignment handling into a separate patch.

> diff --git a/arch/loongarch/kernel/unaligned.c b/arch/loongarch/kernel/unaligned.c
> new file mode 100644
> index 000000000000..d66e453297da
> --- /dev/null
> +++ b/arch/loongarch/kernel/unaligned.c
> @@ -0,0 +1,461 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Handle unaligned accesses by emulation.

Can you explain in this comment what the CPU can or cannot do? Are all
memory accesses assumed to be naturally aligned? Is any of the CPU
implementation dependent?

        Arnd

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

* Re: [PATCH 07/19] LoongArch: Add process management
  2021-07-06  4:18 ` [PATCH 07/19] LoongArch: Add process management Huacai Chen
@ 2021-07-06 10:16   ` Arnd Bergmann
  2021-07-06 10:57     ` Arnd Bergmann
  2021-07-06 11:09     ` Peter Zijlstra
  0 siblings, 2 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:16 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> +void arch_cpu_idle(void)
> +{
> +       local_irq_enable();
> +       __arch_cpu_idle();
> +}

This looks racy: What happens if an interrupt is pending and hits before
entering __arch_cpu_idle()?

Usually CPU idle instructions are designed to be called with interrupts disabled
and wait until an interrupt happens, including one that was already pending.

           Arnd

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

* Re: [PATCH 08/19] LoongArch: Add memory management
  2021-07-06  4:18 ` [PATCH 08/19] LoongArch: Add memory management Huacai Chen
@ 2021-07-06 10:16   ` Arnd Bergmann
  2021-07-06 10:57     ` Arnd Bergmann
  2021-08-12 11:20     ` Huacai Chen
  2021-08-16  1:57   ` Guo Ren
  1 sibling, 2 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:16 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> diff --git a/arch/loongarch/include/asm/kmalloc.h b/arch/loongarch/include/asm/kmalloc.h
> new file mode 100644
> index 000000000000..b318c41520d8
> --- /dev/null
> +++ b/arch/loongarch/include/asm/kmalloc.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef __ASM_KMALLOC_H
> +#define __ASM_KMALLOC_H
> +
> +#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
> +
> +#endif /* __ASM_KMALLOC_H */

You wrote elsewhere that DMA is cache-coherent, so this should not
be needed at all.

> diff --git a/arch/loongarch/include/asm/shmparam.h b/arch/loongarch/include/asm/shmparam.h
> new file mode 100644
> index 000000000000..f726ac537710
> --- /dev/null
> +++ b/arch/loongarch/include/asm/shmparam.h
> @@ -0,0 +1,12 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef _ASM_SHMPARAM_H
> +#define _ASM_SHMPARAM_H
> +
> +#define __ARCH_FORCE_SHMLBA    1
> +
> +#define        SHMLBA  (4 * PAGE_SIZE)          /* attach addr a multiple of this */
> +
> +#endif /* _ASM_SHMPARAM_H */

I think this needs to be defined in a way that is independent of the configured
page size to minimize the differences between kernel configuration visible to
user space.

Maybe make it always 64KB?

> diff --git a/arch/loongarch/include/asm/sparsemem.h b/arch/loongarch/include/asm/sparsemem.h
> new file mode 100644
> index 000000000000..9b57dc69f523
> --- /dev/null
> +++ b/arch/loongarch/include/asm/sparsemem.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _LOONGARCH_SPARSEMEM_H
> +#define _LOONGARCH_SPARSEMEM_H
> +
> +#ifdef CONFIG_SPARSEMEM
> +
> +/*
> + * SECTION_SIZE_BITS           2^N: how big each section will be
> + * MAX_PHYSMEM_BITS            2^N: how much memory we can have in that space
> + */
> +#define SECTION_SIZE_BITS      29
> +#define MAX_PHYSMEM_BITS       48

Maybe add a comment to explain how you got to '29'?

> +
> +#ifdef CONFIG_PAGE_SIZE_4KB
> +#define PAGE_SHIFT      12
> +#endif
> +#ifdef CONFIG_PAGE_SIZE_16KB
> +#define PAGE_SHIFT      14
> +#endif
> +#ifdef CONFIG_PAGE_SIZE_64KB
> +#define PAGE_SHIFT      16
> +#endif

Shouldn't these be defined in some header?

        Arnd

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

* Re: [PATCH 09/19] LoongArch: Add system call support
  2021-07-06  4:18 ` [PATCH 09/19] LoongArch: Add system call support Huacai Chen
@ 2021-07-06 10:17   ` Arnd Bergmann
  2021-07-06 10:58     ` Arnd Bergmann
  2021-07-07  4:24     ` Huacai Chen
  2021-07-06 13:51   ` Thomas Gleixner
  1 sibling, 2 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:17 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> diff --git a/arch/loongarch/include/asm/seccomp.h b/arch/loongarch/include/asm/seccomp.h
> new file mode 100644
> index 000000000000..220c885f5478
> --- /dev/null
> +++ b/arch/loongarch/include/asm/seccomp.h
> @@ -0,0 +1,9 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef __ASM_SECCOMP_H
> +#define __ASM_SECCOMP_H
> +
> +#include <linux/unistd.h>
> +
> +#include <asm-generic/seccomp.h>
> +
> +#endif /* __ASM_SECCOMP_H */

I would expect this file to not be needed.

> diff --git a/arch/loongarch/include/asm/uaccess.h b/arch/loongarch/include/asm/uaccess.h
> new file mode 100644
> index 000000000000..b760aa0a3bc6
> --- /dev/null
> +++ b/arch/loongarch/include/asm/uaccess.h
> @@ -0,0 +1,453 @@
> + * The fs value determines whether argument validity checking should be
> + * performed or not.  If get_fs() == USER_DS, checking is performed, with
> + * get_fs() == KERNEL_DS, checking is bypassed.
> + *
> + * For historical reasons, these macros are grossly misnamed.
> + */

You removed set_fs()/get_fs(), which is good, but you forgot to remove
the comment.

> diff --git a/arch/loongarch/include/uapi/asm/unistd.h b/arch/loongarch/include/uapi/asm/unistd.h
> new file mode 100644
> index 000000000000..6c194d740ed0
> --- /dev/null
> +++ b/arch/loongarch/include/uapi/asm/unistd.h
> @@ -0,0 +1,7 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +#define __ARCH_WANT_NEW_STAT

Why do you need newstat? I think now that we have statx and the libc
emulation code on top of it, there is probably no need to support both
on the kernel side.

> +#define __ARCH_WANT_SYS_CLONE
> +#define __ARCH_WANT_SYS_CLONE3

Similarly, if you have clone3, you should not need clone.

> +#define __ARCH_WANT_SET_GET_RLIMIT

And here for prlimit64


> +void *sys_call_table[__NR_syscalls] = {
> +       [0 ... __NR_syscalls - 1] = sys_ni_syscall,
> +#include <asm/unistd.h>
> +       __SYSCALL(__NR_clone, __sys_clone)
> +       __SYSCALL(__NR_clone3, __sys_clone3)
> +};

I would suggest expressing this as

#defined sys_clone3 __sys_clone3

instead of overriding the other entries.

          Arnd

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

* Re: [PATCH 10/19] LoongArch: Add signal handling support
  2021-07-06  4:18 ` [PATCH 10/19] LoongArch: Add signal handling support Huacai Chen
@ 2021-07-06 10:17   ` Arnd Bergmann
  2021-07-06 10:59     ` Arnd Bergmann
  2021-07-08 13:04     ` Huacai Chen
  2021-08-26 16:43   ` Xi Ruoyao
  1 sibling, 2 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:17 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang, Eric W. Biederman

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
>
> This patch adds signal handling support for LoongArch.
>
> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>

Eric Biederman should review this part as well.

> --- /dev/null
> +++ b/arch/loongarch/include/asm/sigcontext.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef _ASM_SIGCONTEXT_H
> +#define _ASM_SIGCONTEXT_H
> +
> +#include <uapi/asm/sigcontext.h>
> +
> +#endif /* _ASM_SIGCONTEXT_H */

Remove this file

> + */
> +#ifndef _UAPI_ASM_SIGINFO_H
> +#define _UAPI_ASM_SIGINFO_H
> +
> +#if _LOONGARCH_SZLONG == 32
> +#define __ARCH_SI_PREAMBLE_SIZE (3 * sizeof(int))
> +#else
> +#define __ARCH_SI_PREAMBLE_SIZE (4 * sizeof(int))
> +#endif

These are no longer used.

> +
> +#ifndef _NSIG
> +#define _NSIG          128
> +#endif

Everything else uses 64 here, except for MIPS.

> +#define _NSIG_BPW      __BITS_PER_LONG
> +#define _NSIG_WORDS    (_NSIG / _NSIG_BPW)
> +
> +#define SIGHUP          1
> +#define SIGINT          2
> +#define SIGQUIT                 3
> +#define SIGILL          4
> +#define SIGTRAP                 5
> +#define SIGABRT                 6
> +#define SIGIOT          6
> +#define SIGBUS          7
> +#define SIGFPE          8
> +#define SIGKILL                 9
> +#define SIGUSR1                10
> +#define SIGSEGV                11
> +#define SIGUSR2                12
> +#define SIGPIPE                13
> +#define SIGALRM                14
> +#define SIGTERM                15
> +#define SIGSTKFLT      16
> +#define SIGCHLD                17
> +#define SIGCONT                18
> +#define SIGSTOP                19
> +#define SIGTSTP                20
> +#define SIGTTIN                21
> +#define SIGTTOU                22
> +#define SIGURG         23
> +#define SIGXCPU                24
> +#define SIGXFSZ                25
> +#define SIGVTALRM      26
> +#define SIGPROF                27
> +#define SIGWINCH       28
> +#define SIGIO          29
> +#define SIGPOLL                SIGIO
> +#define SIGPWR         30
> +#define SIGSYS         31
> +#define        SIGUNUSED       31

Please try to use the asm-generic version of these definitions instead
copying them. If you need something different, you can add an #ifdef there,
and then we can discuss whether the difference makes sense.


        Arnd

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

* Re: [PATCH 12/19] LoongArch: Add misc common routines
  2021-07-06  4:18 ` [PATCH 12/19] LoongArch: Add misc common routines Huacai Chen
@ 2021-07-06 10:17   ` Arnd Bergmann
  2021-07-06 11:00     ` Arnd Bergmann
  2021-07-23 10:41     ` Huacai Chen
  0 siblings, 2 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:17 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> +
> +#ifdef ARCH_HAS_USABLE_BUILTIN_POPCOUNT
> +
> +#include <asm/types.h>
> +
> +static inline unsigned int __arch_hweight32(unsigned int w)
> +{
> +       return __builtin_popcount(w);
> +}

This looks like you incorrect copied it from MIPS: For a new architecture,
you should know whether __builtin_popcount is usable or not.

> +static inline unsigned long __xchg(volatile void *ptr, unsigned long x,
> +                                  int size)
> +{
> +       switch (size) {
> +       case 1:
> +       case 2:
> +               return __xchg_small(ptr, x, size);

If there is no native sub-word xchg(), then better make this BUILD_BUG(),
see the riscv implementation.

> +
> +static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
> +                                     unsigned long new, unsigned int size)
> +{
> +       switch (size) {
> +       case 1:
> +       case 2:
> +               return __cmpxchg_small(ptr, old, new, size);

Same here.

> +++ b/arch/loongarch/include/asm/fb.h
> @@ -0,0 +1,23 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef _ASM_FB_H_
> +#define _ASM_FB_H_
> +
> +#include <linux/fb.h>
> +#include <linux/fs.h>
> +#include <asm/page.h>
> +
> +static inline void fb_pgprotect(struct file *file, struct vm_area_struct *vma,
> +                               unsigned long off)
> +{
> +       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
> +}

Do you have a writethrough or write-combine map type? noncached makes
this slower than necessary.
> +/*
> + * On LoongArch I/O ports are memory mapped, so we access them using normal
> + * load/store instructions. loongarch_io_port_base is the virtual address to
> + * which all ports are being mapped.  For sake of efficiency some code
> + * assumes that this is an address that can be loaded with a single lui
> + * instruction, so the lower 16 bits must be zero. Should be true on any
> + * sane architecture; generic code does not use this assumption.
> + */
> +extern unsigned long loongarch_io_port_base;
> +
> +static inline void set_io_port_base(unsigned long base)
> +{
> +       loongarch_io_port_base = base;
> +}

If you are able to map this to a fixed virtual address (in fixmap or elsewhere),
you can just use the asm-generic version.

> +/*
> + * ISA I/O bus memory addresses are 1:1 with the physical address.
> + */
> +static inline unsigned long isa_virt_to_bus(volatile void *address)
> +{
> +       return virt_to_phys(address);
> +}
> +
> +static inline void *isa_bus_to_virt(unsigned long address)
> +{
> +       return phys_to_virt(address);
> +}
> +/*
> + * However PCI ones are not necessarily 1:1 and therefore these interfaces
> + * are forbidden in portable PCI drivers.
> + *
> + * Allow them for x86 for legacy drivers, though.
> + */
> +#define virt_to_bus virt_to_phys
> +#define bus_to_virt phys_to_virt

As mentioned in another patch, these should not exist on new architectures.

> +
> +static inline void __iomem *ioremap_prot(phys_addr_t offset, unsigned long size,
> +       unsigned long prot_val)
> +{
> +       /* This only works for !HIGHMEM currently */

Do you support highmem? I would expect new architectures to no longer
implement that. Just use a 64-bit kernel on systems with lots of ram.

> +#define ioremap(offset, size)                                  \
> +       ioremap_prot((offset), (size), _CACHE_SUC)
> +#define ioremap_uc ioremap

Remove ioremap_uc(), it should never be called here.

> +/*
> + * ioremap_wc     -   map bus memory into CPU space
> + * @offset:    bus address of the memory
> + * @size:      size of the resource to map
> + *
> + * ioremap_wc performs a platform specific sequence of operations to
> + * make bus memory CPU accessible via the readb/readw/readl/writeb/
> + * writew/writel functions and the other mmio helpers. The returned
> + * address is not guaranteed to be usable directly as a virtual
> + * address.
> + *
> + * This version of ioremap ensures that the memory is marked uncachable
> + * but accelerated by means of write-combining feature. It is specifically
> + * useful for PCIe prefetchable windows, which may vastly improve a
> + * communications performance. If it was determined on boot stage, what
> + * CPU CCA doesn't support WUC, the method shall fall-back to the
> + * _CACHE_SUC option (see cpu_probe() method).
> + */
> +#define ioremap_wc(offset, size)                               \
> +       ioremap_prot((offset), (size), boot_cpu_data.writecombine)

It seems this is all copied from MIPS again. Are you sure you need to support
both versions with a runtime conditional?

> +#define __BUILD_MEMORY_SINGLE(pfx, bwlq, type)                         \
> +                                                                       \
> +static inline void pfx##write##bwlq(type val,                          \
> +                                   volatile void __iomem *mem)         \
> +{                                                                      \

Please don't add another copy of these macros. Use the version from
include/asm-generic, or modify it as needed if it doesn't quite work.

> diff --git a/arch/loongarch/include/asm/vga.h b/arch/loongarch/include/asm/vga.h
> new file mode 100644
> index 000000000000..eef95f2f837a
> --- /dev/null
> +++ b/arch/loongarch/include/asm/vga.h
> @@ -0,0 +1,56 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Access to VGA videoram
> + *
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */

I think it would be better not to support VGA console, but to use the EFI
framebuffer.


> diff --git a/arch/loongarch/kernel/cmpxchg.c b/arch/loongarch/kernel/cmpxchg.c
> new file mode 100644
> index 000000000000..30f9f1ee4f0a
> --- /dev/null
> +++ b/arch/loongarch/kernel/cmpxchg.c
> @@ -0,0 +1,100 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Author: Huacai Chen <chenhuacai@loongson.cn>
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/bitops.h>
> +#include <asm/cmpxchg.h>
> +
> +unsigned long __xchg_small(volatile void *ptr, unsigned long val, unsigned int size)
> +{

This file should not be there, I think you just copied it from the MIPS version.

Which brings me to an important point: anything you copied from elsewhere
is clearly not copyrighted by Loongson. I think you need to go through each
file and update the copyright and author statements to reflect who actually
wrote the code. At least you won't have to update this file if you remove it ;-)


       Arnd

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

* Re: [PATCH 13/19] LoongArch: Add some library functions
  2021-07-06  4:18 ` [PATCH 13/19] LoongArch: Add some library functions Huacai Chen
@ 2021-07-06 10:17   ` Arnd Bergmann
  2021-07-06 11:00     ` Arnd Bergmann
  2021-08-12 11:22     ` Huacai Chen
  0 siblings, 2 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:17 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
>
> +/*
> + * long __strncpy_from_user(char *to, const char *from, long len)
> + *
> + * a0: to
> + * a1: from
> + * a2: len
> + */
> +SYM_FUNC_START(__strncpy_from_user)
> +       move    a3, zero
> +

I doubt this is better than the C version in lib/strncpy_from_user.c

> diff --git a/arch/loongarch/lib/strnlen_user.S b/arch/loongarch/lib/strnlen_user.S
> new file mode 100644
> index 000000000000..9288a5ad294e
> --- /dev/null
> +++ b/arch/loongarch/lib/strnlen_user.S
> @@ -0,0 +1,47 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */

Same here.

Have you done any measurement to show that the asm version actually helps
here? If not, just use the generic version.


       Arnd

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

* Re: [PATCH 15/19] LoongArch: Add PCI controller support
  2021-07-06  4:18 ` [PATCH 15/19] LoongArch: Add PCI controller support Huacai Chen
@ 2021-07-06 10:17   ` Arnd Bergmann
  2021-07-06 11:01     ` Arnd Bergmann
  2021-08-12 11:29     ` Huacai Chen
  0 siblings, 2 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:17 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
>
> Loongson64 based systems are PC-like systems which use PCI/PCIe as its
> I/O bus, This patch adds the PCI host controller support for LoongArch.
>
> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
> ---
>  arch/loongarch/include/asm/pci.h  | 124 ++++++++++++++++++
>  arch/loongarch/pci/acpi.c         | 194 ++++++++++++++++++++++++++++
>  arch/loongarch/pci/mmconfig.c     | 105 +++++++++++++++
>  arch/loongarch/pci/pci-loongson.c | 130 +++++++++++++++++++
>  arch/loongarch/pci/pci.c          | 207 ++++++++++++++++++++++++++++++

PCI controller support should generally live in drivers/pci/controller/ and
get reviewed by the subsystem maintainers.

> +/*
> + * This file essentially defines the interface between board specific
> + * PCI code and LoongArch common PCI code. Should potentially put into
> + * include/asm/pci.h file.
> + */
> +
> +#include <linux/ioport.h>
> +#include <linux/list.h>
> +
> +extern const struct pci_ops *__read_mostly loongarch_pci_ops;

There is already an abstraction for this in the common code, don't add another.

> +/*
> + * Each pci channel is a top-level PCI bus seem by CPU.         A machine  with
> + * multiple PCI channels may have multiple PCI host controllers or a
> + * single controller supporting multiple channels.
> + */
> +struct pci_controller {
> +       struct list_head list;
> +       struct pci_bus *bus;
> +       struct device_node *of_node;
> +
> +       struct pci_ops *pci_ops;
> +       struct resource *mem_resource;
> +       unsigned long mem_offset;
> +       struct resource *io_resource;
> +       unsigned long io_offset;
> +       unsigned long io_map_base;
> +       struct resource *busn_resource;
> +
> +       unsigned int node;
> +       unsigned int index;
> +       unsigned int need_domain_info;
> +#ifdef CONFIG_ACPI
> +       struct acpi_device *companion;
> +#endif
> +       phys_addr_t mcfg_addr;
> +};
> +
> +extern void pcibios_add_root_resources(struct list_head *resources);
> +
> +extern phys_addr_t mcfg_addr_init(int domain);
> +
> +#ifdef CONFIG_PCI_DOMAINS
> +static inline void set_pci_need_domain_info(struct pci_controller *hose,
> +                                           int need_domain_info)
> +{
> +       hose->need_domain_info = need_domain_info;
> +}
> +#endif /* CONFIG_PCI_DOMAINS */

Just use PCI_DOMAINS unconditionally

> +
> +/*
> + * Can be used to override the logic in pci_scan_bus for skipping
> + * already-configured bus numbers - to be used for buggy BIOSes
> + * or architectures with incomplete PCI setup by the loader
> + */
> +static inline unsigned int pcibios_assign_all_busses(void)
> +{
> +       return 1;
> +}

Since you use ACPI, the BIOS should be responsible for assigning the
buses, otherwise the ACPI data may be mismatched with the PCI
device locations that the kernel sees.

> +#define PCIBIOS_MIN_IO         0

I think this means PCI devices can reuse ports that are reserved
for ISA devices. Since you claim to support ISA, I think this should
be 0x1000

> +
> +int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn,
> +                                               int reg, int len, u32 *val)
> +{
> +       struct pci_bus *bus_tmp = pci_find_bus(domain, bus);
> +
> +       if (bus_tmp)
> +               return bus_tmp->ops->read(bus_tmp, devfn, reg, len, val);
> +       return -EINVAL;
> +}
> +
> +int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,
> +                                               int reg, int len, u32 val)
> +{
> +       struct pci_bus *bus_tmp = pci_find_bus(domain, bus);
> +
> +       if (bus_tmp)
> +               return bus_tmp->ops->write(bus_tmp, devfn, reg, len, val);
> +       return -EINVAL;
> +}

This looks like you copied from arch/arm64. I think the code really
needs to be generalized more. Maybe move the arm64 implementation
to drivers/acpi/ so it can be shared with loongarch?

> +/*
> + * We need to avoid collisions with `mirrored' VGA ports
> + * and other strange ISA hardware, so we always want the
> + * addresses to be allocated in the 0x000-0x0ff region
> + * modulo 0x400.
> + *
> + * Why? Because some silly external IO cards only decode
> + * the low 10 bits of the IO address. The 0x00-0xff region
> + * is reserved for motherboard devices that decode all 16
> + * bits, so it's ok to allocate at, say, 0x2800-0x28ff,
> + * but we want to try to avoid allocating at 0x2900-0x2bff
> + * which might have be mirrored at 0x0100-0x03ff..
> + */
> +resource_size_t
> +pcibios_align_resource(void *data, const struct resource *res,
> +                      resource_size_t size, resource_size_t align)
> +{
> +       struct pci_dev *dev = data;
> +       struct pci_controller *hose = dev->sysdata;
> +       resource_size_t start = res->start;
> +
> +       if (res->flags & IORESOURCE_IO) {
> +               /* Make sure we start at our min on all hoses */
> +               if (start < PCIBIOS_MIN_IO + hose->io_resource->start)
> +                       start = PCIBIOS_MIN_IO + hose->io_resource->start;
> +
> +               /*
> +                * Put everything into 0x00-0xff region modulo 0x400
> +                */
> +               if (start & 0x300)
> +                       start = (start + 0x3ff) & ~0x3ff;
> +       } else if (res->flags & IORESOURCE_MEM) {
> +               /* Make sure we start at our min on all hoses */
> +               if (start < PCIBIOS_MIN_MEM)
> +                       start = PCIBIOS_MIN_MEM;
> +       }
> +
> +       return start;
> +}

Same here, please don't add another copy of this function.


       Arnd

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

* Re: [PATCH 16/19] LoongArch: Add VDSO and VSYSCALL support
  2021-07-06  4:18 ` [PATCH 16/19] LoongArch: Add VDSO and VSYSCALL support Huacai Chen
@ 2021-07-06 10:17   ` Arnd Bergmann
  2021-07-06 11:02     ` Arnd Bergmann
  2021-08-12 11:31     ` Huacai Chen
  0 siblings, 2 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:17 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
>
> This patch adds VDSO and VSYSCALL support (gettimeofday and its friends)
> for LoongArch.
>
> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
> ---
>  arch/loongarch/include/asm/vdso.h             |  50 +++
>  arch/loongarch/include/asm/vdso/clocksource.h |   8 +
>  .../loongarch/include/asm/vdso/gettimeofday.h | 101 ++++++
>  arch/loongarch/include/asm/vdso/processor.h   |  14 +
>  arch/loongarch/include/asm/vdso/vdso.h        |  42 +++
>  arch/loongarch/include/asm/vdso/vsyscall.h    |  27 ++
>  arch/loongarch/kernel/vdso.c                  | 132 ++++++++
>  arch/loongarch/vdso/Makefile                  |  97 ++++++
>  arch/loongarch/vdso/elf.S                     |  15 +
>  arch/loongarch/vdso/genvdso.c                 | 311 ++++++++++++++++++
>  arch/loongarch/vdso/genvdso.h                 | 122 +++++++
>  arch/loongarch/vdso/sigreturn.S               |  24 ++
>  arch/loongarch/vdso/vdso.lds.S                |  65 ++++
>  arch/loongarch/vdso/vgettimeofday.c           |  26 ++

I fear you may have copied the wrong one here, the MIPS implementation seems
more complex than the rv64 or arm64 versions, and you should not need that
complexity.

Can you try removing the genvdso.c  file completely? To be honest I don't
see why this is needed here, so I may be missing something, but the other
ones don't have it either.


       Arnd

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

* Re: [PATCH 17/19] LoongArch: Add multi-processor (SMP) support
  2021-07-06  4:18 ` [PATCH 17/19] LoongArch: Add multi-processor (SMP) support Huacai Chen
@ 2021-07-06 10:17   ` Arnd Bergmann
  2021-07-06 11:03     ` Arnd Bergmann
  2021-07-06 11:32   ` Peter Zijlstra
                     ` (3 subsequent siblings)
  4 siblings, 1 reply; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:17 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> +const struct plat_smp_ops loongson3_smp_ops = {
> +       .send_ipi_single = loongson3_send_ipi_single,
> +       .send_ipi_mask = loongson3_send_ipi_mask,
> +       .smp_setup = loongson3_smp_setup,
> +       .prepare_cpus = loongson3_prepare_cpus,
> +       .boot_secondary = loongson3_boot_secondary,
> +       .init_secondary = loongson3_init_secondary,
> +       .smp_finish = loongson3_smp_finish,
> +#ifdef CONFIG_HOTPLUG_CPU
> +       .cpu_disable = loongson3_cpu_disable,
> +       .cpu_die = loongson3_cpu_die,
> +#endif
> +};

I would hope that these functions can be used across platforms without
an abstraction
layer in-between. Are these not all part of the either the CPU
architecture definition or
the firmware interface?

Do you even expect to see non-SMP systems deployed widely enough that
SMP support
must be optional? On arch/arm64 we ended up always building SMP support into
the kernel because practically everyone wants it.


      Arnd

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

* Re: [PATCH 18/19] LoongArch: Add Non-Uniform Memory Access (NUMA) support
  2021-07-06  4:18 ` [PATCH 18/19] LoongArch: Add Non-Uniform Memory Access (NUMA) support Huacai Chen
@ 2021-07-06 10:18   ` Arnd Bergmann
  2021-07-06 11:03     ` Arnd Bergmann
  2021-08-12 11:46     ` Huacai Chen
  0 siblings, 2 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:18 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> +#ifdef CONFIG_SWIOTLB
> +/*
> + * Optional platform hook to call swiotlb_setup().
> + */
> +extern void plat_swiotlb_setup(void);
> +
> +#else
> +
> +static inline void plat_swiotlb_setup(void) {}
> +
> +#endif /* CONFIG_SWIOTLB */

I guess this accidentally slipped into the wrong patch? It doesn't appear to be
NUMA related.

> diff --git a/arch/loongarch/loongson64/dma.c b/arch/loongarch/loongson64/dma.c
> new file mode 100644
> index 000000000000..f259f70c75fa
> --- /dev/null
> +++ b/arch/loongarch/loongson64/dma.c
> @@ -0,0 +1,59 @@
> +// SPDX-License-Identifier: GPL-2.0

Same for this file.


         Arnd

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

* Re: [PATCH 19/19] LoongArch: Add Loongson-3 default config file
  2021-07-06  4:18 ` [PATCH 19/19] LoongArch: Add Loongson-3 default config file Huacai Chen
@ 2021-07-06 10:18   ` Arnd Bergmann
  2021-07-06 11:04     ` Arnd Bergmann
  2021-08-12 11:58     ` Huacai Chen
  0 siblings, 2 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:18 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> +CONFIG_EMBEDDED=y

You probably should not set this one.

> +CONFIG_CAIF=y

And almost certainly not this one.

> +CONFIG_PCCARD=m
> +CONFIG_YENTA=m
> +CONFIG_RAPIDIO=y
> +CONFIG_RAPIDIO_TSI721=y

These seem unlikely as well. Can you confirm that you have both old-style
PC-Card support and RAPIDIO support on your most common hardware?

> +CONFIG_PARPORT=y
> +CONFIG_PARPORT_PC=y
> +CONFIG_PARPORT_SERIAL=y
> +CONFIG_PARPORT_PC_FIFO=y
> +CONFIG_PRINTER=m

Is this an on-board PCI device you actually have?
What do you connect to the PC parport? Most printers
in the past 15 years only have USB or network connections.

> +CONFIG_8139CP=m
> +CONFIG_8139TOO=m

Do you actually support legacy PCI slots?

> +CONFIG_SERIAL_8250=y
> +CONFIG_SERIAL_8250_CONSOLE=y
> +CONFIG_SERIAL_8250_NR_UARTS=16
> +CONFIG_SERIAL_8250_RUNTIME_UARTS=16
> +CONFIG_SERIAL_8250_EXTENDED=y
> +CONFIG_SERIAL_8250_MANY_PORTS=y
> +CONFIG_SERIAL_8250_SHARE_IRQ=y
> +CONFIG_SERIAL_8250_RSA=y
> +CONFIG_SERIAL_OF_PLATFORM=y

I don't see any device tree support in your patches, so I think the
SERIAL_OF_PLATFORM is not needed.

> +CONFIG_RAW_DRIVER=m

This is gone now.

> +CONFIG_INFINIBAND=m

You don't seem to enable any infiniband host drivers, so the core
layer can probably remain turned off

> +CONFIG_RTC_CLASS=y
> +CONFIG_RTC_DRV_EFI=y
> +CONFIG_UIO=m

same for UIO.

> +CONFIG_EXT2_FS=y
> +CONFIG_EXT2_FS_XATTR=y
> +CONFIG_EXT2_FS_POSIX_ACL=y
> +CONFIG_EXT2_FS_SECURITY=y
> +CONFIG_EXT3_FS=y
> +CONFIG_EXT3_FS_POSIX_ACL=y
> +CONFIG_EXT3_FS_SECURITY=y

I would generally recommend using EXT4 over EXT2 or EXT3

> +CONFIG_FRAME_WARN=1024

On 64-bit platforms, you probably want to increase this a bit,
otherwise you get extra warnings about code that works as
intended. The default is 2048, but you should be able to get by
with 1280, if that lets you build a defconfig kernel without warnings.


      Arnd

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

* Re: [PATCH 00/19] arch: Add basic LoongArch support
  2021-07-06  4:18 [PATCH 00/19] arch: Add basic LoongArch support Huacai Chen
                   ` (18 preceding siblings ...)
  2021-07-06 10:11 ` [PATCH 00/19] arch: Add basic LoongArch support Arnd Bergmann
@ 2021-07-06 10:33 ` Arnd Bergmann
       [not found] ` <20210706041820.1536502-5-chenhuacai@loongson.cn>
  20 siblings, 0 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:33 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang, Chen Zhu

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
>
> LoongArch is a new RISC ISA, which is a bit like MIPS or RISC-V.
> LoongArch includes a reduced 32-bit version (LA32R), a standard 32-bit
> version (LA32S) and a 64-bit version (LA64). LoongArch use ACPI as its
> boot protocol LoongArch-specific interrupt controllers (similar to APIC)
> are already added in the next revision of ACPI Specification (current
> revision is 6.4).
>
> This patchset is adding basic LoongArch support in mainline kernel, we
> can see a complete snapshot here:
> https://github.com/loongson/linux/tree/loongarch-next
>
> Cross-compile tool chain to build kernel:
> https://github.com/loongson/build-tools/releases
>
> Loongson and LoongArch documentations:
> https://github.com/loongson/LoongArch-Documentation
>
> LoongArch-specific interrupt controllers:
> https://mantis.uefi.org/mantis/view.php?id=2203

(sorry for sending duplicate emails, it appears that the provide of my
arnd@arndb.de address gets spam-filtered once more, so sending all my
replies again from arnd@kernel.org)

Thanks a lot for your submission, I've been waiting for this, and have just
completed an initial quick review, will send out the individual replies in a
bit.

I have a few high-level comments about things that need to be improved
before merging:

1. Copyright statements: I can see that you copied a lot of code from arch/mips,
 and then a bit from arch/arm64 and possibly arch/riscv/, but every file just
 lists loongson as the copyright holder and one or more employees as authors.
 This clearly has to be resolved by listing the original authors as well.

2. Reducing the amount of copied code: my impression is that the bits you
   wrote yourself are generally of higher quality than the bits you copied from
   mips, usually because those have grown over a long time to accommodate
   cases you should not worry about. In cases where there was no generic
   implementation, we should try to come up with a version that can be shared
   instead of adding another copy.

3. 32-bit support: There are small bits of code for 32-bit support everywhere
   throughout the code base, but there is also code that does not look like
   it would work in that configuration. I would suggest you split out all 32-bit
   code into a separate patch, the way you have done for SMP and NUMA
   support. That way it can be reviewed separately, or deferred until when
   you actually add support for a 32-bit platform. Please also clarify which
   modes you plan to support: should every 64-bit kernel be able to run
   LA32R and LA32S user space binaries, or do you require running the same
   instruction set in both kernel and user space as RISC-V does?
   Do you plan to have MIPS N32 style user space on 64-bit kernels?

4. Toolchain sources: I see you only have binaries for an older gcc-8.3
    release and no sources so far. I assume you are already busy porting
    the internal patches to the latest gcc and will post that soon, but my
    feeling is that the kernel port should not get merged until we have a
    confirmation from the toolchain maintainers that they plan to merge
    support for their following release. We should however make sure that
    your kernel port is completely reviewed and can just get merged once
    we get there.

5. Platform support: You have copied the underlying logic from arch/mips,
    but that still uses a method where most platforms (not the new
    "generic" version) are mutually exclusive. Since you only support
    one platform right now, it would be best to just simplify it to the point
    where no platform specific code is needed in arch/loongarch/ and
    it just works. If you add 32-bit support later on, that obviously requires
    making a choice between two or three incompatible options.

        Arnd

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

* Re: [PATCH 03/19] LoongArch: Add build infrastructure
  2021-07-06  4:18 ` [PATCH 03/19] LoongArch: Add build infrastructure Huacai Chen
  2021-07-06 10:12   ` Arnd Bergmann
@ 2021-07-06 10:35   ` Arnd Bergmann
  2021-07-07  0:00   ` Randy Dunlap
  2 siblings, 0 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:35 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> --- /dev/null
> +++ b/arch/loongarch/Kbuild
> @@ -0,0 +1,21 @@
> +# Fail on warnings - also for files referenced in subdirs
> +# -Werror can be disabled for specific files using:
> +# CFLAGS_<file.o> := -Wno-error
> +ifeq ($(W),)
> +subdir-ccflags-y := -Werror
> +endif

This tends to break building with 'make W=12' or similar, I would recommend not
adding -Werror. It is a good idea though to have your CI systems build
with -Werror enabled on the command line though.

> +# platform specific definitions
> +include arch/loongarch/Kbuild.platforms
> +obj-y := $(platform-y)
> +

I would recommend not planning on having platform specific files. The way
we do it on modern platforms (arm64, rv64) is to have all the device drivers
in the driver subsystems instead.

> +       select GENERIC_IOMAP

GENERIC_IOMAP is one of those you probably don't want here. If PCI I/O
space is mapped into MMIO registers, just do what arm64 has and make
ioport_map() return a pointer, then make ioread32() an alias for readl().

> +       select HAVE_CBPF_JIT if !64BIT
> +       select HAVE_EBPF_JIT if 64BIT

These look like you incorrectly copied them from MIPS. I don't see any EBPF_JIT
implemementation (I may have missed that, as I'm still starting), but
you probably
don't want the CBPF version for 32-bit anyway.

> +       select HAVE_IDE

drivers/ide/ was just removed,so this line can go as well.

> +       select VIRT_TO_BUS

Remove this: if you still require a driver that fails to use the dma-mapping
API, fix the driver instead so it can work with IOMMUs.

> +menu "Machine selection"
> +
> +choice
> +       prompt "System type"
> +       default MACH_LOONGSON64
> +
> +config MACH_LOONGSON64
> +       bool "Loongson 64-bit family of machines"

There should generally not be a 'choice' menu to select between
different machine types, but instead the kernel should be able
to run on all platforms that have the same instruction set.

Maybe just make this a silent option and use 'select
MACH_LOONGSON64' so you can pick up the device drivers
that mips uses.

> +       select ARCH_MIGHT_HAVE_PC_PARPORT
> +       select ARCH_MIGHT_HAVE_PC_SERIO
> +       select GENERIC_ISA_DMA
> +       select ISA

Do you actually have ISA-style add-on cards? If not, then
remove the ISA options. If you do, which drivers are they?


> +config CPU_LOONGSON64
> +       bool "Loongson 64-bit CPU"
> +       depends on SYS_HAS_CPU_LOONGSON64
> +       select CPU_SUPPORTS_64BIT_KERNEL
> +       select GPIOLIB
> +       select SWIOTLB
> +       select ARCH_SUPPORTS_ATOMIC_RMW
> +       help
> +         The Loongson 64-bit processor implements the LoongArch64 (the 64-bit
> +         version of LoongArch) instruction set.
> +
> +endchoice
> +
> +config SYS_HAS_CPU_LOONGSON64
> +       bool
> +
> +endmenu
> +
> +config SYS_SUPPORTS_32BIT_KERNEL
> +       bool
> +config SYS_SUPPORTS_64BIT_KERNEL
> +       bool
> +config CPU_SUPPORTS_32BIT_KERNEL
> +       bool
> +config CPU_SUPPORTS_64BIT_KERNEL
> +       bool

Same for the CPUs: I would suggest you keep this simple until you get
to the point of actually having multiple CPUs that you need to distinguish.

Different 64-bit CPUs are hopefully going to be compatible with one
another, so they should not be listed as mutually exclusive in a
'choice' statement. The complexity with two levels of 32/64 support
is probably not going to be helpful here either.

The 'select' statements that you have under CPU_LOONGSON64
ook like they should just be in the top level.

GPIOLIB and SWIOTLB could just be left user selectable, if turning
them off results only in run-time loss of functionality but not a
build failure.

> +menu "Kernel type"
> +
> +choice
> +       prompt "Kernel code model"
> +       help
> +         You should only select this option if you have a workload that
> +         actually benefits from 64-bit processing or if your machine has
> +         large memory.  You will only be presented a single option in this
> +         menu if your system does not support both 32-bit and 64-bit kernels.
> +
> +config 32BIT
> +       bool "32-bit kernel"
> +       depends on CPU_SUPPORTS_32BIT_KERNEL && SYS_SUPPORTS_32BIT_KERNEL
> +       help
> +         Select this option if you want to build a 32-bit kernel.
> +
> +config 64BIT
> +       bool "64-bit kernel"
> +       depends on CPU_SUPPORTS_64BIT_KERNEL && SYS_SUPPORTS_64BIT_KERNEL
> +       help
> +         Select this option if you want to build a 64-bit kernel.
> +
> +endchoice

Since you don't support any 32-bit target at the moment, just make CONFIG_64BIT
a default-on statement, and make it user-selectable only when you add a 32-bit
target.


> +choice
> +       prompt "Kernel page size"
> +       default PAGE_SIZE_16KB
> +
> +config PAGE_SIZE_4KB
> +       bool "4kB"
> +       help
> +         This option select the standard 4kB Linux page size.
> +
> +config PAGE_SIZE_16KB
> +       bool "16kB"
> +       help
> +         This option select the standard 16kB Linux page size.
> +
> +config PAGE_SIZE_64KB
> +       bool "64kB"
> +       help
> +         This option select the standard 64kB Linux page size.
> +
> +endchoice
> +
> +choice
> +       prompt "Virtual memory address space bits"
> +       default VA_BITS_40
> +       help
> +         Allows choosing one of multiple possible virtual memory
> +         address space bits for applications. The level of page
> +         translation table is determined by a combination of page
> +         size and virtual memory address space bits.
> +
> +config VA_BITS_40
> +       bool "40-bits"
> +       depends on 64BIT
> +       help
> +         Support a maximum at least 40 bits of application virtual memory.
> +
> +config VA_BITS_48
> +       bool "48-bits"
> +       depends on 64BIT
> +       help
> +         Support a maximum at least 48 bits of application virtual memory.
> +
> +endchoice

Isn't the size of the address space tied to the page size?

         Arnd

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

* Re: [PATCH 04/19] LoongArch: Add common headers
       [not found] ` <20210706041820.1536502-5-chenhuacai@loongson.cn>
  2021-07-06 10:16   ` [PATCH 04/19] LoongArch: Add common headers Arnd Bergmann
@ 2021-07-06 10:54   ` Arnd Bergmann
  2021-07-06 10:57   ` Peter Zijlstra
                     ` (2 subsequent siblings)
  4 siblings, 0 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:54 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

 On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
> This patch adds common headers for basic LoongArch support.
>

This patch is really too long to properly review, as it puts lots of
unrelated headers into one commit. Please split it up into logical
chunks, ideally together with the corresponding C files. Some obvious
categories are:

- memory management
- atomics and locking
- CPU register specifics

> --- /dev/null
> +++ b/arch/loongarch/include/asm/abi.h
> @@ -0,0 +1,33 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef _ASM_ABI_H
> +#define _ASM_ABI_H
> +
> +#include <linux/signal_types.h>
> +
> +#include <asm/signal.h>
> +#include <asm/siginfo.h>
> +#include <asm/vdso.h>
> +
> +struct loongarch_abi {
> +       int (* const setup_frame)(void *sig_return, struct ksignal *ksig,
> +                                 struct pt_regs *regs, sigset_t *set);
> +       int (* const setup_rt_frame)(void *sig_return, struct ksignal *ksig,
> +                                    struct pt_regs *regs, sigset_t *set);

I can see you copied this from mips, but I don't see why you would need it here.
Can you just call the functions directly?

> +/*
> + * Return the bit position (0..63) of the most significant 1 bit in a word
> + * Returns -1 if no 1 bit exists
> + */
> +static inline unsigned long __fls(unsigned long word)
> +{

Can you use the compiler builtins here? Since you know that you
have a modern compiler, you can probably expect it to produce better
output than the open-coded inline.

> diff --git a/arch/loongarch/include/asm/bootinfo.h b/arch/loongarch/include/asm/bootinfo.h
> new file mode 100644
> index 000000000000..aa9915a56f66
> --- /dev/null
> +++ b/arch/loongarch/include/asm/bootinfo.h
> @@ -0,0 +1,38 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef _ASM_BOOTINFO_H
> +#define _ASM_BOOTINFO_H
> +
> +#include <linux/types.h>
> +#include <asm/setup.h>
> +
> +const char *get_system_type(void);
> +
> +extern void early_memblock_init(void);
> +extern void detect_memory_region(phys_addr_t start, phys_addr_t sz_min,  phys_addr_t sz_max);
> +
> +extern void early_init(void);
> +extern void platform_init(void);
> +
> +extern void free_init_pages(const char *what, unsigned long begin, unsigned long end);
> +
> +/*
> + * Initial kernel command line, usually setup by fw_init_cmdline()
> + */
> +extern char arcs_cmdline[COMMAND_LINE_SIZE];
> +
> +/*
> + * Registers a0, a1, a3 and a4 as passed to the kernel entry by firmware
> + */
> +extern unsigned long fw_arg0, fw_arg1, fw_arg2, fw_arg3;
> +
> +extern unsigned long initrd_start, initrd_end;
> +
> +/*
> + * Platform memory detection hook called by setup_arch
> + */
> +extern void plat_mem_setup(void);

I think the platform specific options should all be removed and replaced
with  a well-defined firmware interface, so remove get_system_type()/
platform_init()/plat_mem_setup() etc.

> +
> +static inline void check_bugs(void)
> +{
> +       unsigned int cpu = smp_processor_id();
> +
> +       cpu_data[cpu].udelay_val = loops_per_jiffy;
> +}

This needs a comment to explain what bug you are working around.

> diff --git a/arch/loongarch/include/asm/dma.h b/arch/loongarch/include/asm/dma.h
> new file mode 100644
> index 000000000000..a8a58dc93422
> --- /dev/null
> +++ b/arch/loongarch/include/asm/dma.h
> @@ -0,0 +1,13 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef __ASM_DMA_H
> +#define __ASM_DMA_H
> +
> +#define MAX_DMA_ADDRESS        PAGE_OFFSET
> +#define MAX_DMA32_PFN  (1UL << (32 - PAGE_SHIFT))
> +
> +extern int isa_dma_bridge_buggy;
> +
> +#endif

Can you send a cleanup patch for this? I think we should get rid of the
isa_dma_bridge_buggy variable for architectures that do not have a VIA
VP2 style ISA bridge.

> diff --git a/arch/loongarch/include/asm/dmi.h b/arch/loongarch/include/asm/dmi.h
> new file mode 100644
> index 000000000000..0fcfbba93363
> --- /dev/null
> +++ b/arch/loongarch/include/asm/dmi.h
> @@ -0,0 +1,26 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef _ASM_DMI_H
> +#define _ASM_DMI_H
> +
> +#include <linux/io.h>
> +#include <linux/memblock.h>
> +
> +#define dmi_early_remap                early_ioremap
> +#define dmi_early_unmap                early_iounmap
> +#define dmi_remap              dmi_ioremap
> +#define dmi_unmap              dmi_iounmap
> +#define dmi_alloc(l)           memblock_alloc_low(l, PAGE_SIZE)
> +
> +void __init __iomem *dmi_ioremap(u64 phys_addr, unsigned long size)
> +{
> +       return ((void *)TO_CAC(phys_addr));
> +}

This will cause a warning from sparse about a mismatched address space.
Maybe check all of your sources with 'make C=1' to see what other warnings
you missed.

> diff --git a/arch/loongarch/include/asm/fw.h b/arch/loongarch/include/asm/fw.h
> new file mode 100644
> index 000000000000..8a0e8e7c625f
> --- /dev/null
> +++ b/arch/loongarch/include/asm/fw.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef __ASM_FW_H_
> +#define __ASM_FW_H_
> +
> +#include <asm/bootinfo.h>
> +
> +extern int fw_argc;
> +extern long *_fw_argv, *_fw_envp;
> +
> +#define fw_argv(index)         ((char *)(long)_fw_argv[(index)])
> +#define fw_envp(index)         ((char *)(long)_fw_envp[(index)])
> +
> +extern void fw_init_cmdline(void);
> +
> +#endif /* __ASM_FW_H_ */

You write that the system is booted using UEFI. Doesn't that mean you
pass the command line in UEFI structures as well?

> +/*
> + * Dummy values to fill the table in mmap.c
> + * The real values will be generated at runtime
> + * See setup_protection_map
> + */
> +#define __P000 __pgprot(0)
> +#define __P001 __pgprot(0)
> +#define __P010 __pgprot(0)
> +#define __P011 __pgprot(0)
> +#define __P100 __pgprot(0)
> +#define __P101 __pgprot(0)
> +#define __P110 __pgprot(0)
> +#define __P111 __pgprot(0)
> +
> +#define __S000 __pgprot(0)
> +#define __S001 __pgprot(0)
> +#define __S010 __pgprot(0)
> +#define __S011 __pgprot(0)
> +#define __S100 __pgprot(0)
> +#define __S101 __pgprot(0)
> +#define __S110 __pgprot(0)
> +#define __S111 __pgprot(0)

Why does this have to be a runtime thing? If you only support one CPU
implementation, are these not all constant?

> +#ifdef CONFIG_64BIT
> +#define TASK_SIZE32    0x80000000UL

Why is the task size for compat tasks limited to 2GB? Shouldn't these
be able to use the full 32-bit address space?

> +#ifdef CONFIG_VA_BITS_40
> +#define TASK_SIZE64     (0x1UL << ((cpu_vabits > 40)?40:cpu_vabits))
> +#endif
> +#ifdef CONFIG_VA_BITS_48
> +#define TASK_SIZE64     (0x1UL << ((cpu_vabits > 48)?48:cpu_vabits))
> +#endif

Why would the CPU have fewer VA bits than the page table layout allows?

> diff --git a/arch/loongarch/include/asm/reg.h b/arch/loongarch/include/asm/reg.h
> new file mode 100644
> index 000000000000..8315e6fc8079
> --- /dev/null
> +++ b/arch/loongarch/include/asm/reg.h
> @@ -0,0 +1,5 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#include <uapi/asm/reg.h>

Empty files like this one should not be needed, since the uapi directory comes
next in the search path.

> --- /dev/null
> +++ b/arch/loongarch/include/asm/spinlock.h
> @@ -0,0 +1,12 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef _ASM_SPINLOCK_H
> +#define _ASM_SPINLOCK_H
> +
> +#include <asm/processor.h>
> +#include <asm/qspinlock.h>
> +#include <asm/qrwlock.h>
> +
> +#endif /* _ASM_SPINLOCK_H */

qspinlock is usually not ideal, unless you have large NUMA systems.
As I see that the instruction set does not have the required 16-bit
xchg() instruction that you emulate using a cmpxchg() loop, it seems
likely that you cannot actually use qspinlock in a way that guarantees
forward progress.

We just had that discussion regarding risc-v qspinlock, see
https://lore.kernel.org/linux-riscv/1616868399-82848-4-git-send-email-guoren@kernel.org/
for Peter's explanations.

> +
> +static inline cycles_t get_cycles(void)
> +{
> +       return drdtime();
> +}
> +
> +static inline unsigned long random_get_entropy(void)
> +{
> +       return drdtime();
> +}
> +#endif /* __KERNEL__ */

The second function here is not used since it's not a macro. Just remove
it and use the identical default.

> +/*
> + * Subprogram calling convention
> + */
> +#define _LOONGARCH_SIM_ABILP32         1
> +#define _LOONGARCH_SIM_ABILPX32                2
> +#define _LOONGARCH_SIM_ABILP64         3

What is the difference between ILP32 and ILPX32 here?

What is the ILP64 support for, do you support both the regular LP64 and ILP64
with 64-bit 'int'?

           Arnd

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

* Re: [PATCH 05/19] LoongArch: Add boot and setup routines
  2021-07-06  4:18 ` [PATCH 05/19] LoongArch: Add boot and setup routines Huacai Chen
  2021-07-06 10:16   ` Arnd Bergmann
@ 2021-07-06 10:55   ` Arnd Bergmann
  1 sibling, 0 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:55 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> +
> +#ifdef CONFIG_64BIT
> +       /* Guess if the sign extension was forgotten by bootloader */
> +       if (start < CAC_BASE)
> +               start = (int)start;
> +#endif
> +       initrd_start = start;
> +       initrd_end += start;
> +       return 0;
> +}
> +early_param("rd_start", rd_start_early);
> +
> +static int __init rd_size_early(char *p)
> +{
> +       initrd_end += memparse(p, &p);
> +       return 0;
> +}
> +early_param("rd_size", rd_size_early);

The early parameters should not be used for this, I'm fairly sure the UEFI
boot protocol already has ways to communicate all necessary information.

> +
> +#ifdef CONFIG_ACPI
> +       init_initrd();
> +#endif

Why is the initrd support tied to ACPI? Can you actually boot without ACPI?

> +#if defined(CONFIG_VT)
> +#if defined(CONFIG_VGA_CONSOLE)
> +       conswitchp = &vga_con;
> +#elif defined(CONFIG_DUMMY_CONSOLE)
> +       conswitchp = &dummy_con;
> +#endif
> +#endif

The VGA console seems rather outdated. If you have UEFI, why not use
the provided framebuffer for the console?

> +u64 cpu_clock_freq;
> +EXPORT_SYMBOL(cpu_clock_freq);
> +u64 const_clock_freq;
> +EXPORT_SYMBOL(const_clock_freq);

You should generally not rely on the CPU clock frequency being fixed
like this, as this breaks down as soon as you add a drivers/cpufreq/ driver.

What code uses these?

> +void __init time_init(void)
> +{
> +       if (!cpu_has_cpucfg)
> +               const_clock_freq = cpu_clock_freq;
> +       else
> +               const_clock_freq = calc_const_freq();
> +
> +       init_timeval = drdtime() - csr_readq(LOONGARCH_CSR_CNTC);
> +
> +       constant_clockevent_init();
> +       constant_clocksource_init();
> +}

Clocksource and clockevents drivers should be located in drivers/clocksource
and reviewed by its maintainers.

         Arnd

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

* Re: [PATCH 06/19] LoongArch: Add exception/interrupt handling
  2021-07-06 10:16   ` Arnd Bergmann
@ 2021-07-06 10:56     ` Arnd Bergmann
  0 siblings, 0 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:56 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> diff --git a/arch/loongarch/include/asm/break.h b/arch/loongarch/include/asm/break.h
> new file mode 100644
> index 000000000000..109d0c85c582
> --- /dev/null
> +++ b/arch/loongarch/include/asm/break.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef __ASM_BREAK_H
> +#define __ASM_BREAK_H
> +
> +#include <uapi/asm/break.h>
> +
> +#endif /* __ASM_BREAK_H */

The file can be removed.

> --- /dev/null
> +++ b/arch/loongarch/include/asm/debug.h
> @@ -0,0 +1,18 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +
> +#ifndef __LOONGARCH_ASM_DEBUG_H__
> +#define __LOONGARCH_ASM_DEBUG_H__
> +
> +#include <linux/dcache.h>
> +
> +/*
> + * loongarch_debugfs_dir corresponds to the "loongarch" directory at the top
> + * level of the DebugFS hierarchy. LoongArch-specific DebugFS entries should
> + * be placed beneath this directory.
> + */
> +extern struct dentry *loongarch_debugfs_dir;

I see this one is used for the alignment trap handling, which on other
architectures
is part of sysctl. Try to see what the most common implementation is
across architectures and use that instead.

I think there also needs to be a discussion about how to handle alignment traps
in general, so maybe split out all alignment handling into a separate patch.

> diff --git a/arch/loongarch/kernel/unaligned.c b/arch/loongarch/kernel/unaligned.c
> new file mode 100644
> index 000000000000..d66e453297da
> --- /dev/null
> +++ b/arch/loongarch/kernel/unaligned.c
> @@ -0,0 +1,461 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Handle unaligned accesses by emulation.

Can you explain in this comment what the CPU can or cannot do? Are all
memory accesses assumed to be naturally aligned? Is any of the CPU
implementation dependent?

        Arnd

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

* Re: [PATCH 04/19] LoongArch: Add common headers
       [not found] ` <20210706041820.1536502-5-chenhuacai@loongson.cn>
  2021-07-06 10:16   ` [PATCH 04/19] LoongArch: Add common headers Arnd Bergmann
  2021-07-06 10:54   ` Arnd Bergmann
@ 2021-07-06 10:57   ` Peter Zijlstra
  2021-07-06 11:23   ` Peter Zijlstra
  2021-07-06 11:59   ` Peter Zijlstra
  4 siblings, 0 replies; 131+ messages in thread
From: Peter Zijlstra @ 2021-07-06 10:57 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 06, 2021 at 12:18:05PM +0800, Huacai Chen wrote:
> diff --git a/arch/loongarch/include/asm/barrier.h b/arch/loongarch/include/asm/barrier.h
> new file mode 100644
> index 000000000000..8ab8d8f15b88
> --- /dev/null
> +++ b/arch/loongarch/include/asm/barrier.h
> @@ -0,0 +1,53 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef __ASM_BARRIER_H
> +#define __ASM_BARRIER_H
> +
> +#include <asm/addrspace.h>
> +
> +#define __sync()	__asm__ __volatile__("dbar 0" : : : "memory")
> +
> +#define fast_wmb()	__sync()
> +#define fast_rmb()	__sync()
> +#define fast_mb()	__sync()
> +#define fast_iob()	__sync()
> +#define wbflush()	__sync()
> +
> +#define wmb()		fast_wmb()
> +#define rmb()		fast_rmb()
> +#define mb()		fast_mb()
> +#define iob()		fast_iob()

Is there any actual documentation about memory ordering for this
architecture? Or are you going to do the MIPS trainwreck?

Having a single full memory barrier for everything is very sad for a new
architecture, we're in 2021, not 1990s.

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

* Re: [PATCH 07/19] LoongArch: Add process management
  2021-07-06 10:16   ` Arnd Bergmann
@ 2021-07-06 10:57     ` Arnd Bergmann
  2021-07-06 11:09     ` Peter Zijlstra
  1 sibling, 0 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:57 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> +void arch_cpu_idle(void)
> +{
> +       local_irq_enable();
> +       __arch_cpu_idle();
> +}

This looks racy: What happens if an interrupt is pending and hits before
entering __arch_cpu_idle()?

Usually CPU idle instructions are designed to be called with interrupts disabled
and wait until an interrupt happens, including one that was already pending.

           Arnd

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

* Re: [PATCH 08/19] LoongArch: Add memory management
  2021-07-06 10:16   ` Arnd Bergmann
@ 2021-07-06 10:57     ` Arnd Bergmann
  2021-08-12 11:20     ` Huacai Chen
  1 sibling, 0 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:57 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> diff --git a/arch/loongarch/include/asm/kmalloc.h b/arch/loongarch/include/asm/kmalloc.h
> new file mode 100644
> index 000000000000..b318c41520d8
> --- /dev/null
> +++ b/arch/loongarch/include/asm/kmalloc.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef __ASM_KMALLOC_H
> +#define __ASM_KMALLOC_H
> +
> +#define ARCH_DMA_MINALIGN L1_CACHE_BYTES
> +
> +#endif /* __ASM_KMALLOC_H */

You wrote elsewhere that DMA is cache-coherent, so this should not
be needed at all.

> diff --git a/arch/loongarch/include/asm/shmparam.h b/arch/loongarch/include/asm/shmparam.h
> new file mode 100644
> index 000000000000..f726ac537710
> --- /dev/null
> +++ b/arch/loongarch/include/asm/shmparam.h
> @@ -0,0 +1,12 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef _ASM_SHMPARAM_H
> +#define _ASM_SHMPARAM_H
> +
> +#define __ARCH_FORCE_SHMLBA    1
> +
> +#define        SHMLBA  (4 * PAGE_SIZE)          /* attach addr a multiple of this */
> +
> +#endif /* _ASM_SHMPARAM_H */

I think this needs to be defined in a way that is independent of the configured
page size to minimize the differences between kernel configuration visible to
user space.

Maybe make it always 64KB?

> diff --git a/arch/loongarch/include/asm/sparsemem.h b/arch/loongarch/include/asm/sparsemem.h
> new file mode 100644
> index 000000000000..9b57dc69f523
> --- /dev/null
> +++ b/arch/loongarch/include/asm/sparsemem.h
> @@ -0,0 +1,21 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef _LOONGARCH_SPARSEMEM_H
> +#define _LOONGARCH_SPARSEMEM_H
> +
> +#ifdef CONFIG_SPARSEMEM
> +
> +/*
> + * SECTION_SIZE_BITS           2^N: how big each section will be
> + * MAX_PHYSMEM_BITS            2^N: how much memory we can have in that space
> + */
> +#define SECTION_SIZE_BITS      29
> +#define MAX_PHYSMEM_BITS       48

Maybe add a comment to explain how you got to '29'?

> +
> +#ifdef CONFIG_PAGE_SIZE_4KB
> +#define PAGE_SHIFT      12
> +#endif
> +#ifdef CONFIG_PAGE_SIZE_16KB
> +#define PAGE_SHIFT      14
> +#endif
> +#ifdef CONFIG_PAGE_SIZE_64KB
> +#define PAGE_SHIFT      16
> +#endif

Shouldn't these be defined in some header?

        Arnd

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

* Re: [PATCH 09/19] LoongArch: Add system call support
  2021-07-06 10:17   ` Arnd Bergmann
@ 2021-07-06 10:58     ` Arnd Bergmann
  2021-07-07  4:24     ` Huacai Chen
  1 sibling, 0 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:58 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> diff --git a/arch/loongarch/include/asm/seccomp.h b/arch/loongarch/include/asm/seccomp.h
> new file mode 100644
> index 000000000000..220c885f5478
> --- /dev/null
> +++ b/arch/loongarch/include/asm/seccomp.h
> @@ -0,0 +1,9 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +#ifndef __ASM_SECCOMP_H
> +#define __ASM_SECCOMP_H
> +
> +#include <linux/unistd.h>
> +
> +#include <asm-generic/seccomp.h>
> +
> +#endif /* __ASM_SECCOMP_H */

I would expect this file to not be needed.

> diff --git a/arch/loongarch/include/asm/uaccess.h b/arch/loongarch/include/asm/uaccess.h
> new file mode 100644
> index 000000000000..b760aa0a3bc6
> --- /dev/null
> +++ b/arch/loongarch/include/asm/uaccess.h
> @@ -0,0 +1,453 @@
> + * The fs value determines whether argument validity checking should be
> + * performed or not.  If get_fs() == USER_DS, checking is performed, with
> + * get_fs() == KERNEL_DS, checking is bypassed.
> + *
> + * For historical reasons, these macros are grossly misnamed.
> + */

You removed set_fs()/get_fs(), which is good, but you forgot to remove
the comment.

> diff --git a/arch/loongarch/include/uapi/asm/unistd.h b/arch/loongarch/include/uapi/asm/unistd.h
> new file mode 100644
> index 000000000000..6c194d740ed0
> --- /dev/null
> +++ b/arch/loongarch/include/uapi/asm/unistd.h
> @@ -0,0 +1,7 @@
> +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> +#define __ARCH_WANT_NEW_STAT

Why do you need newstat? I think now that we have statx and the libc
emulation code on top of it, there is probably no need to support both
on the kernel side.

> +#define __ARCH_WANT_SYS_CLONE
> +#define __ARCH_WANT_SYS_CLONE3

Similarly, if you have clone3, you should not need clone.

> +#define __ARCH_WANT_SET_GET_RLIMIT

And here for prlimit64

> +void *sys_call_table[__NR_syscalls] = {
> +       [0 ... __NR_syscalls - 1] = sys_ni_syscall,
> +#include <asm/unistd.h>
> +       __SYSCALL(__NR_clone, __sys_clone)
> +       __SYSCALL(__NR_clone3, __sys_clone3)
> +};

I would suggest expressing this as

#defined sys_clone3 __sys_clone3

instead of overriding the other entries.

          Arnd

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

* Re: [PATCH 10/19] LoongArch: Add signal handling support
  2021-07-06 10:17   ` Arnd Bergmann
@ 2021-07-06 10:59     ` Arnd Bergmann
  2021-07-08 13:04     ` Huacai Chen
  1 sibling, 0 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 10:59 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang, Eric W. Biederman

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
>
> This patch adds signal handling support for LoongArch.
>
> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>

Eric Biederman should review this part as well.

> --- /dev/null
> +++ b/arch/loongarch/include/asm/sigcontext.h
> @@ -0,0 +1,10 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef _ASM_SIGCONTEXT_H
> +#define _ASM_SIGCONTEXT_H
> +
> +#include <uapi/asm/sigcontext.h>
> +
> +#endif /* _ASM_SIGCONTEXT_H */

Remove this file

> + */
> +#ifndef _UAPI_ASM_SIGINFO_H
> +#define _UAPI_ASM_SIGINFO_H
> +
> +#if _LOONGARCH_SZLONG == 32
> +#define __ARCH_SI_PREAMBLE_SIZE (3 * sizeof(int))
> +#else
> +#define __ARCH_SI_PREAMBLE_SIZE (4 * sizeof(int))
> +#endif

These are no longer used.

> +
> +#ifndef _NSIG
> +#define _NSIG          128
> +#endif

Everything else uses 64 here, except for MIPS.


> +#define _NSIG_BPW      __BITS_PER_LONG
> +#define _NSIG_WORDS    (_NSIG / _NSIG_BPW)
> +
> +#define SIGHUP          1
> +#define SIGINT          2
> +#define SIGQUIT                 3
> +#define SIGILL          4
> +#define SIGTRAP                 5
> +#define SIGABRT                 6
> +#define SIGIOT          6
> +#define SIGBUS          7
> +#define SIGFPE          8
> +#define SIGKILL                 9
> +#define SIGUSR1                10
> +#define SIGSEGV                11
> +#define SIGUSR2                12
> +#define SIGPIPE                13
> +#define SIGALRM                14
> +#define SIGTERM                15
> +#define SIGSTKFLT      16
> +#define SIGCHLD                17
> +#define SIGCONT                18
> +#define SIGSTOP                19
> +#define SIGTSTP                20
> +#define SIGTTIN                21
> +#define SIGTTOU                22
> +#define SIGURG         23
> +#define SIGXCPU                24
> +#define SIGXFSZ                25
> +#define SIGVTALRM      26
> +#define SIGPROF                27
> +#define SIGWINCH       28
> +#define SIGIO          29
> +#define SIGPOLL                SIGIO
> +#define SIGPWR         30
> +#define SIGSYS         31
> +#define        SIGUNUSED       31

Please try to use the asm-generic version of these definitions instead
copying them. If you need something different, you can add an #ifdef there,
and then we can discuss whether the difference makes sense.

        Arnd

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

* Re: [PATCH 12/19] LoongArch: Add misc common routines
  2021-07-06 10:17   ` Arnd Bergmann
@ 2021-07-06 11:00     ` Arnd Bergmann
  2021-07-23 10:41     ` Huacai Chen
  1 sibling, 0 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 11:00 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> +
> +#ifdef ARCH_HAS_USABLE_BUILTIN_POPCOUNT
> +
> +#include <asm/types.h>
> +
> +static inline unsigned int __arch_hweight32(unsigned int w)
> +{
> +       return __builtin_popcount(w);
> +}

This looks like you incorrect copied it from MIPS: For a new architecture,
you should know whether __builtin_popcount is usable or not.

> +static inline unsigned long __xchg(volatile void *ptr, unsigned long x,
> +                                  int size)
> +{
> +       switch (size) {
> +       case 1:
> +       case 2:
> +               return __xchg_small(ptr, x, size);

If there is no native sub-word xchg(), then better make this BUILD_BUG(),
see the riscv implementation.

> +
> +static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,
> +                                     unsigned long new, unsigned int size)
> +{
> +       switch (size) {
> +       case 1:
> +       case 2:
> +               return __cmpxchg_small(ptr, old, new, size);

Same here.

> +++ b/arch/loongarch/include/asm/fb.h
> @@ -0,0 +1,23 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +#ifndef _ASM_FB_H_
> +#define _ASM_FB_H_
> +
> +#include <linux/fb.h>
> +#include <linux/fs.h>
> +#include <asm/page.h>
> +
> +static inline void fb_pgprotect(struct file *file, struct vm_area_struct *vma,
> +                               unsigned long off)
> +{
> +       vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
> +}

Do you have a writethrough or write-combine map type? noncached makes
this slower than necessary.
> +/*
> + * On LoongArch I/O ports are memory mapped, so we access them using normal
> + * load/store instructions. loongarch_io_port_base is the virtual address to
> + * which all ports are being mapped.  For sake of efficiency some code
> + * assumes that this is an address that can be loaded with a single lui
> + * instruction, so the lower 16 bits must be zero. Should be true on any
> + * sane architecture; generic code does not use this assumption.
> + */
> +extern unsigned long loongarch_io_port_base;
> +
> +static inline void set_io_port_base(unsigned long base)
> +{
> +       loongarch_io_port_base = base;
> +}

If you are able to map this to a fixed virtual address (in fixmap or elsewhere),
you can just use the asm-generic version.

> +/*
> + * ISA I/O bus memory addresses are 1:1 with the physical address.
> + */
> +static inline unsigned long isa_virt_to_bus(volatile void *address)
> +{
> +       return virt_to_phys(address);
> +}
> +
> +static inline void *isa_bus_to_virt(unsigned long address)
> +{
> +       return phys_to_virt(address);
> +}
> +/*
> + * However PCI ones are not necessarily 1:1 and therefore these interfaces
> + * are forbidden in portable PCI drivers.
> + *
> + * Allow them for x86 for legacy drivers, though.
> + */
> +#define virt_to_bus virt_to_phys
> +#define bus_to_virt phys_to_virt

As mentioned in another patch, these should not exist on new architectures.

> +
> +static inline void __iomem *ioremap_prot(phys_addr_t offset, unsigned long size,
> +       unsigned long prot_val)
> +{
> +       /* This only works for !HIGHMEM currently */

Do you support highmem? I would expect new architectures to no longer
implement that. Just use a 64-bit kernel on systems with lots of ram.

> +#define ioremap(offset, size)                                  \
> +       ioremap_prot((offset), (size), _CACHE_SUC)
> +#define ioremap_uc ioremap

Remove ioremap_uc(), it should never be called here.

> +/*
> + * ioremap_wc     -   map bus memory into CPU space
> + * @offset:    bus address of the memory
> + * @size:      size of the resource to map
> + *
> + * ioremap_wc performs a platform specific sequence of operations to
> + * make bus memory CPU accessible via the readb/readw/readl/writeb/
> + * writew/writel functions and the other mmio helpers. The returned
> + * address is not guaranteed to be usable directly as a virtual
> + * address.
> + *
> + * This version of ioremap ensures that the memory is marked uncachable
> + * but accelerated by means of write-combining feature. It is specifically
> + * useful for PCIe prefetchable windows, which may vastly improve a
> + * communications performance. If it was determined on boot stage, what
> + * CPU CCA doesn't support WUC, the method shall fall-back to the
> + * _CACHE_SUC option (see cpu_probe() method).
> + */
> +#define ioremap_wc(offset, size)                               \
> +       ioremap_prot((offset), (size), boot_cpu_data.writecombine)

It seems this is all copied from MIPS again. Are you sure you need to support
both versions with a runtime conditional?

> +#define __BUILD_MEMORY_SINGLE(pfx, bwlq, type)                         \
> +                                                                       \
> +static inline void pfx##write##bwlq(type val,                          \
> +                                   volatile void __iomem *mem)         \
> +{                                                                      \

Please don't add another copy of these macros. Use the version from
include/asm-generic, or modify it as needed if it doesn't quite work.

> diff --git a/arch/loongarch/include/asm/vga.h b/arch/loongarch/include/asm/vga.h
> new file mode 100644
> index 000000000000..eef95f2f837a
> --- /dev/null
> +++ b/arch/loongarch/include/asm/vga.h
> @@ -0,0 +1,56 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Access to VGA videoram
> + *
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */

I think it would be better not to support VGA console, but to use the EFI
framebuffer.


> diff --git a/arch/loongarch/kernel/cmpxchg.c b/arch/loongarch/kernel/cmpxchg.c
> new file mode 100644
> index 000000000000..30f9f1ee4f0a
> --- /dev/null
> +++ b/arch/loongarch/kernel/cmpxchg.c
> @@ -0,0 +1,100 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Author: Huacai Chen <chenhuacai@loongson.cn>
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */
> +
> +#include <linux/bitops.h>
> +#include <asm/cmpxchg.h>
> +
> +unsigned long __xchg_small(volatile void *ptr, unsigned long val, unsigned int size)
> +{

This file should not be there, I think you just copied it from the MIPS version.

Which brings me to an important point: anything you copied from elsewhere
is clearly not copyrighted by Loongson. I think you need to go through each
file and update the copyright and author statements to reflect who actually
wrote the code. At least you won't have to update this file if you remove it ;-)


       Arnd

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

* Re: [PATCH 13/19] LoongArch: Add some library functions
  2021-07-06 10:17   ` Arnd Bergmann
@ 2021-07-06 11:00     ` Arnd Bergmann
  2021-08-12 11:22     ` Huacai Chen
  1 sibling, 0 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 11:00 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
>
> +/*
> + * long __strncpy_from_user(char *to, const char *from, long len)
> + *
> + * a0: to
> + * a1: from
> + * a2: len
> + */
> +SYM_FUNC_START(__strncpy_from_user)
> +       move    a3, zero
> +

I doubt this is better than the C version in lib/strncpy_from_user.c

> diff --git a/arch/loongarch/lib/strnlen_user.S b/arch/loongarch/lib/strnlen_user.S
> new file mode 100644
> index 000000000000..9288a5ad294e
> --- /dev/null
> +++ b/arch/loongarch/lib/strnlen_user.S
> @@ -0,0 +1,47 @@
> +/* SPDX-License-Identifier: GPL-2.0 */
> +/*
> + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> + */

Same here.

Have you done any measurement to show that the asm version actually helps
here? If not, just use the generic version.


       Arnd

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

* Re: [PATCH 15/19] LoongArch: Add PCI controller support
  2021-07-06 10:17   ` Arnd Bergmann
@ 2021-07-06 11:01     ` Arnd Bergmann
  2021-08-12 11:29     ` Huacai Chen
  1 sibling, 0 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 11:01 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
>
> Loongson64 based systems are PC-like systems which use PCI/PCIe as its
> I/O bus, This patch adds the PCI host controller support for LoongArch.
>
> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
> ---
>  arch/loongarch/include/asm/pci.h  | 124 ++++++++++++++++++
>  arch/loongarch/pci/acpi.c         | 194 ++++++++++++++++++++++++++++
>  arch/loongarch/pci/mmconfig.c     | 105 +++++++++++++++
>  arch/loongarch/pci/pci-loongson.c | 130 +++++++++++++++++++
>  arch/loongarch/pci/pci.c          | 207 ++++++++++++++++++++++++++++++

PCI controller support should generally live in drivers/pci/controller/ and
get reviewed by the subsystem maintainers.

> +/*
> + * This file essentially defines the interface between board specific
> + * PCI code and LoongArch common PCI code. Should potentially put into
> + * include/asm/pci.h file.
> + */
> +
> +#include <linux/ioport.h>
> +#include <linux/list.h>
> +
> +extern const struct pci_ops *__read_mostly loongarch_pci_ops;

There is already an abstraction for this in the common code, don't add another.

> +/*
> + * Each pci channel is a top-level PCI bus seem by CPU.         A machine  with
> + * multiple PCI channels may have multiple PCI host controllers or a
> + * single controller supporting multiple channels.
> + */
> +struct pci_controller {
> +       struct list_head list;
> +       struct pci_bus *bus;
> +       struct device_node *of_node;
> +
> +       struct pci_ops *pci_ops;
> +       struct resource *mem_resource;
> +       unsigned long mem_offset;
> +       struct resource *io_resource;
> +       unsigned long io_offset;
> +       unsigned long io_map_base;
> +       struct resource *busn_resource;
> +
> +       unsigned int node;
> +       unsigned int index;
> +       unsigned int need_domain_info;
> +#ifdef CONFIG_ACPI
> +       struct acpi_device *companion;
> +#endif
> +       phys_addr_t mcfg_addr;
> +};
> +
> +extern void pcibios_add_root_resources(struct list_head *resources);
> +
> +extern phys_addr_t mcfg_addr_init(int domain);
> +
> +#ifdef CONFIG_PCI_DOMAINS
> +static inline void set_pci_need_domain_info(struct pci_controller *hose,
> +                                           int need_domain_info)
> +{
> +       hose->need_domain_info = need_domain_info;
> +}
> +#endif /* CONFIG_PCI_DOMAINS */

Just use PCI_DOMAINS unconditionally

> +
> +/*
> + * Can be used to override the logic in pci_scan_bus for skipping
> + * already-configured bus numbers - to be used for buggy BIOSes
> + * or architectures with incomplete PCI setup by the loader
> + */
> +static inline unsigned int pcibios_assign_all_busses(void)
> +{
> +       return 1;
> +}

Since you use ACPI, the BIOS should be responsible for assigning the
buses, otherwise the ACPI data may be mismatched with the PCI
device locations that the kernel sees.

> +#define PCIBIOS_MIN_IO         0

I think this means PCI devices can reuse ports that are reserved
for ISA devices. Since you claim to support ISA, I think this should
be 0x1000

> +
> +int raw_pci_read(unsigned int domain, unsigned int bus, unsigned int devfn,
> +                                               int reg, int len, u32 *val)
> +{
> +       struct pci_bus *bus_tmp = pci_find_bus(domain, bus);
> +
> +       if (bus_tmp)
> +               return bus_tmp->ops->read(bus_tmp, devfn, reg, len, val);
> +       return -EINVAL;
> +}
> +
> +int raw_pci_write(unsigned int domain, unsigned int bus, unsigned int devfn,
> +                                               int reg, int len, u32 val)
> +{
> +       struct pci_bus *bus_tmp = pci_find_bus(domain, bus);
> +
> +       if (bus_tmp)
> +               return bus_tmp->ops->write(bus_tmp, devfn, reg, len, val);
> +       return -EINVAL;
> +}

This looks like you copied from arch/arm64. I think the code really
needs to be generalized more. Maybe move the arm64 implementation
to drivers/acpi/ so it can be shared with loongarch?

> +/*
> + * We need to avoid collisions with `mirrored' VGA ports
> + * and other strange ISA hardware, so we always want the
> + * addresses to be allocated in the 0x000-0x0ff region
> + * modulo 0x400.
> + *
> + * Why? Because some silly external IO cards only decode
> + * the low 10 bits of the IO address. The 0x00-0xff region
> + * is reserved for motherboard devices that decode all 16
> + * bits, so it's ok to allocate at, say, 0x2800-0x28ff,
> + * but we want to try to avoid allocating at 0x2900-0x2bff
> + * which might have be mirrored at 0x0100-0x03ff..
> + */
> +resource_size_t
> +pcibios_align_resource(void *data, const struct resource *res,
> +                      resource_size_t size, resource_size_t align)
> +{
> +       struct pci_dev *dev = data;
> +       struct pci_controller *hose = dev->sysdata;
> +       resource_size_t start = res->start;
> +
> +       if (res->flags & IORESOURCE_IO) {
> +               /* Make sure we start at our min on all hoses */
> +               if (start < PCIBIOS_MIN_IO + hose->io_resource->start)
> +                       start = PCIBIOS_MIN_IO + hose->io_resource->start;
> +
> +               /*
> +                * Put everything into 0x00-0xff region modulo 0x400
> +                */
> +               if (start & 0x300)
> +                       start = (start + 0x3ff) & ~0x3ff;
> +       } else if (res->flags & IORESOURCE_MEM) {
> +               /* Make sure we start at our min on all hoses */
> +               if (start < PCIBIOS_MIN_MEM)
> +                       start = PCIBIOS_MIN_MEM;
> +       }
> +
> +       return start;
> +}

Same here, please don't add another copy of this function.


       Arnd

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

* Re: [PATCH 16/19] LoongArch: Add VDSO and VSYSCALL support
  2021-07-06 10:17   ` Arnd Bergmann
@ 2021-07-06 11:02     ` Arnd Bergmann
  2021-08-12 11:31     ` Huacai Chen
  1 sibling, 0 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 11:02 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
>
> This patch adds VDSO and VSYSCALL support (gettimeofday and its friends)
> for LoongArch.
>
> Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
> ---
>  arch/loongarch/include/asm/vdso.h             |  50 +++
>  arch/loongarch/include/asm/vdso/clocksource.h |   8 +
>  .../loongarch/include/asm/vdso/gettimeofday.h | 101 ++++++
>  arch/loongarch/include/asm/vdso/processor.h   |  14 +
>  arch/loongarch/include/asm/vdso/vdso.h        |  42 +++
>  arch/loongarch/include/asm/vdso/vsyscall.h    |  27 ++
>  arch/loongarch/kernel/vdso.c                  | 132 ++++++++
>  arch/loongarch/vdso/Makefile                  |  97 ++++++
>  arch/loongarch/vdso/elf.S                     |  15 +
>  arch/loongarch/vdso/genvdso.c                 | 311 ++++++++++++++++++
>  arch/loongarch/vdso/genvdso.h                 | 122 +++++++
>  arch/loongarch/vdso/sigreturn.S               |  24 ++
>  arch/loongarch/vdso/vdso.lds.S                |  65 ++++
>  arch/loongarch/vdso/vgettimeofday.c           |  26 ++

I fear you may have copied the wrong one here, the MIPS implementation seems
more complex than the rv64 or arm64 versions, and you should not need that
complexity.

Can you try removing the genvdso.c  file completely? To be honest I don't
see why this is needed here, so I may be missing something, but the other
ones don't have it either.

       Arnd

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

* Re: [PATCH 17/19] LoongArch: Add multi-processor (SMP) support
  2021-07-06 10:17   ` Arnd Bergmann
@ 2021-07-06 11:03     ` Arnd Bergmann
  0 siblings, 0 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 11:03 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> +const struct plat_smp_ops loongson3_smp_ops = {
> +       .send_ipi_single = loongson3_send_ipi_single,
> +       .send_ipi_mask = loongson3_send_ipi_mask,
> +       .smp_setup = loongson3_smp_setup,
> +       .prepare_cpus = loongson3_prepare_cpus,
> +       .boot_secondary = loongson3_boot_secondary,
> +       .init_secondary = loongson3_init_secondary,
> +       .smp_finish = loongson3_smp_finish,
> +#ifdef CONFIG_HOTPLUG_CPU
> +       .cpu_disable = loongson3_cpu_disable,
> +       .cpu_die = loongson3_cpu_die,
> +#endif
> +};

I would hope that these functions can be used across platforms without
an abstraction layer in-between. Are these not all part of the either the
CPU architecture definition or the firmware interface?

Do you even expect to see non-SMP systems deployed widely enough that
SMP support must be optional? On arch/arm64 we ended up always
building SMP support into the kernel because practically everyone wants it.

      Arnd

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

* Re: [PATCH 18/19] LoongArch: Add Non-Uniform Memory Access (NUMA) support
  2021-07-06 10:18   ` Arnd Bergmann
@ 2021-07-06 11:03     ` Arnd Bergmann
  2021-08-12 11:46     ` Huacai Chen
  1 sibling, 0 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 11:03 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> +#ifdef CONFIG_SWIOTLB
> +/*
> + * Optional platform hook to call swiotlb_setup().
> + */
> +extern void plat_swiotlb_setup(void);
> +
> +#else
> +
> +static inline void plat_swiotlb_setup(void) {}
> +
> +#endif /* CONFIG_SWIOTLB */

I guess this accidentally slipped into the wrong patch? It doesn't appear to be
NUMA related.

> diff --git a/arch/loongarch/loongson64/dma.c b/arch/loongarch/loongson64/dma.c
> new file mode 100644
> index 000000000000..f259f70c75fa
> --- /dev/null
> +++ b/arch/loongarch/loongson64/dma.c
> @@ -0,0 +1,59 @@
> +// SPDX-License-Identifier: GPL-2.0

Same for this file.

         Arnd

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

* Re: [PATCH 19/19] LoongArch: Add Loongson-3 default config file
  2021-07-06 10:18   ` Arnd Bergmann
@ 2021-07-06 11:04     ` Arnd Bergmann
  2021-08-12 11:58     ` Huacai Chen
  1 sibling, 0 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 11:04 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Andy Lutomirski, Thomas Gleixner, Peter Zijlstra, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:

> +CONFIG_EMBEDDED=y

You probably should not set this one.

> +CONFIG_CAIF=y

And almost certainly not this one.

> +CONFIG_PCCARD=m
> +CONFIG_YENTA=m
> +CONFIG_RAPIDIO=y
> +CONFIG_RAPIDIO_TSI721=y

These seem unlikely as well. Can you confirm that you have both old-style
PC-Card support and RAPIDIO support on your most common hardware?

> +CONFIG_PARPORT=y
> +CONFIG_PARPORT_PC=y
> +CONFIG_PARPORT_SERIAL=y
> +CONFIG_PARPORT_PC_FIFO=y
> +CONFIG_PRINTER=m

Is this an on-board PCI device you actually have?
What do you connect to the PC parport? Most printers
in the past 15 years only have USB or network connections.

> +CONFIG_8139CP=m
> +CONFIG_8139TOO=m

Do you actually support legacy PCI slots?

> +CONFIG_SERIAL_8250=y
> +CONFIG_SERIAL_8250_CONSOLE=y
> +CONFIG_SERIAL_8250_NR_UARTS=16
> +CONFIG_SERIAL_8250_RUNTIME_UARTS=16
> +CONFIG_SERIAL_8250_EXTENDED=y
> +CONFIG_SERIAL_8250_MANY_PORTS=y
> +CONFIG_SERIAL_8250_SHARE_IRQ=y
> +CONFIG_SERIAL_8250_RSA=y
> +CONFIG_SERIAL_OF_PLATFORM=y

I don't see any device tree support in your patches, so I think the
SERIAL_OF_PLATFORM is not needed.

> +CONFIG_RAW_DRIVER=m

This is gone now.

> +CONFIG_INFINIBAND=m

You don't seem to enable any infiniband host drivers, so the core
layer can probably remain turned off

> +CONFIG_RTC_CLASS=y
> +CONFIG_RTC_DRV_EFI=y
> +CONFIG_UIO=m

same for UIO.

> +CONFIG_EXT2_FS=y
> +CONFIG_EXT2_FS_XATTR=y
> +CONFIG_EXT2_FS_POSIX_ACL=y
> +CONFIG_EXT2_FS_SECURITY=y
> +CONFIG_EXT3_FS=y
> +CONFIG_EXT3_FS_POSIX_ACL=y
> +CONFIG_EXT3_FS_SECURITY=y

I would generally recommend using EXT4 over EXT2 or EXT3

> +CONFIG_FRAME_WARN=1024

On 64-bit platforms, you probably want to increase this a bit,
otherwise you get extra warnings about code that works as
intended. The default is 2048, but you should be able to get by
with 1280, if that lets you build a defconfig kernel without warnings.


      Arnd

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

* Re: [PATCH 06/19] LoongArch: Add exception/interrupt handling
  2021-07-06  4:18 ` [PATCH 06/19] LoongArch: Add exception/interrupt handling Huacai Chen
  2021-07-06 10:16   ` Arnd Bergmann
@ 2021-07-06 11:06   ` Peter Zijlstra
  2021-07-07 13:56     ` Nicholas Piggin
  1 sibling, 1 reply; 131+ messages in thread
From: Peter Zijlstra @ 2021-07-06 11:06 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 06, 2021 at 12:18:07PM +0800, Huacai Chen wrote:
> +	.align	5	/* 32 byte rollback region */
> +SYM_FUNC_START(__arch_cpu_idle)
> +	/* start of rollback region */
> +	LONG_L	t0, tp, TI_FLAGS
> +	nop
> +	andi	t0, t0, _TIF_NEED_RESCHED
> +	bnez	t0, 1f
> +	nop
> +	nop
> +	nop
> +	idle	0
> +	/* end of rollback region */
> +1:
> +	jirl	zero, ra, 0
> +SYM_FUNC_END(__arch_cpu_idle)

> +/*
> + * Common Vectored Interrupt
> + * Complete the register saves and invoke the do_vi() handler
> + */
> +SYM_FUNC_START(except_vec_vi_handler)
> +	la	t1, __arch_cpu_idle
> +	LONG_L  t0, sp, PT_EPC
> +	/* 32 byte rollback region */
> +	ori	t0, t0, 0x1f
> +	xori	t0, t0, 0x1f
> +	bne	t0, t1, 1f
> +	LONG_S  t0, sp, PT_EPC

Seriously, you're having your interrupt handler recover from the idle
race? On a *new* architecture?

> +1:	SAVE_TEMP
> +	SAVE_STATIC
> +	CLI
> +
> +	LONG_L		s0, tp, TI_REGS
> +	LONG_S		sp, tp, TI_REGS
> +
> +	move		s1, sp /* Preserve sp */
> +
> +	/* Get IRQ stack for this CPU */
> +	la		t1, irq_stack
> +	LONG_ADDU	t1, t1, x0
> +	LONG_L		t0, t1, 0
> +
> +	/* Check if already on IRQ stack */
> +	PTR_LI		t1, ~(_THREAD_SIZE-1)
> +	and		t1, t1, sp
> +	beq		t0, t1, 2f
> +
> +	/* Switch to IRQ stack */
> +	li.w		t1, _IRQ_STACK_START
> +	PTR_ADDU	sp, t0, t1
> +
> +	/* Save task's sp on IRQ stack so that unwinding can follow it */
> +	LONG_S		s1, sp, 0
> +2:	la		t0, do_vi
> +	jirl		ra, t0, 0
> +
> +	move		sp, s1 /* Restore sp */
> +	la		t0, ret_from_irq
> +	jirl    	zero, t0, 0
> +SYM_FUNC_END(except_vec_vi_handler)



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

* Re: [PATCH 07/19] LoongArch: Add process management
  2021-07-06 10:16   ` Arnd Bergmann
  2021-07-06 10:57     ` Arnd Bergmann
@ 2021-07-06 11:09     ` Peter Zijlstra
  2021-08-12 11:17       ` Huacai Chen
  1 sibling, 1 reply; 131+ messages in thread
From: Peter Zijlstra @ 2021-07-06 11:09 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Huacai Chen, Andy Lutomirski, Thomas Gleixner, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 06, 2021 at 12:16:37PM +0200, Arnd Bergmann wrote:
> On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
> 
> > +void arch_cpu_idle(void)
> > +{
> > +       local_irq_enable();
> > +       __arch_cpu_idle();
> > +}
> 
> This looks racy: What happens if an interrupt is pending and hits before
> entering __arch_cpu_idle()?

They fix it up in their interrupt handler by moving the IP over the
actual IDLE instruction..

Still the above is broken in that local_irq_enable() will have all sorts
of tracing, but RCU is disabled at this point, so it is still very much
broken.

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

* Re: [PATCH 04/19] LoongArch: Add common headers
       [not found] ` <20210706041820.1536502-5-chenhuacai@loongson.cn>
                     ` (2 preceding siblings ...)
  2021-07-06 10:57   ` Peter Zijlstra
@ 2021-07-06 11:23   ` Peter Zijlstra
  2021-07-06 12:59     ` Arnd Bergmann
  2021-07-06 11:59   ` Peter Zijlstra
  4 siblings, 1 reply; 131+ messages in thread
From: Peter Zijlstra @ 2021-07-06 11:23 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 06, 2021 at 12:18:05PM +0800, Huacai Chen wrote:
> +/* CSR */
> +static inline u32 csr_readl(u32 reg)
> +{
> +	return __csrrd(reg);
> +}
> +
> +static inline u64 csr_readq(u32 reg)
> +{
> +	return __dcsrrd(reg);
> +}
> +
> +static inline void csr_writel(u32 val, u32 reg)
> +{
> +	__csrwr(val, reg);
> +}
> +
> +static inline void csr_writeq(u64 val, u32 reg)
> +{
> +	__dcsrwr(val, reg);
> +}
> +
> +static inline u32 csr_xchgl(u32 val, u32 mask, u32 reg)
> +{
> +	return __csrxchg(val, mask, reg);
> +}
> +
> +static inline u64 csr_xchgq(u64 val, u64 mask, u32 reg)
> +{
> +	return __dcsrxchg(val, mask, reg);
> +}

What are these __csrfoo() things, I cannot seem to find a definition of
them anywhere..

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

* Re: [PATCH 17/19] LoongArch: Add multi-processor (SMP) support
  2021-07-06  4:18 ` [PATCH 17/19] LoongArch: Add multi-processor (SMP) support Huacai Chen
  2021-07-06 10:17   ` Arnd Bergmann
@ 2021-07-06 11:32   ` Peter Zijlstra
  2021-08-12 11:39     ` Huacai Chen
  2021-07-06 11:56   ` Peter Zijlstra
                     ` (2 subsequent siblings)
  4 siblings, 1 reply; 131+ messages in thread
From: Peter Zijlstra @ 2021-07-06 11:32 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 06, 2021 at 12:18:18PM +0800, Huacai Chen wrote:
> diff --git a/arch/loongarch/include/asm/barrier.h b/arch/loongarch/include/asm/barrier.h
> index 8ab8d8f15b88..ad09a3b31cba 100644
> --- a/arch/loongarch/include/asm/barrier.h
> +++ b/arch/loongarch/include/asm/barrier.h
> @@ -20,6 +20,19 @@
>  #define mb()		fast_mb()
>  #define iob()		fast_iob()
>  
> +#define __smp_mb()	__asm__ __volatile__("dbar 0" : : : "memory")
> +#define __smp_rmb()	__asm__ __volatile__("dbar 0" : : : "memory")
> +#define __smp_wmb()	__asm__ __volatile__("dbar 0" : : : "memory")

:-(

> +
> +#ifdef CONFIG_SMP
> +#define __WEAK_LLSC_MB		"	dbar 0  \n"
> +#else
> +#define __WEAK_LLSC_MB		"		\n"
> +#endif

Isn't that spelled smp_mb() ?

> +
> +#define __smp_mb__before_atomic()	barrier()
> +#define __smp_mb__after_atomic()	__smp_mb()

Clarification please.

Does this imply LL is sequentially consistent, while SC is not?

> +
>  /**
>   * array_index_mask_nospec() - generate a ~0 mask when index < size, 0 otherwise
>   * @index: array element index
> @@ -48,6 +61,112 @@ static inline unsigned long array_index_mask_nospec(unsigned long index,
>  	return mask;
>  }
>  
> +#define __smp_load_acquire(p)							\
> +({										\
> +	union { typeof(*p) __val; char __c[1]; } __u;				\
> +	unsigned long __tmp = 0;							\
> +	compiletime_assert_atomic_type(*p);					\
> +	switch (sizeof(*p)) {							\
> +	case 1:									\
> +		*(__u8 *)__u.__c = *(volatile __u8 *)p;				\
> +		__smp_mb();							\
> +		break;								\
> +	case 2:									\
> +		*(__u16 *)__u.__c = *(volatile __u16 *)p;			\
> +		__smp_mb();							\
> +		break;								\
> +	case 4:									\
> +		__asm__ __volatile__(						\
> +		"amor.w %[val], %[tmp], %[mem]	\n"				\
> +		: [val] "=&r" (*(__u32 *)__u.__c)				\
> +		: [mem] "ZB" (*(u32 *) p), [tmp] "r" (__tmp)			\
> +		: "memory");							\
> +		break;								\
> +	case 8:									\
> +		__asm__ __volatile__(						\
> +		"amor.d %[val], %[tmp], %[mem]	\n"				\
> +		: [val] "=&r" (*(__u64 *)__u.__c)				\
> +		: [mem] "ZB" (*(u64 *) p), [tmp] "r" (__tmp)			\
> +		: "memory");							\
> +		break;								\
> +	default:								\
> +		barrier();							\
> +		__builtin_memcpy((void *)__u.__c, (const void *)p, sizeof(*p));	\
> +		__smp_mb();							\
> +	}									\
> +	__u.__val;								\
> +})
> +
> +#define __smp_store_release(p, v)						\
> +do {										\
> +	union { typeof(*p) __val; char __c[1]; } __u =				\
> +		{ .__val = (__force typeof(*p)) (v) };				\
> +	unsigned long __tmp;							\
> +	compiletime_assert_atomic_type(*p);					\
> +	switch (sizeof(*p)) {							\
> +	case 1:									\
> +		__smp_mb();							\
> +		*(volatile __u8 *)p = *(__u8 *)__u.__c;				\
> +		break;								\
> +	case 2:									\
> +		__smp_mb();							\
> +		*(volatile __u16 *)p = *(__u16 *)__u.__c;			\
> +		break;								\
> +	case 4:									\
> +		__asm__ __volatile__(						\
> +		"amswap.w %[tmp], %[val], %[mem]	\n"			\
> +		: [mem] "+ZB" (*(u32 *)p), [tmp] "=&r" (__tmp)			\
> +		: [val] "r" (*(__u32 *)__u.__c)					\
> +		: );								\
> +		break;								\
> +	case 8:									\
> +		__asm__ __volatile__(						\
> +		"amswap.d %[tmp], %[val], %[mem]	\n"			\
> +		: [mem] "+ZB" (*(u64 *)p), [tmp] "=&r" (__tmp)			\
> +		: [val] "r" (*(__u64 *)__u.__c)					\
> +		: );								\
> +		break;								\
> +	default:								\
> +		__smp_mb();							\
> +		__builtin_memcpy((void *)p, (const void *)__u.__c, sizeof(*p));	\
> +		barrier();							\
> +	}									\
> +} while (0)

What's the actual ordering of those AMO things?


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

* Re: [PATCH 17/19] LoongArch: Add multi-processor (SMP) support
  2021-07-06  4:18 ` [PATCH 17/19] LoongArch: Add multi-processor (SMP) support Huacai Chen
  2021-07-06 10:17   ` Arnd Bergmann
  2021-07-06 11:32   ` Peter Zijlstra
@ 2021-07-06 11:56   ` Peter Zijlstra
  2021-07-06 13:48   ` Peter Zijlstra
  2021-07-06 13:52   ` Peter Zijlstra
  4 siblings, 0 replies; 131+ messages in thread
From: Peter Zijlstra @ 2021-07-06 11:56 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 06, 2021 at 12:18:18PM +0800, Huacai Chen wrote:
> diff --git a/arch/loongarch/include/asm/percpu.h b/arch/loongarch/include/asm/percpu.h
> index ea5979872485..ea3c3e7808dc 100644
> --- a/arch/loongarch/include/asm/percpu.h
> +++ b/arch/loongarch/include/asm/percpu.h
> @@ -5,6 +5,8 @@
>  #ifndef __ASM_PERCPU_H
>  #define __ASM_PERCPU_H
>  
> +#include <asm/cmpxchg.h>
> +
>  /* Use r21 for fast access */
>  register unsigned long __my_cpu_offset __asm__("$r21");
>  
> @@ -15,6 +17,181 @@ static inline void set_my_cpu_offset(unsigned long off)
>  }
>  #define __my_cpu_offset __my_cpu_offset
>  
> +#define PERCPU_OP(op, asm_op, c_op)					\
> +static inline unsigned long __percpu_##op(void *ptr,			\
> +			unsigned long val, int size)			\
> +{									\
> +	unsigned long ret;						\
> +									\
> +	switch (size) {							\
> +	case 4:								\
> +		__asm__ __volatile__(					\
> +		"am"#asm_op".w"	" %[ret], %[val], %[ptr]	\n"		\
> +		: [ret] "=&r" (ret), [ptr] "+ZB"(*(u32 *)ptr)		\
> +		: [val] "r" (val));					\
> +		break;							\
> +	case 8:								\
> +		__asm__ __volatile__(					\
> +		"am"#asm_op".d" " %[ret], %[val], %[ptr]	\n"		\
> +		: [ret] "=&r" (ret), [ptr] "+ZB"(*(u64 *)ptr)		\
> +		: [val] "r" (val));					\
> +		break;							\
> +	default:							\
> +		ret = 0;						\
> +		BUILD_BUG();						\
> +	}								\
> +									\
> +	return ret c_op val;						\
> +}
> +
> +PERCPU_OP(add, add, +)
> +PERCPU_OP(and, and, &)
> +PERCPU_OP(or, or, |)
> +#undef PERCPU_OP
> +
> +static inline unsigned long __percpu_read(void *ptr, int size)
> +{
> +	unsigned long ret;
> +
> +	switch (size) {
> +	case 1:
> +		ret = READ_ONCE(*(u8 *)ptr);
> +		break;
> +	case 2:
> +		ret = READ_ONCE(*(u16 *)ptr);
> +		break;
> +	case 4:
> +		ret = READ_ONCE(*(u32 *)ptr);
> +		break;
> +	case 8:
> +		ret = READ_ONCE(*(u64 *)ptr);
> +		break;
> +	default:
> +		ret = 0;
> +		BUILD_BUG();
> +	}
> +
> +	return ret;
> +}
> +
> +static inline void __percpu_write(void *ptr, unsigned long val, int size)
> +{
> +	switch (size) {
> +	case 1:
> +		WRITE_ONCE(*(u8 *)ptr, (u8)val);
> +		break;
> +	case 2:
> +		WRITE_ONCE(*(u16 *)ptr, (u16)val);
> +		break;
> +	case 4:
> +		WRITE_ONCE(*(u32 *)ptr, (u32)val);
> +		break;
> +	case 8:
> +		WRITE_ONCE(*(u64 *)ptr, (u64)val);
> +		break;
> +	default:
> +		BUILD_BUG();
> +	}
> +}
> +
> +static inline unsigned long __percpu_xchg(void *ptr, unsigned long val,
> +						int size)
> +{
> +	switch (size) {
> +	case 1:
> +	case 2:
> +		return __xchg_small((volatile void *)ptr, val, size);
> +
> +	case 4:
> +		return __xchg_asm("amswap.w", (volatile u32 *)ptr, (u32)val);
> +
> +	case 8:
> +		if (!IS_ENABLED(CONFIG_64BIT))
> +			return __xchg_called_with_bad_pointer();
> +
> +		return __xchg_asm("amswap.d", (volatile u64 *)ptr, (u64)val);
> +
> +	default:
> +		return __xchg_called_with_bad_pointer();
> +	}
> +}
> +
> +/* this_cpu_cmpxchg */
> +#define _protect_cmpxchg_local(pcp, o, n)			\
> +({								\
> +	typeof(*raw_cpu_ptr(&(pcp))) __ret;			\
> +	__ret = cmpxchg_local(raw_cpu_ptr(&(pcp)), o, n);	\
> +	__ret;							\
> +})
> +
> +#define this_cpu_cmpxchg_1(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
> +#define this_cpu_cmpxchg_2(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
> +#define this_cpu_cmpxchg_4(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
> +#define this_cpu_cmpxchg_8(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
> +
> +#define _percpu_read(pcp)						\
> +({									\
> +	typeof(pcp) __retval;						\
> +	__retval = (typeof(pcp))__percpu_read(raw_cpu_ptr(&(pcp)),	\
> +					      sizeof(pcp));		\
> +	__retval;							\
> +})
> +
> +#define _percpu_write(pcp, val)						\
> +do {									\
> +	__percpu_write(raw_cpu_ptr(&(pcp)), (unsigned long)(val),	\
> +				sizeof(pcp));				\
> +} while (0)								\
> +
> +#define _pcp_protect(operation, pcp, val)			\
> +({								\
> +	typeof(pcp) __retval;					\
> +	__retval = (typeof(pcp))operation(raw_cpu_ptr(&(pcp)),	\
> +					  (val), sizeof(pcp));	\
> +	__retval;						\
> +})
> +
> +#define _percpu_add(pcp, val) \
> +	_pcp_protect(__percpu_add, pcp, val)
> +
> +#define _percpu_add_return(pcp, val) _percpu_add(pcp, val)
> +
> +#define _percpu_and(pcp, val) \
> +	_pcp_protect(__percpu_and, pcp, val)
> +
> +#define _percpu_or(pcp, val) \
> +	_pcp_protect(__percpu_or, pcp, val)
> +
> +#define _percpu_xchg(pcp, val) ((typeof(pcp)) \
> +	_pcp_protect(__percpu_xchg, pcp, (unsigned long)(val)))
> +
> +#define this_cpu_add_4(pcp, val) _percpu_add(pcp, val)
> +#define this_cpu_add_8(pcp, val) _percpu_add(pcp, val)
> +
> +#define this_cpu_add_return_4(pcp, val) _percpu_add_return(pcp, val)
> +#define this_cpu_add_return_8(pcp, val) _percpu_add_return(pcp, val)
> +
> +#define this_cpu_and_4(pcp, val) _percpu_and(pcp, val)
> +#define this_cpu_and_8(pcp, val) _percpu_and(pcp, val)
> +
> +#define this_cpu_or_4(pcp, val) _percpu_or(pcp, val)
> +#define this_cpu_or_8(pcp, val) _percpu_or(pcp, val)
> +
> +#define this_cpu_read_1(pcp) _percpu_read(pcp)
> +#define this_cpu_read_2(pcp) _percpu_read(pcp)
> +#define this_cpu_read_4(pcp) _percpu_read(pcp)
> +#define this_cpu_read_8(pcp) _percpu_read(pcp)
> +
> +#define this_cpu_write_1(pcp, val) _percpu_write(pcp, val)
> +#define this_cpu_write_2(pcp, val) _percpu_write(pcp, val)
> +#define this_cpu_write_4(pcp, val) _percpu_write(pcp, val)
> +#define this_cpu_write_8(pcp, val) _percpu_write(pcp, val)
> +
> +#define this_cpu_xchg_1(pcp, val) _percpu_xchg(pcp, val)
> +#define this_cpu_xchg_2(pcp, val) _percpu_xchg(pcp, val)
> +#define this_cpu_xchg_4(pcp, val) _percpu_xchg(pcp, val)
> +#define this_cpu_xchg_8(pcp, val) _percpu_xchg(pcp, val)
> +
>  #include <asm-generic/percpu.h>
>  
>  #endif /* __ASM_PERCPU_H */

Sadness :-( You could've extended your LL/SC instructions to have an
extra address register like PowerPC does. Then per-cpu can be done like:

1:	ll.w	%1, %2, r21
	...
	sc.w	%1, %2, r21
	bne	$zero, %1, 1b

Also AFAICT there is no NMI on this thing (even though you select
HAVE_NMI, I've not found any actual NMI code). So what is the
performance of CSR_CRMD_IE toggles vs using AMOs ?



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

* Re: [PATCH 04/19] LoongArch: Add common headers
       [not found] ` <20210706041820.1536502-5-chenhuacai@loongson.cn>
                     ` (3 preceding siblings ...)
  2021-07-06 11:23   ` Peter Zijlstra
@ 2021-07-06 11:59   ` Peter Zijlstra
  4 siblings, 0 replies; 131+ messages in thread
From: Peter Zijlstra @ 2021-07-06 11:59 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 06, 2021 at 12:18:05PM +0800, Huacai Chen wrote:
> +		__asm__ __volatile__(
> +		"1:	ll.w	%1, %2		# atomic_sub_if_positive\n"
> +		"	addi.w	%0, %1, %3				\n"
> +		"	or	%1, %0, $zero				\n"
> +		"	blt	%0, $zero, 1f				\n"
> +		"	sc.w	%1, %2					\n"
> +		"	beq	$zero, %1, 1b				\n"
> +		"1:							\n"

Can you please make that 2: for everyone's sanity? (idem for all the
other sites you done this).

> +		: "=&r" (result), "=&r" (temp),
> +		  "+" GCC_OFF_SMALL_ASM() (v->counter)
> +		: "I" (-i));

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

* Re: [PATCH 04/19] LoongArch: Add common headers
  2021-07-06 11:23   ` Peter Zijlstra
@ 2021-07-06 12:59     ` Arnd Bergmann
  2021-07-06 13:20       ` Peter Zijlstra
  2021-07-06 13:37       ` Peter Zijlstra
  0 siblings, 2 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-06 12:59 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Huacai Chen, Andy Lutomirski, Thomas Gleixner, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 6, 2021 at 1:23 PM Peter Zijlstra <peterz@infradead.org> wrote:
>
> On Tue, Jul 06, 2021 at 12:18:05PM +0800, Huacai Chen wrote:

> > +
> > +static inline u32 csr_xchgl(u32 val, u32 mask, u32 reg)
> > +{
> > +     return __csrxchg(val, mask, reg);
> > +}
> > +
> > +static inline u64 csr_xchgq(u64 val, u64 mask, u32 reg)
> > +{
> > +     return __dcsrxchg(val, mask, reg);
> > +}
>
> What are these __csrfoo() things, I cannot seem to find a definition of
> them anywhere..

It seems that those are provided as compiler intrinsics in <larchintrin.h>,
based on an architecture specific __builtin_loongarch_csrxchg() etc.

The specific registers are documented at
https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN#control-and-status-registers

    Arnd

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

* Re: [PATCH 04/19] LoongArch: Add common headers
  2021-07-06 12:59     ` Arnd Bergmann
@ 2021-07-06 13:20       ` Peter Zijlstra
  2021-07-06 13:37       ` Peter Zijlstra
  1 sibling, 0 replies; 131+ messages in thread
From: Peter Zijlstra @ 2021-07-06 13:20 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Huacai Chen, Andy Lutomirski, Thomas Gleixner, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 06, 2021 at 02:59:22PM +0200, Arnd Bergmann wrote:
> On Tue, Jul 6, 2021 at 1:23 PM Peter Zijlstra <peterz@infradead.org> wrote:
> >
> > On Tue, Jul 06, 2021 at 12:18:05PM +0800, Huacai Chen wrote:
> 
> > > +
> > > +static inline u32 csr_xchgl(u32 val, u32 mask, u32 reg)
> > > +{
> > > +     return __csrxchg(val, mask, reg);
> > > +}
> > > +
> > > +static inline u64 csr_xchgq(u64 val, u64 mask, u32 reg)
> > > +{
> > > +     return __dcsrxchg(val, mask, reg);
> > > +}
> >
> > What are these __csrfoo() things, I cannot seem to find a definition of
> > them anywhere..
> 
> It seems that those are provided as compiler intrinsics in <larchintrin.h>,
> based on an architecture specific __builtin_loongarch_csrxchg() etc.

Thanks, I couldn't find them.

> The specific registers are documented at
> https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN#control-and-status-registers

Bah, that's terrible naming. CSRWR really is CSRXCHG and CSRXCHG is more
like CSRXCHGMASK or something :/


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

* Re: [PATCH 04/19] LoongArch: Add common headers
  2021-07-06 12:59     ` Arnd Bergmann
  2021-07-06 13:20       ` Peter Zijlstra
@ 2021-07-06 13:37       ` Peter Zijlstra
  1 sibling, 0 replies; 131+ messages in thread
From: Peter Zijlstra @ 2021-07-06 13:37 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Huacai Chen, Andy Lutomirski, Thomas Gleixner, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 06, 2021 at 02:59:22PM +0200, Arnd Bergmann wrote:
> The specific registers are documented at
> https://loongson.github.io/LoongArch-Documentation/LoongArch-Vol1-EN#control-and-status-registers

Urrrgghhh.... the AMOs are not only fully sequentially consistent,
they're specified to be completion barriers. That seems horrifically
expensive.

Also, AFAICT there is no forward progress specified for LL/SC :/

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

* Re: [PATCH 17/19] LoongArch: Add multi-processor (SMP) support
  2021-07-06  4:18 ` [PATCH 17/19] LoongArch: Add multi-processor (SMP) support Huacai Chen
                     ` (2 preceding siblings ...)
  2021-07-06 11:56   ` Peter Zijlstra
@ 2021-07-06 13:48   ` Peter Zijlstra
  2021-08-12 11:41     ` Huacai Chen
  2021-07-06 13:52   ` Peter Zijlstra
  4 siblings, 1 reply; 131+ messages in thread
From: Peter Zijlstra @ 2021-07-06 13:48 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 06, 2021 at 12:18:18PM +0800, Huacai Chen wrote:
> +/*
> + * Loongson-3's SFB (Store-Fill-Buffer) may buffer writes indefinitely when a
> + * tight read loop is executed, because reads take priority over writes & the
> + * hardware (incorrectly) doesn't ensure that writes will eventually occur.
> + *
> + * Since spin loops of any kind should have a cpu_relax() in them, force an SFB
> + * flush from cpu_relax() such that any pending writes will become visible as
> + * expected.
> + */
> +#define cpu_relax()	smp_mb()

Guys! You've not fixed that utter trainwreck ?!? You've taped out a
whole new architecture and you're keeping this?

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

* Re: [PATCH 09/19] LoongArch: Add system call support
  2021-07-06  4:18 ` [PATCH 09/19] LoongArch: Add system call support Huacai Chen
  2021-07-06 10:17   ` Arnd Bergmann
@ 2021-07-06 13:51   ` Thomas Gleixner
  2021-07-07  4:27     ` Huacai Chen
  2021-08-12 12:40     ` Huacai Chen
  1 sibling, 2 replies; 131+ messages in thread
From: Thomas Gleixner @ 2021-07-06 13:51 UTC (permalink / raw)
  To: Huacai Chen, Arnd Bergmann, Andy Lutomirski, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang, Huacai Chen

Chen!

On Tue, Jul 06 2021 at 12:18, Huacai Chen wrote:
> +	li.d	t1, _TIF_WORK_SYSCALL_ENTRY
> +	LONG_L	t0, tp, TI_FLAGS	# syscall tracing enabled?
> +	and	t0, t1, t0
> +	bnez	t0, syscall_trace_entry
> +
> +syscall_common:
> +	/* Check to make sure we don't jump to a bogus syscall number. */
> +	li.w	t0, __NR_syscalls
> +	sub.d	t2, a7, t0
> +	bgez	t2, illegal_syscall
> +
> +	/* Syscall number held in a7 */
> +	slli.d	t0, a7, 3		# offset into table
> +	la	t2, sys_call_table
> +	add.d	t0, t2, t0
> +	ld.d	t2, t0, 0		#syscall routine
> +	beqz    t2, illegal_syscall
> +
> +	jalr	t2			# Do The Real Thing (TM)
> +
> +	ld.d	t1, sp, PT_R11		# syscall number
> +	addi.d	t1, t1, 1		# +1 for handle_signal
> +	st.d	t1, sp, PT_R0		# save it for syscall restarting
> +	st.d	v0, sp, PT_R4		# result

Please do not add _again_ TIF handling in ASM. Please use the generic
entry code infrastructure for this. It handles the complete set of TIF
bits (if enabled in config) out of the box and it does so correctly.

Thanks,

        tglx



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

* Re: [PATCH 17/19] LoongArch: Add multi-processor (SMP) support
  2021-07-06  4:18 ` [PATCH 17/19] LoongArch: Add multi-processor (SMP) support Huacai Chen
                     ` (3 preceding siblings ...)
  2021-07-06 13:48   ` Peter Zijlstra
@ 2021-07-06 13:52   ` Peter Zijlstra
  4 siblings, 0 replies; 131+ messages in thread
From: Peter Zijlstra @ 2021-07-06 13:52 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Arnd Bergmann, Andy Lutomirski, Thomas Gleixner, Andrew Morton,
	David Airlie, Linus Torvalds, linux-arch, Xuefeng Li,
	Huacai Chen, Jiaxun Yang

On Tue, Jul 06, 2021 at 12:18:18PM +0800, Huacai Chen wrote:
> +#define __smp_load_acquire(p)							\
> +({										\
> +	union { typeof(*p) __val; char __c[1]; } __u;				\
> +	unsigned long __tmp = 0;							\
> +	compiletime_assert_atomic_type(*p);					\
> +	switch (sizeof(*p)) {							\
> +	case 1:									\
> +		*(__u8 *)__u.__c = *(volatile __u8 *)p;				\
> +		__smp_mb();							\
> +		break;								\
> +	case 2:									\
> +		*(__u16 *)__u.__c = *(volatile __u16 *)p;			\
> +		__smp_mb();							\
> +		break;								\
> +	case 4:									\
> +		__asm__ __volatile__(						\
> +		"amor.w %[val], %[tmp], %[mem]	\n"				\
> +		: [val] "=&r" (*(__u32 *)__u.__c)				\
> +		: [mem] "ZB" (*(u32 *) p), [tmp] "r" (__tmp)			\
> +		: "memory");							\
> +		break;								\
> +	case 8:									\
> +		__asm__ __volatile__(						\
> +		"amor.d %[val], %[tmp], %[mem]	\n"				\
> +		: [val] "=&r" (*(__u64 *)__u.__c)				\
> +		: [mem] "ZB" (*(u64 *) p), [tmp] "r" (__tmp)			\
> +		: "memory");							\
> +		break;								\
> +	default:								\
> +		barrier();							\
> +		__builtin_memcpy((void *)__u.__c, (const void *)p, sizeof(*p));	\
> +		__smp_mb();							\

smp_load_acquire() is explicitly not defined on longer than machine word
sizes.

> +	}									\
> +	__u.__val;								\
> +})

By using that cute AMO-fetch-or, this LOAD turns into a LOAD-STORE
cycle. Which means you cannot use it on RO memory -- also cache fail.

Surely just the volatile load and smp_mb() is faster and saner.

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

* Re: [PATCH 03/19] LoongArch: Add build infrastructure
  2021-07-06  4:18 ` [PATCH 03/19] LoongArch: Add build infrastructure Huacai Chen
  2021-07-06 10:12   ` Arnd Bergmann
  2021-07-06 10:35   ` Arnd Bergmann
@ 2021-07-07  0:00   ` Randy Dunlap
  2021-07-19  1:28     ` Huacai Chen
  2 siblings, 1 reply; 131+ messages in thread
From: Randy Dunlap @ 2021-07-07  0:00 UTC (permalink / raw)
  To: Huacai Chen, Arnd Bergmann, Andy Lutomirski, Thomas Gleixner,
	Peter Zijlstra, Andrew Morton, David Airlie, Linus Torvalds
  Cc: linux-arch, Xuefeng Li, Huacai Chen, Jiaxun Yang

Hi,

On 7/5/21 9:18 PM, Huacai Chen wrote:
> diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig
> new file mode 100644
> index 000000000000..7d5889a264c6
> --- /dev/null
> +++ b/arch/loongarch/Kconfig
> @@ -0,0 +1,403 @@
> +# SPDX-License-Identifier: GPL-2.0
> +config LOONGARCH
> +	bool
> +	default y

Some arch/ maintainers prefer to keep this list in alphabetical order...
It may make it easier to find entries -- prevent duplicates from being added.

> +	select ACPI_SYSTEM_POWER_STATES_SUPPORT	if ACPI
> +	select ARCH_BINFMT_ELF_STATE
> +	select ARCH_DISCARD_MEMBLOCK
> +	select ARCH_HAS_ACPI_TABLE_UPGRADE	if ACPI
> +	select ARCH_HAS_ELF_RANDOMIZE
> +	select ARCH_HAS_PTE_SPECIAL if !32BIT
> +	select ARCH_HAS_TICK_BROADCAST if GENERIC_CLOCKEVENTS_BROADCAST
> +	select ARCH_INLINE_READ_LOCK if !PREEMPTION
> +	select ARCH_INLINE_READ_LOCK_BH if !PREEMPTION
> +	select ARCH_INLINE_READ_LOCK_IRQ if !PREEMPTION
> +	select ARCH_INLINE_READ_LOCK_IRQSAVE if !PREEMPTION
> +	select ARCH_INLINE_READ_UNLOCK if !PREEMPTION
> +	select ARCH_INLINE_READ_UNLOCK_BH if !PREEMPTION
> +	select ARCH_INLINE_READ_UNLOCK_IRQ if !PREEMPTION
> +	select ARCH_INLINE_READ_UNLOCK_IRQRESTORE if !PREEMPTION
> +	select ARCH_INLINE_WRITE_LOCK if !PREEMPTION
> +	select ARCH_INLINE_WRITE_LOCK_BH if !PREEMPTION
> +	select ARCH_INLINE_WRITE_LOCK_IRQ if !PREEMPTION
> +	select ARCH_INLINE_WRITE_LOCK_IRQSAVE if !PREEMPTION
> +	select ARCH_INLINE_WRITE_UNLOCK if !PREEMPTION
> +	select ARCH_INLINE_WRITE_UNLOCK_BH if !PREEMPTION
> +	select ARCH_INLINE_WRITE_UNLOCK_IRQ if !PREEMPTION
> +	select ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE if !PREEMPTION
> +	select ARCH_INLINE_SPIN_TRYLOCK if !PREEMPTION
> +	select ARCH_INLINE_SPIN_TRYLOCK_BH if !PREEMPTION
> +	select ARCH_INLINE_SPIN_LOCK if !PREEMPTION
> +	select ARCH_INLINE_SPIN_LOCK_BH if !PREEMPTION
> +	select ARCH_INLINE_SPIN_LOCK_IRQ if !PREEMPTION
> +	select ARCH_INLINE_SPIN_LOCK_IRQSAVE if !PREEMPTION
> +	select ARCH_INLINE_SPIN_UNLOCK if !PREEMPTION
> +	select ARCH_INLINE_SPIN_UNLOCK_BH if !PREEMPTION
> +	select ARCH_INLINE_SPIN_UNLOCK_IRQ if !PREEMPTION
> +	select ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE if !PREEMPTION
> +	select ARCH_SUPPORTS_ACPI
> +	select ARCH_SUPPORTS_HUGETLBFS
> +	select ARCH_USE_BUILTIN_BSWAP
> +	select ARCH_USE_CMPXCHG_LOCKREF if 64BIT
> +	select ARCH_USE_QUEUED_RWLOCKS
> +	select ARCH_USE_QUEUED_SPINLOCKS
> +	select BUILDTIME_TABLE_SORT
> +	select GENERIC_ATOMIC64 if !64BIT
> +	select GENERIC_CLOCKEVENTS
> +	select GENERIC_CMOS_UPDATE
> +	select GENERIC_CPU_AUTOPROBE
> +	select GENERIC_GETTIMEOFDAY
> +	select GENERIC_IOMAP
> +	select GENERIC_IRQ_PROBE
> +	select GENERIC_IRQ_SHOW
> +	select GENERIC_LIB_ASHLDI3
> +	select GENERIC_LIB_ASHRDI3
> +	select GENERIC_LIB_CMPDI2
> +	select GENERIC_LIB_LSHRDI3
> +	select GENERIC_LIB_UCMPDI2
> +	select GENERIC_TIME_VSYSCALL
> +	select HANDLE_DOMAIN_IRQ
> +	select HAVE_ARCH_AUDITSYSCALL
> +	select HAVE_ARCH_COMPILER_H
> +	select HAVE_ARCH_MMAP_RND_BITS if MMU
> +	select HAVE_ARCH_SECCOMP_FILTER
> +	select HAVE_ARCH_TRACEHOOK
> +	select HAVE_ARCH_TRANSPARENT_HUGEPAGE
> +	select HAVE_ASM_MODVERSIONS
> +	select HAVE_CBPF_JIT if !64BIT
> +	select HAVE_EBPF_JIT if 64BIT
> +	select HAVE_CONTEXT_TRACKING
> +	select HAVE_COPY_THREAD_TLS
> +	select HAVE_DEBUG_KMEMLEAK
> +	select HAVE_DEBUG_STACKOVERFLOW
> +	select HAVE_DMA_CONTIGUOUS
> +	select HAVE_EXIT_THREAD
> +	select HAVE_FAST_GUP
> +	select HAVE_FUTEX_CMPXCHG if FUTEX
> +	select HAVE_GENERIC_VDSO
> +	select HAVE_IDE
> +	select HAVE_IOREMAP_PROT
> +	select HAVE_IRQ_EXIT_ON_IRQ_STACK
> +	select HAVE_IRQ_TIME_ACCOUNTING
> +	select HAVE_MEMBLOCK
> +	select HAVE_MEMBLOCK_NODE_MAP
> +	select HAVE_MOD_ARCH_SPECIFIC
> +	select HAVE_NMI
> +	select HAVE_PERF_EVENTS
> +	select HAVE_REGS_AND_STACK_ACCESS_API
> +	select HAVE_RSEQ
> +	select HAVE_SYSCALL_TRACEPOINTS
> +	select HAVE_VIRT_CPU_ACCOUNTING_GEN if 64BIT
> +	select IRQ_FORCED_THREADING
> +	select MODULES_USE_ELF_RELA if MODULES && 64BIT
> +	select MODULES_USE_ELF_REL if MODULES
> +	select PCI_DOMAINS if PCI
> +	select PCI_MSI_ARCH_FALLBACKS
> +	select PERF_USE_VMALLOC
> +	select RTC_LIB
> +	select SYSCTL_EXCEPTION_TRACE
> +	select VIRT_TO_BUS
> +
> +menu "Machine selection"
> +
> +choice
> +	prompt "System type"
> +	default MACH_LOONGSON64
> +
> +config MACH_LOONGSON64


[...]

> +choice
> +	prompt "Kernel page size"
> +	default PAGE_SIZE_16KB
> +
> +config PAGE_SIZE_4KB
> +	bool "4kB"
> +	help
> +	  This option select the standard 4kB Linux page size.

	              selects

> +
> +config PAGE_SIZE_16KB
> +	bool "16kB"
> +	help
> +	  This option select the standard 16kB Linux page size.

	              selects

> +
> +config PAGE_SIZE_64KB
> +	bool "64kB"
> +	help
> +	  This option select the standard 64kB Linux page size.

	              selects

> +
> +endchoice


-- 
~Randy


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

* Re: [PATCH 00/19] arch: Add basic LoongArch support
  2021-07-06 10:11 ` [PATCH 00/19] arch: Add basic LoongArch support Arnd Bergmann
@ 2021-07-07  3:04   ` Huacai Chen
  2021-07-07  7:28     ` Arnd Bergmann
  0 siblings, 1 reply; 131+ messages in thread
From: Huacai Chen @ 2021-07-07  3:04 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Huacai Chen, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds, linux-arch,
	Xuefeng Li, Jiaxun Yang, Chen Zhu

Hi, Arnd,

On Tue, Jul 6, 2021 at 6:12 PM Arnd Bergmann <arnd@arndb.de> wrote:
>
> On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
> >
> > LoongArch is a new RISC ISA, which is a bit like MIPS or RISC-V.
> > LoongArch includes a reduced 32-bit version (LA32R), a standard 32-bit
> > version (LA32S) and a 64-bit version (LA64). LoongArch use ACPI as its
> > boot protocol LoongArch-specific interrupt controllers (similar to APIC)
> > are already added in the next revision of ACPI Specification (current
> > revision is 6.4).
> >
> > This patchset is adding basic LoongArch support in mainline kernel, we
> > can see a complete snapshot here:
> > https://github.com/loongson/linux/tree/loongarch-next
> >
> > Cross-compile tool chain to build kernel:
> > https://github.com/loongson/build-tools/releases
> >
> > Loongson and LoongArch documentations:
> > https://github.com/loongson/LoongArch-Documentation
> >
> > LoongArch-specific interrupt controllers:
> > https://mantis.uefi.org/mantis/view.php?id=2203
>
> Thanks a lot for your submission, I've been waiting for this, and have just
> completed an initial quick review, will send out the individual replies in a
> bit.
Thanks for your reply. I will reply to other mails with some delays
because I need some time to absorb so much information.

>
> I have a few high-level comments about things that need to be improved
> before merging:
>
> 1. Copyright statements: I can see that you copied a lot of code from arch/mips,
>  and then a bit from arch/arm64 and possibly arch/riscv/, but every file just
>  lists loongson as the copyright holder and one or more employees as authors.
>  This clearly has to be resolved by listing the original authors as well.
As you know, old Loongson processors are based on MIPS, so we are
familiar with MIPS, from hardware designer to software developer. This
is the root cause why we copy so much code from MIPS, and LoongArch
actually has many similarities as MIPS. But anyway, it is our fault to
remove the copyright holders, so this will be resolved, of course.

>
> 2. Reducing the amount of copied code: my impression is that the bits you
>    wrote yourself are generally of higher quality than the bits you copied from
>    mips, usually because those have grown over a long time to accommodate
>    cases you should not worry about. In cases where there was no generic
>    implementation, we should try to come up with a version that can be shared
>    instead of adding another copy.
We know MIPS is out of date, but our knowledge is not enough, so we
are not able to find the best way always, and the MIPS code "just
works". This will be improved in future versions.

>
> 3. 32-bit support: There are small bits of code for 32-bit support everywhere
>    throughout the code base, but there is also code that does not look like
>    it would work in that configuration. I would suggest you split out all 32-bit
>    code into a separate patch, the way you have done for SMP and NUMA
>    support. That way it can be reviewed separately, or deferred until when
>    you actually add support for a 32-bit platform. Please also clarify which
>    modes you plan to support: should every 64-bit kernel be able to run
>    LA32R and LA32S user space binaries, or do you require running the same
>    instruction set in both kernel and user space as RISC-V does?
>    Do you plan to have MIPS N32 style user space on 64-bit kernels?
No N32 style user space in our current plan, but the 64-bit kernel is
supposed to run 32-bit applications in the near future.

>
> 4. Toolchain sources: I see you only have binaries for an older gcc-8.3
>     release and no sources so far. I assume you are already busy porting
>     the internal patches to the latest gcc and will post that soon, but my
>     feeling is that the kernel port should not get merged until we have a
>     confirmation from the toolchain maintainers that they plan to merge
>     support for their following release. We should however make sure that
>     your kernel port is completely reviewed and can just get merged once
>     we get there.
Toolchain is maintained by other developers, it will be open source of
course, I hope it won't be too late. :)

>
> 5. Platform support: You have copied the underlying logic from arch/mips,
>     but that still uses a method where most platforms (not the new
>     "generic" version) are mutually exclusive. Since you only support
>     one platform right now, it would be best to just simplify it to the point
>     where no platform specific code is needed in arch/loongarch/ and
>     it just works. If you add 32-bit support later on, that obviously requires
>     making a choice between two or three incompatible options.
I will improve it, and I will need some help for this.

Huacai
>
>         Arnd

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

* Re: [PATCH 09/19] LoongArch: Add system call support
  2021-07-06 10:17   ` Arnd Bergmann
  2021-07-06 10:58     ` Arnd Bergmann
@ 2021-07-07  4:24     ` Huacai Chen
  2021-07-07  6:44       ` Arnd Bergmann
  1 sibling, 1 reply; 131+ messages in thread
From: Huacai Chen @ 2021-07-07  4:24 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Huacai Chen, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds, linux-arch,
	Xuefeng Li, Jiaxun Yang

Hi, Arnd,

On Tue, Jul 6, 2021 at 6:17 PM Arnd Bergmann <arnd@arndb.de> wrote:
>
> On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
>
> > diff --git a/arch/loongarch/include/asm/seccomp.h b/arch/loongarch/include/asm/seccomp.h
> > new file mode 100644
> > index 000000000000..220c885f5478
> > --- /dev/null
> > +++ b/arch/loongarch/include/asm/seccomp.h
> > @@ -0,0 +1,9 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +#ifndef __ASM_SECCOMP_H
> > +#define __ASM_SECCOMP_H
> > +
> > +#include <linux/unistd.h>
> > +
> > +#include <asm-generic/seccomp.h>
> > +
> > +#endif /* __ASM_SECCOMP_H */
>
> I would expect this file to not be needed.
>
> > diff --git a/arch/loongarch/include/asm/uaccess.h b/arch/loongarch/include/asm/uaccess.h
> > new file mode 100644
> > index 000000000000..b760aa0a3bc6
> > --- /dev/null
> > +++ b/arch/loongarch/include/asm/uaccess.h
> > @@ -0,0 +1,453 @@
> > + * The fs value determines whether argument validity checking should be
> > + * performed or not.  If get_fs() == USER_DS, checking is performed, with
> > + * get_fs() == KERNEL_DS, checking is bypassed.
> > + *
> > + * For historical reasons, these macros are grossly misnamed.
> > + */
>
> You removed set_fs()/get_fs(), which is good, but you forgot to remove
> the comment.
OK, thanks.

>
> > diff --git a/arch/loongarch/include/uapi/asm/unistd.h b/arch/loongarch/include/uapi/asm/unistd.h
> > new file mode 100644
> > index 000000000000..6c194d740ed0
> > --- /dev/null
> > +++ b/arch/loongarch/include/uapi/asm/unistd.h
> > @@ -0,0 +1,7 @@
> > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > +#define __ARCH_WANT_NEW_STAT
>
> Why do you need newstat? I think now that we have statx and the libc
> emulation code on top of it, there is probably no need to support both
> on the kernel side.
>
> > +#define __ARCH_WANT_SYS_CLONE
> > +#define __ARCH_WANT_SYS_CLONE3
>
> Similarly, if you have clone3, you should not need clone.
>
> > +#define __ARCH_WANT_SET_GET_RLIMIT
>
> And here for prlimit64
Is newstat()/clone()/setrlimit() completely unacceptable in a new
arch? If not, I want to keep it here for compatibility, because there
are some existing distros.

>
>
> > +void *sys_call_table[__NR_syscalls] = {
> > +       [0 ... __NR_syscalls - 1] = sys_ni_syscall,
> > +#include <asm/unistd.h>
> > +       __SYSCALL(__NR_clone, __sys_clone)
> > +       __SYSCALL(__NR_clone3, __sys_clone3)
> > +};
>
> I would suggest expressing this as
>
> #defined sys_clone3 __sys_clone3
>
> instead of overriding the other entries.
OK, thanks.

Huacai
>
>           Arnd

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

* Re: [PATCH 09/19] LoongArch: Add system call support
  2021-07-06 13:51   ` Thomas Gleixner
@ 2021-07-07  4:27     ` Huacai Chen
  2021-08-12 12:40     ` Huacai Chen
  1 sibling, 0 replies; 131+ messages in thread
From: Huacai Chen @ 2021-07-07  4:27 UTC (permalink / raw)
  To: Thomas Gleixner
  Cc: Huacai Chen, Arnd Bergmann, Andy Lutomirski, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds, linux-arch,
	Xuefeng Li, Jiaxun Yang

Hi, Thomas,

On Tue, Jul 6, 2021 at 9:51 PM Thomas Gleixner <tglx@linutronix.de> wrote:
>
> Chen!
>
> On Tue, Jul 06 2021 at 12:18, Huacai Chen wrote:
> > +     li.d    t1, _TIF_WORK_SYSCALL_ENTRY
> > +     LONG_L  t0, tp, TI_FLAGS        # syscall tracing enabled?
> > +     and     t0, t1, t0
> > +     bnez    t0, syscall_trace_entry
> > +
> > +syscall_common:
> > +     /* Check to make sure we don't jump to a bogus syscall number. */
> > +     li.w    t0, __NR_syscalls
> > +     sub.d   t2, a7, t0
> > +     bgez    t2, illegal_syscall
> > +
> > +     /* Syscall number held in a7 */
> > +     slli.d  t0, a7, 3               # offset into table
> > +     la      t2, sys_call_table
> > +     add.d   t0, t2, t0
> > +     ld.d    t2, t0, 0               #syscall routine
> > +     beqz    t2, illegal_syscall
> > +
> > +     jalr    t2                      # Do The Real Thing (TM)
> > +
> > +     ld.d    t1, sp, PT_R11          # syscall number
> > +     addi.d  t1, t1, 1               # +1 for handle_signal
> > +     st.d    t1, sp, PT_R0           # save it for syscall restarting
> > +     st.d    v0, sp, PT_R4           # result
>
> Please do not add _again_ TIF handling in ASM. Please use the generic
> entry code infrastructure for this. It handles the complete set of TIF
> bits (if enabled in config) out of the box and it does so correctly.
OK, thanks, I will improve.

Huacai
>
> Thanks,
>
>         tglx
>
>

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

* Re: [PATCH 09/19] LoongArch: Add system call support
  2021-07-07  4:24     ` Huacai Chen
@ 2021-07-07  6:44       ` Arnd Bergmann
  2021-07-07  7:00         ` Huacai Chen
  2021-07-09  8:44         ` Huacai Chen
  0 siblings, 2 replies; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-07  6:44 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Huacai Chen, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds, linux-arch,
	Xuefeng Li, Jiaxun Yang

On Wed, Jul 7, 2021 at 6:24 AM Huacai Chen <chenhuacai@gmail.com> wrote:
> On Tue, Jul 6, 2021 at 6:17 PM Arnd Bergmann <arnd@arndb.de> wrote:
> > On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
> > > diff --git a/arch/loongarch/include/uapi/asm/unistd.h b/arch/loongarch/include/uapi/asm/unistd.h
> > > new file mode 100644
> > > index 000000000000..6c194d740ed0
> > > --- /dev/null
> > > +++ b/arch/loongarch/include/uapi/asm/unistd.h
> > > @@ -0,0 +1,7 @@
> > > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > > +#define __ARCH_WANT_NEW_STAT
> >
> > Why do you need newstat? I think now that we have statx and the libc
> > emulation code on top of it, there is probably no need to support both
> > on the kernel side.
> >
> > > +#define __ARCH_WANT_SYS_CLONE
> > > +#define __ARCH_WANT_SYS_CLONE3
> >
> > Similarly, if you have clone3, you should not need clone.
> >
> > > +#define __ARCH_WANT_SET_GET_RLIMIT
> >
> > And here for prlimit64
>
> Is newstat()/clone()/setrlimit() completely unacceptable in a new
> arch? If not, I want to keep it here for compatibility, because there
> are some existing distros.

I'd say they should go. None of these are normally called directly by
applications, so if you just update the C library to redirect the user
level interfaces to the new system calls, I expect no major problems
here as long as you update libc along with the kernel in the existing
distros.
Any application using seccomp to allow only the old system calls
may need a small update, but it would need that anyway to work
with future libc implementations.

Generally speaking there should be no expectation that the
system call interface is stable until the port is upstream. Note that
you will face a similar problem with the libc port, which may also
change its interface from what you are using internally.

       Arnd

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

* Re: [PATCH 09/19] LoongArch: Add system call support
  2021-07-07  6:44       ` Arnd Bergmann
@ 2021-07-07  7:00         ` Huacai Chen
  2021-07-09  8:44         ` Huacai Chen
  1 sibling, 0 replies; 131+ messages in thread
From: Huacai Chen @ 2021-07-07  7:00 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Huacai Chen, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds, linux-arch,
	Xuefeng Li, Jiaxun Yang

Hi, Arnd,

On Wed, Jul 7, 2021 at 2:44 PM Arnd Bergmann <arnd@arndb.de> wrote:
>
> On Wed, Jul 7, 2021 at 6:24 AM Huacai Chen <chenhuacai@gmail.com> wrote:
> > On Tue, Jul 6, 2021 at 6:17 PM Arnd Bergmann <arnd@arndb.de> wrote:
> > > On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
> > > > diff --git a/arch/loongarch/include/uapi/asm/unistd.h b/arch/loongarch/include/uapi/asm/unistd.h
> > > > new file mode 100644
> > > > index 000000000000..6c194d740ed0
> > > > --- /dev/null
> > > > +++ b/arch/loongarch/include/uapi/asm/unistd.h
> > > > @@ -0,0 +1,7 @@
> > > > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > > > +#define __ARCH_WANT_NEW_STAT
> > >
> > > Why do you need newstat? I think now that we have statx and the libc
> > > emulation code on top of it, there is probably no need to support both
> > > on the kernel side.
> > >
> > > > +#define __ARCH_WANT_SYS_CLONE
> > > > +#define __ARCH_WANT_SYS_CLONE3
> > >
> > > Similarly, if you have clone3, you should not need clone.
> > >
> > > > +#define __ARCH_WANT_SET_GET_RLIMIT
> > >
> > > And here for prlimit64
> >
> > Is newstat()/clone()/setrlimit() completely unacceptable in a new
> > arch? If not, I want to keep it here for compatibility, because there
> > are some existing distros.
>
> I'd say they should go. None of these are normally called directly by
> applications, so if you just update the C library to redirect the user
> level interfaces to the new system calls, I expect no major problems
> here as long as you update libc along with the kernel in the existing
> distros.
> Any application using seccomp to allow only the old system calls
> may need a small update, but it would need that anyway to work
> with future libc implementations.
>
> Generally speaking there should be no expectation that the
> system call interface is stable until the port is upstream. Note that
> you will face a similar problem with the libc port, which may also
> change its interface from what you are using internally.
OK, I know, thanks.

Huacai
>
>        Arnd

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

* Re: [PATCH 00/19] arch: Add basic LoongArch support
  2021-07-07  3:04   ` Huacai Chen
@ 2021-07-07  7:28     ` Arnd Bergmann
  2021-07-29 16:48       ` Huacai Chen
  0 siblings, 1 reply; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-07  7:28 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Huacai Chen, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds, linux-arch,
	Xuefeng Li, Jiaxun Yang, Chen Zhu

On Wed, Jul 7, 2021 at 5:04 AM Huacai Chen <chenhuacai@gmail.com> wrote:
> On Tue, Jul 6, 2021 at 6:12 PM Arnd Bergmann <arnd@arndb.de> wrote:
> > 3. 32-bit support: There are small bits of code for 32-bit support everywhere
> >    throughout the code base, but there is also code that does not look like
> >    it would work in that configuration. I would suggest you split out all 32-bit
> >    code into a separate patch, the way you have done for SMP and NUMA
> >    support. That way it can be reviewed separately, or deferred until when
> >    you actually add support for a 32-bit platform. Please also clarify which
> >    modes you plan to support: should every 64-bit kernel be able to run
> >    LA32R and LA32S user space binaries, or do you require running the same
> >    instruction set in both kernel and user space as RISC-V does?
> >    Do you plan to have MIPS N32 style user space on 64-bit kernels?
> No N32 style user space in our current plan, but the 64-bit kernel is
> supposed to run 32-bit applications in the near future.

Ok, sounds good. Adding compat support for a native 32-bit ABI has gotten
a lot easier over time.

On that note, what are the requirements to the kernel for supporting LBT
with aarch64/mips/x86 tasks? Does this involve handling the system calls
(ioctl in particular) for those architectures in the kernel as well, or do you
expect this to be done entirely in user space?

The same topic has come up in the past, as there are at least three projects
that need support for emulating foreign-architecture system calls (qemu,
fex and tango). If there is sufficient demand, we may end up generalizing the
kernel's compat layer to support multiple such ABIs instead of just native
and compat.

> > 4. Toolchain sources: I see you only have binaries for an older gcc-8.3
> >     release and no sources so far. I assume you are already busy porting
> >     the internal patches to the latest gcc and will post that soon, but my
> >     feeling is that the kernel port should not get merged until we have a
> >     confirmation from the toolchain maintainers that they plan to merge
> >     support for their following release. We should however make sure that
> >     your kernel port is completely reviewed and can just get merged once
> >     we get there.
>
> Toolchain is maintained by other developers, it will be open source of
> course, I hope it won't be too late. :)

Right, I meant 'you' as in someone @loongson.cn, not necessarily you
personally.

> > 5. Platform support: You have copied the underlying logic from arch/mips,
> >     but that still uses a method where most platforms (not the new
> >     "generic" version) are mutually exclusive. Since you only support
> >     one platform right now, it would be best to just simplify it to the point
> >     where no platform specific code is needed in arch/loongarch/ and
> >     it just works. If you add 32-bit support later on, that obviously requires
> >     making a choice between two or three incompatible options.
>
> I will improve it, and I will need some help for this.

It should be mostly covered by the comments I made on the individual
comments already, but let me know if you have more specific questions.

       Arnd

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

* Re: [PATCH 06/19] LoongArch: Add exception/interrupt handling
  2021-07-06 11:06   ` Peter Zijlstra
@ 2021-07-07 13:56     ` Nicholas Piggin
  2021-07-27 14:10       ` Peter Zijlstra
  0 siblings, 1 reply; 131+ messages in thread
From: Nicholas Piggin @ 2021-07-07 13:56 UTC (permalink / raw)
  To: Huacai Chen, Peter Zijlstra
  Cc: David Airlie, Andrew Morton, Arnd Bergmann, Huacai Chen,
	Jiaxun Yang, linux-arch, Xuefeng Li, Andy Lutomirski,
	Thomas Gleixner, Linus Torvalds

Excerpts from Peter Zijlstra's message of July 6, 2021 9:06 pm:
> On Tue, Jul 06, 2021 at 12:18:07PM +0800, Huacai Chen wrote:
>> +	.align	5	/* 32 byte rollback region */
>> +SYM_FUNC_START(__arch_cpu_idle)
>> +	/* start of rollback region */
>> +	LONG_L	t0, tp, TI_FLAGS
>> +	nop
>> +	andi	t0, t0, _TIF_NEED_RESCHED
>> +	bnez	t0, 1f
>> +	nop
>> +	nop
>> +	nop
>> +	idle	0
>> +	/* end of rollback region */
>> +1:
>> +	jirl	zero, ra, 0
>> +SYM_FUNC_END(__arch_cpu_idle)
> 
>> +/*
>> + * Common Vectored Interrupt
>> + * Complete the register saves and invoke the do_vi() handler
>> + */
>> +SYM_FUNC_START(except_vec_vi_handler)
>> +	la	t1, __arch_cpu_idle
>> +	LONG_L  t0, sp, PT_EPC
>> +	/* 32 byte rollback region */
>> +	ori	t0, t0, 0x1f
>> +	xori	t0, t0, 0x1f
>> +	bne	t0, t1, 1f
>> +	LONG_S  t0, sp, PT_EPC
> 
> Seriously, you're having your interrupt handler recover from the idle
> race? On a *new* architecture?

It's heavily derived from MIPS (does that make the wholesale replacement 
of arch/mips copyright headers a bit questionable?).

I don't think it's such a bad trick though -- restartable sequences 
before they were cool. It can let you save an irq disable in some
cases (depending on the arch of course).

Thanks,
Nick

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

* Re: [PATCH 10/19] LoongArch: Add signal handling support
  2021-07-06 10:17   ` Arnd Bergmann
  2021-07-06 10:59     ` Arnd Bergmann
@ 2021-07-08 13:04     ` Huacai Chen
  2021-07-08 13:23       ` Arnd Bergmann
  1 sibling, 1 reply; 131+ messages in thread
From: Huacai Chen @ 2021-07-08 13:04 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Huacai Chen, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds, linux-arch,
	Xuefeng Li, Jiaxun Yang, Eric W. Biederman

Hi Arnd,

On Tue, Jul 6, 2021 at 6:17 PM Arnd Bergmann <arnd@arndb.de> wrote:
>
> On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
> >
> > This patch adds signal handling support for LoongArch.
> >
> > Signed-off-by: Huacai Chen <chenhuacai@loongson.cn>
>
> Eric Biederman should review this part as well.
>
> > --- /dev/null
> > +++ b/arch/loongarch/include/asm/sigcontext.h
> > @@ -0,0 +1,10 @@
> > +/* SPDX-License-Identifier: GPL-2.0 */
> > +/*
> > + * Copyright (C) 2020-2021 Loongson Technology Corporation Limited
> > + */
> > +#ifndef _ASM_SIGCONTEXT_H
> > +#define _ASM_SIGCONTEXT_H
> > +
> > +#include <uapi/asm/sigcontext.h>
> > +
> > +#endif /* _ASM_SIGCONTEXT_H */
>
> Remove this file
OK, thanks.

>
> > + */
> > +#ifndef _UAPI_ASM_SIGINFO_H
> > +#define _UAPI_ASM_SIGINFO_H
> > +
> > +#if _LOONGARCH_SZLONG == 32
> > +#define __ARCH_SI_PREAMBLE_SIZE (3 * sizeof(int))
> > +#else
> > +#define __ARCH_SI_PREAMBLE_SIZE (4 * sizeof(int))
> > +#endif
>
> These are no longer used.
OK, thanks.

>
> > +
> > +#ifndef _NSIG
> > +#define _NSIG          128
> > +#endif
>
> Everything else uses 64 here, except for MIPS.
Once before we also wanted to use 64, but we also want to use LBT to
execute X86/MIPS/ARM binaries, so we chose the largest value (128).
Some applications, such as sighold02 in LTP, will fail if _NSIG is not
big enough.

Huacai
>
> > +#define _NSIG_BPW      __BITS_PER_LONG
> > +#define _NSIG_WORDS    (_NSIG / _NSIG_BPW)
> > +
> > +#define SIGHUP          1
> > +#define SIGINT          2
> > +#define SIGQUIT                 3
> > +#define SIGILL          4
> > +#define SIGTRAP                 5
> > +#define SIGABRT                 6
> > +#define SIGIOT          6
> > +#define SIGBUS          7
> > +#define SIGFPE          8
> > +#define SIGKILL                 9
> > +#define SIGUSR1                10
> > +#define SIGSEGV                11
> > +#define SIGUSR2                12
> > +#define SIGPIPE                13
> > +#define SIGALRM                14
> > +#define SIGTERM                15
> > +#define SIGSTKFLT      16
> > +#define SIGCHLD                17
> > +#define SIGCONT                18
> > +#define SIGSTOP                19
> > +#define SIGTSTP                20
> > +#define SIGTTIN                21
> > +#define SIGTTOU                22
> > +#define SIGURG         23
> > +#define SIGXCPU                24
> > +#define SIGXFSZ                25
> > +#define SIGVTALRM      26
> > +#define SIGPROF                27
> > +#define SIGWINCH       28
> > +#define SIGIO          29
> > +#define SIGPOLL                SIGIO
> > +#define SIGPWR         30
> > +#define SIGSYS         31
> > +#define        SIGUNUSED       31
>
> Please try to use the asm-generic version of these definitions instead
> copying them. If you need something different, you can add an #ifdef there,
> and then we can discuss whether the difference makes sense.
>
>
>         Arnd

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

* Re: [PATCH 10/19] LoongArch: Add signal handling support
  2021-07-08 13:04     ` Huacai Chen
@ 2021-07-08 13:23       ` Arnd Bergmann
  2021-07-09  9:24         ` Huacai Chen
  0 siblings, 1 reply; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-08 13:23 UTC (permalink / raw)
  To: Huacai Chen
  Cc: Huacai Chen, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds, linux-arch,
	Xuefeng Li, Jiaxun Yang, Eric W. Biederman

On Thu, Jul 8, 2021 at 3:04 PM Huacai Chen <chenhuacai@gmail.com> wrote:
> On Tue, Jul 6, 2021 at 6:17 PM Arnd Bergmann <arnd@arndb.de> wrote:
> > On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
> > > +
> > > +#ifndef _NSIG
> > > +#define _NSIG          128
> > > +#endif
> >
> > Everything else uses 64 here, except for MIPS.
>
> Once before we also wanted to use 64, but we also want to use LBT to
> execute X86/MIPS/ARM binaries, so we chose the largest value (128).
> Some applications, such as sighold02 in LTP, will fail if _NSIG is not
> big enough.

Have you tried separating the in-kernel _NSIG from the number used
in the loongarch ABI? This may require a few changes to architecture
independent signal handling code, but I think it would be a cleaner
solution, and make it easier to port existing software without having
to special-case loongarch along with mips.

      Arnd

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

* Re: [PATCH 09/19] LoongArch: Add system call support
  2021-07-07  6:44       ` Arnd Bergmann
  2021-07-07  7:00         ` Huacai Chen
@ 2021-07-09  8:44         ` Huacai Chen
  1 sibling, 0 replies; 131+ messages in thread
From: Huacai Chen @ 2021-07-09  8:44 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Huacai Chen, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds, linux-arch,
	Xuefeng Li, Jiaxun Yang

Hi, Arnd,

On Wed, Jul 7, 2021 at 2:44 PM Arnd Bergmann <arnd@arndb.de> wrote:
>
> On Wed, Jul 7, 2021 at 6:24 AM Huacai Chen <chenhuacai@gmail.com> wrote:
> > On Tue, Jul 6, 2021 at 6:17 PM Arnd Bergmann <arnd@arndb.de> wrote:
> > > On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
> > > > diff --git a/arch/loongarch/include/uapi/asm/unistd.h b/arch/loongarch/include/uapi/asm/unistd.h
> > > > new file mode 100644
> > > > index 000000000000..6c194d740ed0
> > > > --- /dev/null
> > > > +++ b/arch/loongarch/include/uapi/asm/unistd.h
> > > > @@ -0,0 +1,7 @@
> > > > +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
> > > > +#define __ARCH_WANT_NEW_STAT
> > >
> > > Why do you need newstat? I think now that we have statx and the libc
> > > emulation code on top of it, there is probably no need to support both
> > > on the kernel side.
> > >
> > > > +#define __ARCH_WANT_SYS_CLONE
> > > > +#define __ARCH_WANT_SYS_CLONE3
> > >
> > > Similarly, if you have clone3, you should not need clone.
> > >
> > > > +#define __ARCH_WANT_SET_GET_RLIMIT
> > >
> > > And here for prlimit64
> >
> > Is newstat()/clone()/setrlimit() completely unacceptable in a new
> > arch? If not, I want to keep it here for compatibility, because there
> > are some existing distros.
>
> I'd say they should go. None of these are normally called directly by
> applications, so if you just update the C library to redirect the user
> level interfaces to the new system calls, I expect no major problems
> here as long as you update libc along with the kernel in the existing
> distros.
> Any application using seccomp to allow only the old system calls
> may need a small update, but it would need that anyway to work
> with future libc implementations.
>
> Generally speaking there should be no expectation that the
> system call interface is stable until the port is upstream. Note that
> you will face a similar problem with the libc port, which may also
> change its interface from what you are using internally.
I found that the latest glibc is not ready for clone3, the last
patchset [1] still breaks something, so I think we should keep clone.
For newstat, I found that the latest glibc only use statx for 32bit
kernel, while 64bit kernel still use newstat.

So, it seems that we can only remove __ARCH_WANT_SET_GET_RLIMIT.

[1] https://patchwork.sourceware.org/project/glibc/patch/20210601145516.3553627-2-hjl.tools@gmail.com/

Huacai
>
>        Arnd

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

* Re: [PATCH 10/19] LoongArch: Add signal handling support
  2021-07-08 13:23       ` Arnd Bergmann
@ 2021-07-09  9:24         ` Huacai Chen
  2021-07-09 10:22           ` Arnd Bergmann
  0 siblings, 1 reply; 131+ messages in thread
From: Huacai Chen @ 2021-07-09  9:24 UTC (permalink / raw)
  To: Arnd Bergmann, yili0568
  Cc: Huacai Chen, Andy Lutomirski, Thomas Gleixner, Peter Zijlstra,
	Andrew Morton, David Airlie, Linus Torvalds, linux-arch,
	Xuefeng Li, Jiaxun Yang, Eric W. Biederman

Hi, Arnd,

On Thu, Jul 8, 2021 at 9:30 PM Arnd Bergmann <arnd@arndb.de> wrote:
>
> On Thu, Jul 8, 2021 at 3:04 PM Huacai Chen <chenhuacai@gmail.com> wrote:
> > On Tue, Jul 6, 2021 at 6:17 PM Arnd Bergmann <arnd@arndb.de> wrote:
> > > On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
> > > > +
> > > > +#ifndef _NSIG
> > > > +#define _NSIG          128
> > > > +#endif
> > >
> > > Everything else uses 64 here, except for MIPS.
> >
> > Once before we also wanted to use 64, but we also want to use LBT to
> > execute X86/MIPS/ARM binaries, so we chose the largest value (128).
> > Some applications, such as sighold02 in LTP, will fail if _NSIG is not
> > big enough.
>
> Have you tried separating the in-kernel _NSIG from the number used
> in the loongarch ABI? This may require a few changes to architecture
> independent signal handling code, but I think it would be a cleaner
> solution, and make it easier to port existing software without having
> to special-case loongarch along with mips.
Jun Yi (yili0568@gmail.com) is my colleague who develops LBT software,
he has some questions about how to "separate the in-kernel _NSIG from
the number used in the LoongArch ABI".

Huacai
>
>       Arnd

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

* Re: [PATCH 10/19] LoongArch: Add signal handling support
  2021-07-09  9:24         ` Huacai Chen
@ 2021-07-09 10:22           ` Arnd Bergmann
  2021-07-09 14:49             ` Eric W. Biederman
  0 siblings, 1 reply; 131+ messages in thread
From: Arnd Bergmann @ 2021-07-09 10:22 UTC (permalink / raw)
  To: Huacai Chen
  Cc: yili0568, Huacai Chen, Andy Lutomirski, Thomas Gleixner,
	Peter Zijlstra, Andrew Morton, David Airlie, Linus Torvalds,
	linux-arch, Xuefeng Li, Jiaxun Yang, Eric W. Biederman

On Fri, Jul 9, 2021 at 11:24 AM Huacai Chen <chenhuacai@gmail.com> wrote:
> On Thu, Jul 8, 2021 at 9:30 PM Arnd Bergmann <arnd@arndb.de> wrote:
> > On Thu, Jul 8, 2021 at 3:04 PM Huacai Chen <chenhuacai@gmail.com> wrote:
> > > On Tue, Jul 6, 2021 at 6:17 PM Arnd Bergmann <arnd@arndb.de> wrote:
> > > > On Tue, Jul 6, 2021 at 6:18 AM Huacai Chen <chenhuacai@loongson.cn> wrote:
> > > > > +
> > > > > +#ifndef _NSIG
> > > > > +#define _NSIG          128
> > > > > +#endif
> > > >
> > > > Everything else uses 64 here, except for MIPS.
> > >
> > > Once before we also wanted to use 64, but we also want to use LBT to
> > > execute X86/MIPS/ARM binaries, so we chose the largest value (128).
> > > Some applications, such as sighold02 in LTP, will fail if _NSIG is not
> > > big enough.
> >
> > Have you tried separating the in-kernel _NSIG from the number used
> > in the loongarch ABI? This may require a few changes to architecture
> > independent signal handling code, but I think it would be a cleaner
> > solution, and make it easier to port existing software without having
> > to special-case loongarch along with mips.
>
> Jun Yi (yili0568@gmail.com) is my colleague who develops LBT software,
> he has some questions about how to "separate the in-kernel _NSIG from
> the number used in the LoongArch ABI".

This ties in with how the foreign syscall implementation is done for LBT,
and I don't know what you have today, on that side, since it is not part
of the initial submission.

I think what this means in the end is that any system call that takes
a sigset_t argument will have to behave differently based on the
architecture. At the moment, we have

- compat_old_sigset_t (a