History

Merge tag 'loongarch-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson

Pull LoongArch updates from Huacai Chen: - Switch to relative exception tables - Add unaligned access support - Add alternative runtime patching mechanism - Add FDT booting support from efi system table - Add suspend/hibernation (ACPI S3/S4) support - Add basic STACKPROTECTOR support - Add ftrace (function tracer) support - Update the default config file * tag 'loongarch-6.2' of git://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson: (24 commits) LoongArch: Update Loongson-3 default config file LoongArch: modules/ftrace: Initialize PLT at load time LoongArch/ftrace: Add HAVE_FUNCTION_GRAPH_RET_ADDR_PTR support LoongArch/ftrace: Add HAVE_DYNAMIC_FTRACE_WITH_ARGS support LoongArch/ftrace: Add HAVE_DYNAMIC_FTRACE_WITH_REGS support LoongArch/ftrace: Add dynamic function graph tracer support LoongArch/ftrace: Add dynamic function tracer support LoongArch/ftrace: Add recordmcount support LoongArch/ftrace: Add basic support LoongArch: module: Use got/plt section indices for relocations LoongArch: Add basic STACKPROTECTOR support LoongArch: Add hibernation (ACPI S4) support LoongArch: Add suspend (ACPI S3) support LoongArch: Add processing ISA Node in DeviceTree LoongArch: Add FDT booting support from efi system table LoongArch: Use alternative to optimize libraries LoongArch: Add alternative runtime patching mechanism LoongArch: Add unaligned access support LoongArch: BPF: Add BPF exception tables LoongArch: Remove the .fixup section usage ...

Linus Torvalds committed 3 years ago

Commit 2f26e422 parents 96bab5b 5535f4f72 files · +3645 −187Browse files

Unified · Split

72 files
  1. Documentation/admin-guide/sysctl/kernel.rst +4 −4
  2. arch/loongarch/Kconfig +20 −0
  3. arch/loongarch/Makefile +8 −0
  4. arch/loongarch/configs/loongson3_defconfig +32 −24
  5. arch/loongarch/include/asm/acpi.h +10 −0
  6. arch/loongarch/include/asm/alternative-asm.h +82 −0
  7. arch/loongarch/include/asm/alternative.h +111 −0
  8. arch/loongarch/include/asm/asm-extable.h +65 −0
  9. arch/loongarch/include/asm/bootinfo.h +1 −0
  10. arch/loongarch/include/asm/bugs.h +15 −0
  11. arch/loongarch/include/asm/efi.h +1 −0
  12. arch/loongarch/include/asm/extable.h +47 −0
  13. arch/loongarch/include/asm/ftrace.h +68 −0
  14. arch/loongarch/include/asm/futex.h +7 −20
  15. arch/loongarch/include/asm/gpr-num.h +22 −0
  16. arch/loongarch/include/asm/inst.h +46 −0
  17. arch/loongarch/include/asm/loongson.h +3 −0
  18. arch/loongarch/include/asm/module.h +16 −11
  19. arch/loongarch/include/asm/module.lds.h +1 −0
  20. arch/loongarch/include/asm/setup.h +1 −0
  21. arch/loongarch/include/asm/stackprotector.h +38 −0
  22. arch/loongarch/include/asm/string.h +5 −0
  23. arch/loongarch/include/asm/thread_info.h +1 −1
  24. arch/loongarch/include/asm/time.h +1 −0
  25. arch/loongarch/include/asm/uaccess.h +6 −18
  26. arch/loongarch/include/asm/unwind.h +2 −1
  27. arch/loongarch/kernel/Makefile +15 −1
  28. arch/loongarch/kernel/acpi.c +15 −2
  29. arch/loongarch/kernel/alternative.c +246 −0
  30. arch/loongarch/kernel/asm-offsets.c +15 −0
  31. arch/loongarch/kernel/efi.c +14 −1
  32. arch/loongarch/kernel/env.c +2 −0
  33. arch/loongarch/kernel/fpu.S +2 −3
  34. arch/loongarch/kernel/ftrace.c +73 −0
  35. arch/loongarch/kernel/ftrace_dyn.c +273 −0
  36. arch/loongarch/kernel/inst.c +127 −0
  37. arch/loongarch/kernel/mcount.S +96 −0
  38. arch/loongarch/kernel/mcount_dyn.S +149 −0
  39. arch/loongarch/kernel/module-sections.c +39 −25
  40. arch/loongarch/kernel/module.c +63 −12
  41. arch/loongarch/kernel/numa.c +16 −1
  42. arch/loongarch/kernel/process.c +6 −0
  43. arch/loongarch/kernel/reset.c +5 −0
  44. arch/loongarch/kernel/setup.c +148 −1
  45. arch/loongarch/kernel/smp.c +35 −0
  46. arch/loongarch/kernel/switch.S +5 −0
  47. arch/loongarch/kernel/time.c +8 −3
  48. arch/loongarch/kernel/traps.c +27 −0
  49. arch/loongarch/kernel/unaligned.c +499 −0
  50. arch/loongarch/kernel/unwind_guess.c +3 −1
  51. arch/loongarch/kernel/unwind_prologue.c +42 −8
  52. arch/loongarch/kernel/vmlinux.lds.S +12 −1
  53. arch/loongarch/lib/Makefile +2 −1
  54. arch/loongarch/lib/clear_user.S +70 −15
  55. arch/loongarch/lib/copy_user.S +92 −16
  56. arch/loongarch/lib/memcpy.S +95 −0
  57. arch/loongarch/lib/memmove.S +121 −0
  58. arch/loongarch/lib/memset.S +91 −0
  59. arch/loongarch/lib/unaligned.S +84 −0
  60. arch/loongarch/mm/extable.c +50 −9
  61. arch/loongarch/net/bpf_jit.c +81 −5
  62. arch/loongarch/net/bpf_jit.h +2 −0
  63. arch/loongarch/pci/acpi.c +5 −2
  64. arch/loongarch/power/Makefile +4 −0
  65. arch/loongarch/power/hibernate.c +62 −0
  66. arch/loongarch/power/hibernate_asm.S +66 −0
  67. arch/loongarch/power/platform.c +57 −0
  68. arch/loongarch/power/suspend.c +73 −0
  69. arch/loongarch/power/suspend_asm.S +89 −0
  70. scripts/mod/modpost.c +13 −0
  71. scripts/recordmcount.c +39 −0
  72. scripts/sorttable.c +1 −1
Documentation/admin-guide/sysctl/kernel.rst+4−4
@@ -436,8 +436,8 @@ ignore-unaligned-usertrap · 435 unmodified lines
436436
437437 On architectures where unaligned accesses cause traps, and where this
438438 feature is supported (``CONFIG_SYSCTL_ARCH_UNALIGN_NO_WARN``;
439−currently, ``arc`` and ``ia64``), controls whether all unaligned traps
440−are logged.
439+currently, ``arc``, ``ia64`` and ``loongarch``), controls whether all
440+unaligned traps are logged.
441441
442442 = =============================================================
443443 0 Log all unaligned accesses.
@@ -1492,8 +1492,8 @@ unaligned-trap · 1048 unmodified lines
14921492
14931493 On architectures where unaligned accesses cause traps, and where this
14941494 feature is supported (``CONFIG_SYSCTL_ARCH_UNALIGN_ALLOW``; currently,
1495−``arc`` and ``parisc``), controls whether unaligned traps are caught
1496−and emulated (instead of failing).
1495+``arc``, ``parisc`` and ``loongarch``), controls whether unaligned traps
1496+are caught and emulated (instead of failing).
14971497
14981498 = ========================================================
14991499 0 Do not emulate unaligned accesses.
arch/loongarch/Kconfig+20−0
@@ -58,6 +58,7 @@ config LOONGARCH · 57 unmodified lines
5858 select ARCH_WANTS_NO_INSTR
5959 select BUILDTIME_TABLE_SORT
6060 select COMMON_CLK
61+ select CPU_PM
6162 select EFI
6263 select GENERIC_CLOCKEVENTS
6364 select GENERIC_CMOS_UPDATE
@@ -86,11 +87,18 @@ config LOONGARCH · 22 unmodified lines
8687 select HAVE_ARCH_TRANSPARENT_HUGEPAGE
8788 select HAVE_ASM_MODVERSIONS
8889 select HAVE_CONTEXT_TRACKING_USER
90+ select HAVE_C_RECORDMCOUNT
8991 select HAVE_DEBUG_STACKOVERFLOW
9092 select HAVE_DMA_CONTIGUOUS
93+ select HAVE_DYNAMIC_FTRACE
94+ select HAVE_DYNAMIC_FTRACE_WITH_ARGS
95+ select HAVE_DYNAMIC_FTRACE_WITH_REGS
9196 select HAVE_EBPF_JIT
9297 select HAVE_EXIT_THREAD
9398 select HAVE_FAST_GUP
99+ select HAVE_FTRACE_MCOUNT_RECORD
100+ select HAVE_FUNCTION_GRAPH_TRACER
101+ select HAVE_FUNCTION_TRACER
94102 select HAVE_GENERIC_VDSO
95103 select HAVE_IOREMAP_PROT
96104 select HAVE_IRQ_EXIT_ON_IRQ_STACK
@@ -104,6 +112,7 @@ config LOONGARCH · 7 unmodified lines
104112 select HAVE_REGS_AND_STACK_ACCESS_API
105113 select HAVE_RSEQ
106114 select HAVE_SETUP_PER_CPU_AREA if NUMA
115+ select HAVE_STACKPROTECTOR
107116 select HAVE_SYSCALL_TRACEPOINTS
108117 select HAVE_TIF_NOHZ
109118 select HAVE_VIRT_CPU_ACCOUNTING_GEN if !SMP
@@ -113,6 +122,8 @@ config LOONGARCH · 3 unmodified lines
113122 select MODULES_USE_ELF_RELA if MODULES
114123 select NEED_PER_CPU_EMBED_FIRST_CHUNK
115124 select NEED_PER_CPU_PAGE_FIRST_CHUNK
125+ select OF
126+ select OF_EARLY_FLATTREE
116127 select PCI
117128 select PCI_DOMAINS_GENERIC
118129 select PCI_ECAM if ACPI
@@ -123,6 +134,8 @@ config LOONGARCH · 4 unmodified lines
123134 select RTC_LIB
124135 select SMP
125136 select SPARSE_IRQ
137+ select SYSCTL_ARCH_UNALIGN_ALLOW
138+ select SYSCTL_ARCH_UNALIGN_NO_WARN
126139 select SYSCTL_EXCEPTION_TRACE
127140 select SWIOTLB
128141 select TRACE_IRQFLAGS_SUPPORT
@@ -516,6 +529,13 @@ config ARCH_MMAP_RND_BITS_MAX · 387 unmodified lines
516529
517530 menu "Power management options"
518531
532+config ARCH_SUSPEND_POSSIBLE
533+ def_bool y
534+
535+config ARCH_HIBERNATION_POSSIBLE
536+ def_bool y
537+
538+source "kernel/power/Kconfig"
519539 source "drivers/acpi/Kconfig"
520540
521541 endmenu
arch/loongarch/Makefile+8−0
@@ -25,6 +25,11 @@ endif · 24 unmodified lines
2525 32bit-emul = elf32loongarch
2626 64bit-emul = elf64loongarch
2727
28+ifdef CONFIG_DYNAMIC_FTRACE
29+KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY
30+CC_FLAGS_FTRACE := -fpatchable-function-entry=2
31+endif
32+
2833 ifdef CONFIG_64BIT
2934 tool-archpref = $(64bit-tool-archpref)
3035 UTS_MACHINE := loongarch64
@@ -104,6 +109,9 @@ endif · 73 unmodified lines
104109 libs-y += arch/loongarch/lib/
105110 libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a
106111
112+# suspend and hibernation support
113+drivers-$(CONFIG_PM) += arch/loongarch/power/
114+
107115 ifeq ($(KBUILD_EXTMOD),)
108116 prepare: vdso_prepare
109117 vdso_prepare: prepare0
arch/loongarch/configs/loongson3_defconfig+32−24
@@ -34,12 +34,13 @@ CONFIG_SYSFS_DEPRECATED=y · 33 unmodified lines
3434 CONFIG_RELAY=y
3535 CONFIG_BLK_DEV_INITRD=y
3636 CONFIG_EXPERT=y
37−CONFIG_USERFAULTFD=y
37+CONFIG_KALLSYMS_ALL=y
3838 CONFIG_PERF_EVENTS=y
39−# CONFIG_COMPAT_BRK is not set
4039 CONFIG_LOONGARCH=y
4140 CONFIG_64BIT=y
4241 CONFIG_MACH_LOONGSON64=y
42+CONFIG_PAGE_SIZE_16KB=y
43+CONFIG_HZ_250=y
4344 CONFIG_DMI=y
4445 CONFIG_EFI=y
4546 CONFIG_SMP=y
@@ -47,14 +48,14 @@ CONFIG_HOTPLUG_CPU=y · 1 unmodified line
4748 CONFIG_NR_CPUS=64
4849 CONFIG_NUMA=y
4950 CONFIG_KEXEC=y
50−CONFIG_PAGE_SIZE_16KB=y
51−CONFIG_HZ_250=y
51+CONFIG_SUSPEND=y
52+CONFIG_HIBERNATION=y
5253 CONFIG_ACPI=y
5354 CONFIG_ACPI_SPCR_TABLE=y
54−CONFIG_ACPI_HOTPLUG_CPU=y
5555 CONFIG_ACPI_TAD=y
5656 CONFIG_ACPI_DOCK=y
5757 CONFIG_ACPI_IPMI=m
58+CONFIG_ACPI_HOTPLUG_CPU=y
5859 CONFIG_ACPI_PCI_SLOT=y
5960 CONFIG_ACPI_HOTPLUG_MEMORY=y
6061 CONFIG_EFI_ZBOOT=y
@@ -73,17 +74,19 @@ CONFIG_UNIXWARE_DISKLABEL=y · 12 unmodified lines
7374 CONFIG_IOSCHED_BFQ=y
7475 CONFIG_BFQ_GROUP_IOSCHED=y
7576 CONFIG_BINFMT_MISC=m
76−CONFIG_MEMORY_HOTPLUG=y
77−CONFIG_MEMORY_HOTPLUG_DEFAULT_ONLINE=y
78−CONFIG_MEMORY_HOTREMOVE=y
79−CONFIG_KSM=y
80−CONFIG_TRANSPARENT_HUGEPAGE=y
77+CONFIG_ZPOOL=y
8178 CONFIG_ZSWAP=y
8279 CONFIG_ZSWAP_COMPRESSOR_DEFAULT_ZSTD=y
83−CONFIG_ZPOOL=y
8480 CONFIG_ZBUD=y
8581 CONFIG_Z3FOLD=y
8682 CONFIG_ZSMALLOC=m
83+# CONFIG_COMPAT_BRK is not set
84+CONFIG_MEMORY_HOTPLUG=y
85+CONFIG_MEMORY_HOTPLUG_DEFAULT_ONLINE=y
86+CONFIG_MEMORY_HOTREMOVE=y
87+CONFIG_KSM=y
88+CONFIG_TRANSPARENT_HUGEPAGE=y
89+CONFIG_USERFAULTFD=y
8790 CONFIG_NET=y
8891 CONFIG_PACKET=y
8992 CONFIG_UNIX=y
@@ -118,7 +121,6 @@ CONFIG_NETFILTER=y · 28 unmodified lines
118121 CONFIG_BRIDGE_NETFILTER=m
119122 CONFIG_NETFILTER_NETLINK_LOG=m
120123 CONFIG_NF_CONNTRACK=m
121−CONFIG_NF_LOG_NETDEV=m
122124 CONFIG_NF_CONNTRACK_AMANDA=m
123125 CONFIG_NF_CONNTRACK_FTP=m
124126 CONFIG_NF_CONNTRACK_NETBIOS_NS=m
@@ -416,6 +418,7 @@ CONFIG_SCSI_VIRTIO=m · 291 unmodified lines
416418 CONFIG_ATA=y
417419 CONFIG_SATA_AHCI=y
418420 CONFIG_SATA_AHCI_PLATFORM=y
421+CONFIG_AHCI_DWC=y
419422 CONFIG_PATA_ATIIXP=y
420423 CONFIG_PATA_PCMCIA=m
421424 CONFIG_MD=y
@@ -469,13 +472,11 @@ CONFIG_VIRTIO_NET=m · 47 unmodified lines
469472 # CONFIG_NET_VENDOR_ARC is not set
470473 # CONFIG_NET_VENDOR_ATHEROS is not set
471474 CONFIG_BNX2=y
472−# CONFIG_NET_VENDOR_BROCADE is not set
473475 # CONFIG_NET_VENDOR_CAVIUM is not set
474476 CONFIG_CHELSIO_T1=m
475477 CONFIG_CHELSIO_T1_1G=y
476478 CONFIG_CHELSIO_T3=m
477479 CONFIG_CHELSIO_T4=m
478−# CONFIG_NET_VENDOR_CIRRUS is not set
479480 # CONFIG_NET_VENDOR_CISCO is not set
480481 # CONFIG_NET_VENDOR_DEC is not set
481482 # CONFIG_NET_VENDOR_DLINK is not set
@@ -496,6 +497,7 @@ CONFIG_IXGBE=y · 14 unmodified lines
496497 # CONFIG_NET_VENDOR_NVIDIA is not set
497498 # CONFIG_NET_VENDOR_OKI is not set
498499 # CONFIG_NET_VENDOR_QLOGIC is not set
500+# CONFIG_NET_VENDOR_BROCADE is not set
499501 # CONFIG_NET_VENDOR_QUALCOMM is not set
500502 # CONFIG_NET_VENDOR_RDC is not set
501503 CONFIG_8139CP=m
@@ -505,9 +507,9 @@ CONFIG_R8169=y · 3 unmodified lines
505507 # CONFIG_NET_VENDOR_ROCKER is not set
506508 # CONFIG_NET_VENDOR_SAMSUNG is not set
507509 # CONFIG_NET_VENDOR_SEEQ is not set
508−# CONFIG_NET_VENDOR_SOLARFLARE is not set
509510 # CONFIG_NET_VENDOR_SILAN is not set
510511 # CONFIG_NET_VENDOR_SIS is not set
512+# CONFIG_NET_VENDOR_SOLARFLARE is not set
511513 # CONFIG_NET_VENDOR_SMSC is not set
512514 CONFIG_STMMAC_ETH=y
513515 # CONFIG_NET_VENDOR_SUN is not set
@@ -588,6 +590,7 @@ CONFIG_SERIAL_8250_EXTENDED=y · 74 unmodified lines
588590 CONFIG_SERIAL_8250_MANY_PORTS=y
589591 CONFIG_SERIAL_8250_SHARE_IRQ=y
590592 CONFIG_SERIAL_8250_RSA=y
593+CONFIG_SERIAL_OF_PLATFORM=y
591594 CONFIG_SERIAL_NONSTANDARD=y
592595 CONFIG_PRINTER=m
593596 CONFIG_VIRTIO_CONSOLE=y
@@ -602,6 +605,11 @@ CONFIG_I2C_GPIO=y · 8 unmodified lines
602605 CONFIG_SPI=y
603606 CONFIG_GPIO_SYSFS=y
604607 CONFIG_GPIO_LOONGSON=y
608+CONFIG_POWER_RESET=y
609+CONFIG_POWER_RESET_RESTART=y
610+CONFIG_POWER_RESET_SYSCON=y
611+CONFIG_POWER_RESET_SYSCON_POWEROFF=y
612+CONFIG_SYSCON_REBOOT_MODE=y
605613 CONFIG_SENSORS_LM75=m
606614 CONFIG_SENSORS_LM93=m
607615 CONFIG_SENSORS_W83795=m
@@ -609,16 +617,16 @@ CONFIG_SENSORS_W83627HF=m · 1 unmodified line
609617 CONFIG_RC_CORE=m
610618 CONFIG_LIRC=y
611619 CONFIG_RC_DECODERS=y
620+CONFIG_IR_IMON_DECODER=m
621+CONFIG_IR_JVC_DECODER=m
622+CONFIG_IR_MCE_KBD_DECODER=m
612623 CONFIG_IR_NEC_DECODER=m
613624 CONFIG_IR_RC5_DECODER=m
614625 CONFIG_IR_RC6_DECODER=m
615−CONFIG_IR_JVC_DECODER=m
616−CONFIG_IR_SONY_DECODER=m
617626 CONFIG_IR_SANYO_DECODER=m
618627 CONFIG_IR_SHARP_DECODER=m
619−CONFIG_IR_MCE_KBD_DECODER=m
628+CONFIG_IR_SONY_DECODER=m
620629 CONFIG_IR_XMP_DECODER=m
621−CONFIG_IR_IMON_DECODER=m
622630 CONFIG_MEDIA_SUPPORT=m
623631 CONFIG_MEDIA_USB_SUPPORT=y
624632 CONFIG_USB_VIDEO_CLASS=m
@@ -638,6 +646,7 @@ CONFIG_DRM_VIRTIO_GPU=m · 13 unmodified lines
638646 CONFIG_FB=y
639647 CONFIG_FB_EFI=y
640648 CONFIG_FB_RADEON=y
649+CONFIG_LCD_CLASS_DEVICE=y
641650 CONFIG_LCD_PLATFORM=m
642651 # CONFIG_VGA_CONSOLE is not set
643652 CONFIG_FRAMEBUFFER_CONSOLE=y
@@ -647,7 +656,6 @@ CONFIG_SOUND=y · 3 unmodified lines
647656 CONFIG_SND=y
648657 CONFIG_SND_SEQUENCER=m
649658 CONFIG_SND_SEQ_DUMMY=m
650−# CONFIG_SND_ISA is not set
651659 CONFIG_SND_BT87X=m
652660 CONFIG_SND_BT87X_OVERCLOCK=y
653661 CONFIG_SND_HDA_INTEL=y
@@ -818,10 +826,6 @@ CONFIG_CRYPTO_USER=m · 164 unmodified lines
818826 # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set
819827 CONFIG_CRYPTO_PCRYPT=m
820828 CONFIG_CRYPTO_CRYPTD=m
821−CONFIG_CRYPTO_CHACHA20POLY1305=m
822−CONFIG_CRYPTO_HMAC=y
823−CONFIG_CRYPTO_VMAC=m
824−CONFIG_CRYPTO_WP512=m
825829 CONFIG_CRYPTO_ANUBIS=m
826830 CONFIG_CRYPTO_BLOWFISH=m
827831 CONFIG_CRYPTO_CAST5=m
@@ -831,6 +835,9 @@ CONFIG_CRYPTO_SEED=m · 3 unmodified lines
831835 CONFIG_CRYPTO_SERPENT=m
832836 CONFIG_CRYPTO_TEA=m
833837 CONFIG_CRYPTO_TWOFISH=m
838+CONFIG_CRYPTO_CHACHA20POLY1305=m
839+CONFIG_CRYPTO_VMAC=m
840+CONFIG_CRYPTO_WP512=m
834841 CONFIG_CRYPTO_DEFLATE=m
835842 CONFIG_CRYPTO_LZO=m
836843 CONFIG_CRYPTO_842=m
@@ -844,6 +851,7 @@ CONFIG_CRYPTO_DEV_VIRTIO=m · 7 unmodified lines
844851 CONFIG_PRINTK_TIME=y
845852 CONFIG_STRIP_ASM_SYMS=y
846853 CONFIG_MAGIC_SYSRQ=y
854+CONFIG_DEBUG_FS=y
847855 # CONFIG_SCHED_DEBUG is not set
848856 CONFIG_SCHEDSTATS=y
849857 # CONFIG_DEBUG_PREEMPT is not set
arch/loongarch/include/asm/acpi.h+10−0
@@ -35,4 +35,14 @@ extern struct list_head acpi_wakeup_device_list; · 34 unmodified lines
3535
3636 #define ACPI_TABLE_UPGRADE_MAX_PHYS ARCH_LOW_ADDRESS_LIMIT
3737
38+extern int loongarch_acpi_suspend(void);
39+extern int (*acpi_suspend_lowlevel)(void);
40+extern void loongarch_suspend_enter(void);
41+
42+static inline unsigned long acpi_get_wakeup_address(void)
43+{
44+ extern void loongarch_wakeup_start(void);
45+ return (unsigned long)loongarch_wakeup_start;
46+}
47+
3848 #endif /* _ASM_LOONGARCH_ACPI_H */
arch/loongarch/include/asm/alternative-asm.h+82−0
@@ -0,0 +1,82 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+#ifndef _ASM_ALTERNATIVE_ASM_H
3+#define _ASM_ALTERNATIVE_ASM_H
4+
5+#ifdef __ASSEMBLY__
6+
7+#include <asm/asm.h>
8+
9+/*
10+ * Issue one struct alt_instr descriptor entry (need to put it into
11+ * the section .altinstructions, see below). This entry contains
12+ * enough information for the alternatives patching code to patch an
13+ * instruction. See apply_alternatives().
14+ */
15+.macro altinstruction_entry orig alt feature orig_len alt_len
16+ .long \orig - .
17+ .long \alt - .
18+ .short \feature
19+ .byte \orig_len
20+ .byte \alt_len
21+.endm
22+
23+/*
24+ * Define an alternative between two instructions. If @feature is
25+ * present, early code in apply_alternatives() replaces @oldinstr with
26+ * @newinstr. ".fill" directive takes care of proper instruction padding
27+ * in case @newinstr is longer than @oldinstr.
28+ */
29+.macro ALTERNATIVE oldinstr, newinstr, feature
30+140 :
31+ \oldinstr
32+141 :
33+ .fill - (((144f-143f)-(141b-140b)) > 0) * ((144f-143f)-(141b-140b)) / 4, 4, 0x03400000
34+142 :
35+
36+ .pushsection .altinstructions, "a"
37+ altinstruction_entry 140b, 143f, \feature, 142b-140b, 144f-143f
38+ .popsection
39+
40+ .subsection 1
41+143 :
42+ \newinstr
43+144 :
44+ .previous
45+.endm
46+
47+#define old_len (141b-140b)
48+#define new_len1 (144f-143f)
49+#define new_len2 (145f-144f)
50+
51+#define alt_max_short(a, b) ((a) ^ (((a) ^ (b)) & -(-((a) < (b)))))
52+
53+/*
54+ * Same as ALTERNATIVE macro above but for two alternatives. If CPU
55+ * has @feature1, it replaces @oldinstr with @newinstr1. If CPU has
56+ * @feature2, it replaces @oldinstr with @feature2.
57+ */
58+.macro ALTERNATIVE_2 oldinstr, newinstr1, feature1, newinstr2, feature2
59+140 :
60+ \oldinstr
61+141 :
62+ .fill - ((alt_max_short(new_len1, new_len2) - (old_len)) > 0) * \
63+ (alt_max_short(new_len1, new_len2) - (old_len)) / 4, 4, 0x03400000
64+142 :
65+
66+ .pushsection .altinstructions, "a"
67+ altinstruction_entry 140b, 143f, \feature1, 142b-140b, 144f-143f, 142b-141b
68+ altinstruction_entry 140b, 144f, \feature2, 142b-140b, 145f-144f, 142b-141b
69+ .popsection
70+
71+ .subsection 1
72+143 :
73+ \newinstr1
74+144 :
75+ \newinstr2
76+145 :
77+ .previous
78+.endm
79+
80+#endif /* __ASSEMBLY__ */
81+
82+#endif /* _ASM_ALTERNATIVE_ASM_H */
arch/loongarch/include/asm/alternative.h+111−0
@@ -0,0 +1,111 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+#ifndef _ASM_ALTERNATIVE_H
3+#define _ASM_ALTERNATIVE_H
4+
5+#ifndef __ASSEMBLY__
6+
7+#include <linux/types.h>
8+#include <linux/stddef.h>
9+#include <linux/stringify.h>
10+#include <asm/asm.h>
11+
12+struct alt_instr {
13+ s32 instr_offset; /* offset to original instruction */
14+ s32 replace_offset; /* offset to replacement instruction */
15+ u16 feature; /* feature bit set for replacement */
16+ u8 instrlen; /* length of original instruction */
17+ u8 replacementlen; /* length of new instruction */
18+} __packed;
19+
20+/*
21+ * Debug flag that can be tested to see whether alternative
22+ * instructions were patched in already:
23+ */
24+extern int alternatives_patched;
25+extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
26+
27+extern void alternative_instructions(void);
28+extern void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
29+
30+#define b_replacement(num) "664"#num
31+#define e_replacement(num) "665"#num
32+
33+#define alt_end_marker "663"
34+#define alt_slen "662b-661b"
35+#define alt_total_slen alt_end_marker"b-661b"
36+#define alt_rlen(num) e_replacement(num)"f-"b_replacement(num)"f"
37+
38+#define __OLDINSTR(oldinstr, num) \
39+ "661:\n\t" oldinstr "\n662:\n" \
40+ ".fill -(((" alt_rlen(num) ")-(" alt_slen ")) > 0) * " \
41+ "((" alt_rlen(num) ")-(" alt_slen ")) / 4, 4, 0x03400000\n"
42+
43+#define OLDINSTR(oldinstr, num) \
44+ __OLDINSTR(oldinstr, num) \
45+ alt_end_marker ":\n"
46+
47+#define alt_max_short(a, b) "((" a ") ^ (((" a ") ^ (" b ")) & -(-((" a ") < (" b ")))))"
48+
49+/*
50+ * Pad the second replacement alternative with additional NOPs if it is
51+ * additionally longer than the first replacement alternative.
52+ */
53+#define OLDINSTR_2(oldinstr, num1, num2) \
54+ "661:\n\t" oldinstr "\n662:\n" \
55+ ".fill -((" alt_max_short(alt_rlen(num1), alt_rlen(num2)) " - (" alt_slen ")) > 0) * " \
56+ "(" alt_max_short(alt_rlen(num1), alt_rlen(num2)) " - (" alt_slen ")) / 4, " \
57+ "4, 0x03400000\n" \
58+ alt_end_marker ":\n"
59+
60+#define ALTINSTR_ENTRY(feature, num) \
61+ " .long 661b - .\n" /* label */ \
62+ " .long " b_replacement(num)"f - .\n" /* new instruction */ \
63+ " .short " __stringify(feature) "\n" /* feature bit */ \
64+ " .byte " alt_total_slen "\n" /* source len */ \
65+ " .byte " alt_rlen(num) "\n" /* replacement len */
66+
67+#define ALTINSTR_REPLACEMENT(newinstr, feature, num) /* replacement */ \
68+ b_replacement(num)":\n\t" newinstr "\n" e_replacement(num) ":\n\t"
69+
70+/* alternative assembly primitive: */
71+#define ALTERNATIVE(oldinstr, newinstr, feature) \
72+ OLDINSTR(oldinstr, 1) \
73+ ".pushsection .altinstructions,\"a\"\n" \
74+ ALTINSTR_ENTRY(feature, 1) \
75+ ".popsection\n" \
76+ ".subsection 1\n" \
77+ ALTINSTR_REPLACEMENT(newinstr, feature, 1) \
78+ ".previous\n"
79+
80+#define ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2)\
81+ OLDINSTR_2(oldinstr, 1, 2) \
82+ ".pushsection .altinstructions,\"a\"\n" \
83+ ALTINSTR_ENTRY(feature1, 1) \
84+ ALTINSTR_ENTRY(feature2, 2) \
85+ ".popsection\n" \
86+ ".subsection 1\n" \
87+ ALTINSTR_REPLACEMENT(newinstr1, feature1, 1) \
88+ ALTINSTR_REPLACEMENT(newinstr2, feature2, 2) \
89+ ".previous\n"
90+
91+/*
92+ * Alternative instructions for different CPU types or capabilities.
93+ *
94+ * This allows to use optimized instructions even on generic binary
95+ * kernels.
96+ *
97+ * length of oldinstr must be longer or equal the length of newinstr
98+ * It can be padded with nops as needed.
99+ *
100+ * For non barrier like inlines please define new variants
101+ * without volatile and memory clobber.
102+ */
103+#define alternative(oldinstr, newinstr, feature) \
104+ (asm volatile (ALTERNATIVE(oldinstr, newinstr, feature) : : : "memory"))
105+
106+#define alternative_2(oldinstr, newinstr1, feature1, newinstr2, feature2) \
107+ (asm volatile(ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2) ::: "memory"))
108+
109+#endif /* __ASSEMBLY__ */
110+
111+#endif /* _ASM_ALTERNATIVE_H */
arch/loongarch/include/asm/asm-extable.h+65−0
@@ -0,0 +1,65 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+#ifndef __ASM_ASM_EXTABLE_H
3+#define __ASM_ASM_EXTABLE_H
4+
5+#define EX_TYPE_NONE 0
6+#define EX_TYPE_FIXUP 1
7+#define EX_TYPE_UACCESS_ERR_ZERO 2
8+#define EX_TYPE_BPF 3
9+
10+#ifdef __ASSEMBLY__
11+
12+#define __ASM_EXTABLE_RAW(insn, fixup, type, data) \
13+ .pushsection __ex_table, "a"; \
14+ .balign 4; \
15+ .long ((insn) - .); \
16+ .long ((fixup) - .); \
17+ .short (type); \
18+ .short (data); \
19+ .popsection;
20+
21+ .macro _asm_extable, insn, fixup
22+ __ASM_EXTABLE_RAW(\insn, \fixup, EX_TYPE_FIXUP, 0)
23+ .endm
24+
25+#else /* __ASSEMBLY__ */
26+
27+#include <linux/bits.h>
28+#include <linux/stringify.h>
29+#include <asm/gpr-num.h>
30+
31+#define __ASM_EXTABLE_RAW(insn, fixup, type, data) \
32+ ".pushsection __ex_table, \"a\"\n" \
33+ ".balign 4\n" \
34+ ".long ((" insn ") - .)\n" \
35+ ".long ((" fixup ") - .)\n" \
36+ ".short (" type ")\n" \
37+ ".short (" data ")\n" \
38+ ".popsection\n"
39+
40+#define _ASM_EXTABLE(insn, fixup) \
41+ __ASM_EXTABLE_RAW(#insn, #fixup, __stringify(EX_TYPE_FIXUP), "0")
42+
43+#define EX_DATA_REG_ERR_SHIFT 0
44+#define EX_DATA_REG_ERR GENMASK(4, 0)
45+#define EX_DATA_REG_ZERO_SHIFT 5
46+#define EX_DATA_REG_ZERO GENMASK(9, 5)
47+
48+#define EX_DATA_REG(reg, gpr) \
49+ "((.L__gpr_num_" #gpr ") << " __stringify(EX_DATA_REG_##reg##_SHIFT) ")"
50+
51+#define _ASM_EXTABLE_UACCESS_ERR_ZERO(insn, fixup, err, zero) \
52+ __DEFINE_ASM_GPR_NUMS \
53+ __ASM_EXTABLE_RAW(#insn, #fixup, \
54+ __stringify(EX_TYPE_UACCESS_ERR_ZERO), \
55+ "(" \
56+ EX_DATA_REG(ERR, err) " | " \
57+ EX_DATA_REG(ZERO, zero) \
58+ ")")
59+
60+#define _ASM_EXTABLE_UACCESS_ERR(insn, fixup, err) \
61+ _ASM_EXTABLE_UACCESS_ERR_ZERO(insn, fixup, err, zero)
62+
63+#endif /* __ASSEMBLY__ */
64+
65+#endif /* __ASM_ASM_EXTABLE_H */
arch/loongarch/include/asm/bootinfo.h+1−0
@@ -32,6 +32,7 @@ struct loongson_system_configuration { · 31 unmodified lines
3232 int cores_per_node;
3333 int cores_per_package;
3434 unsigned long cores_io_master;
35+ unsigned long suspend_addr;
3536 const char *cpuname;
3637 };
3738
arch/loongarch/include/asm/bugs.h+15−0
@@ -0,0 +1,15 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+/*
3+ * This is included by init/main.c to check for architecture-dependent bugs.
4+ *
5+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
6+ */
7+#ifndef _ASM_BUGS_H
8+#define _ASM_BUGS_H
9+
10+#include <asm/cpu.h>
11+#include <asm/cpu-info.h>
12+
13+extern void check_bugs(void);
14+
15+#endif /* _ASM_BUGS_H */
arch/loongarch/include/asm/efi.h+1−0
@@ -9,6 +9,7 @@ · 8 unmodified lines
99
1010 void __init efi_init(void);
1111 void __init efi_runtime_init(void);
12+void __init *efi_fdt_pointer(void);
1213 void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
1314
1415 #define ARCH_EFI_IRQ_FLAGS_MASK 0x00000004 /* Bit 2: CSR.CRMD.IE */
arch/loongarch/include/asm/extable.h+47−0
@@ -0,0 +1,47 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+#ifndef _ASM_LOONGARCH_EXTABLE_H
3+#define _ASM_LOONGARCH_EXTABLE_H
4+
5+/*
6+ * The exception table consists of pairs of relative offsets: the first
7+ * is the relative offset to an instruction that is allowed to fault,
8+ * and the second is the relative offset at which the program should
9+ * continue. No registers are modified, so it is entirely up to the
10+ * continuation code to figure out what to do.
11+ *
12+ * All the routines below use bits of fixup code that are out of line
13+ * with the main instruction path. This means when everything is well,
14+ * we don't even have to jump over them. Further, they do not intrude
15+ * on our cache or tlb entries.
16+ */
17+
18+struct exception_table_entry {
19+ int insn, fixup;
20+ short type, data;
21+};
22+
23+#define ARCH_HAS_RELATIVE_EXTABLE
24+
25+#define swap_ex_entry_fixup(a, b, tmp, delta) \
26+do { \
27+ (a)->fixup = (b)->fixup + (delta); \
28+ (b)->fixup = (tmp).fixup - (delta); \
29+ (a)->type = (b)->type; \
30+ (b)->type = (tmp).type; \
31+ (a)->data = (b)->data; \
32+ (b)->data = (tmp).data; \
33+} while (0)
34+
35+#ifdef CONFIG_BPF_JIT
36+bool ex_handler_bpf(const struct exception_table_entry *ex, struct pt_regs *regs);
37+#else
38+static inline
39+bool ex_handler_bpf(const struct exception_table_entry *ex, struct pt_regs *regs)
40+{
41+ return false;
42+}
43+#endif /* !CONFIG_BPF_JIT */
44+
45+bool fixup_exception(struct pt_regs *regs);
46+
47+#endif
arch/loongarch/include/asm/ftrace.h+68−0
@@ -0,0 +1,68 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+/*
3+ * Copyright (C) 2022 Loongson Technology Corporation Limited
4+ */
5+
6+#ifndef _ASM_LOONGARCH_FTRACE_H
7+#define _ASM_LOONGARCH_FTRACE_H
8+
9+#define FTRACE_PLT_IDX 0
10+#define FTRACE_REGS_PLT_IDX 1
11+#define NR_FTRACE_PLTS 2
12+
13+#define GRAPH_FAKE_OFFSET (sizeof(struct pt_regs) - offsetof(struct pt_regs, regs[1]))
14+
15+#ifdef CONFIG_FUNCTION_TRACER
16+
17+#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
18+
19+#ifndef __ASSEMBLY__
20+
21+#ifndef CONFIG_DYNAMIC_FTRACE
22+
23+#define mcount _mcount
24+extern void _mcount(void);
25+extern void prepare_ftrace_return(unsigned long self_addr, unsigned long callsite_sp, unsigned long old);
26+
27+#else
28+
29+struct dyn_ftrace;
30+struct dyn_arch_ftrace { };
31+
32+#define ARCH_SUPPORTS_FTRACE_OPS 1
33+#define HAVE_FUNCTION_GRAPH_RET_ADDR_PTR
34+
35+#define ftrace_init_nop ftrace_init_nop
36+int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec);
37+
38+static inline unsigned long ftrace_call_adjust(unsigned long addr)
39+{
40+ return addr;
41+}
42+
43+void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent);
44+
45+#endif /* CONFIG_DYNAMIC_FTRACE */
46+
47+#ifdef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS
48+struct ftrace_ops;
49+
50+struct ftrace_regs {
51+ struct pt_regs regs;
52+};
53+
54+static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *fregs)
55+{
56+ return &fregs->regs;
57+}
58+
59+#define ftrace_graph_func ftrace_graph_func
60+void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
61+ struct ftrace_ops *op, struct ftrace_regs *fregs);
62+#endif
63+
64+#endif /* __ASSEMBLY__ */
65+
66+#endif /* CONFIG_FUNCTION_TRACER */
67+
68+#endif /* _ASM_LOONGARCH_FTRACE_H */
arch/loongarch/include/asm/futex.h+7−20
@@ -7,6 +7,7 @@ · 6 unmodified lines
77
88 #include <linux/futex.h>
99 #include <linux/uaccess.h>
10+#include <asm/asm-extable.h>
1011 #include <asm/barrier.h>
1112 #include <asm/errno.h>
1213
@@ -18,18 +19,11 @@ · 5 unmodified lines
1819 "2: sc.w $t0, %2 \n" \
1920 " beqz $t0, 1b \n" \
2021 "3: \n" \
21− " .section .fixup,\"ax\" \n" \
22− "4: li.w %0, %6 \n" \
23− " b 3b \n" \
24− " .previous \n" \
25− " .section __ex_table,\"a\" \n" \
26− " "__UA_ADDR "\t1b, 4b \n" \
27− " "__UA_ADDR "\t2b, 4b \n" \
28− " .previous \n" \
22+ _ASM_EXTABLE_UACCESS_ERR(1b, 3b, %0) \
23+ _ASM_EXTABLE_UACCESS_ERR(2b, 3b, %0) \
2924 : "=r" (ret), "=&r" (oldval), \
3025 "=ZC" (*uaddr) \
31− : "0" (0), "ZC" (*uaddr), "Jr" (oparg), \
32− "i" (-EFAULT) \
26+ : "0" (0), "ZC" (*uaddr), "Jr" (oparg) \
3327 : "memory", "t0"); \
3428 }
3529
@@ -86,17 +80,10 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32 newv · 50 unmodified lines
8680 " beqz $t0, 1b \n"
8781 "3: \n"
8882 __WEAK_LLSC_MB
89− " .section .fixup,\"ax\" \n"
90− "4: li.d %0, %6 \n"
91− " b 3b \n"
92− " .previous \n"
93− " .section __ex_table,\"a\" \n"
94− " "__UA_ADDR "\t1b, 4b \n"
95− " "__UA_ADDR "\t2b, 4b \n"
96− " .previous \n"
83+ _ASM_EXTABLE_UACCESS_ERR(1b, 3b, %0)
84+ _ASM_EXTABLE_UACCESS_ERR(2b, 3b, %0)
9785 : "+r" (ret), "=&r" (val), "=ZC" (*uaddr)
98− : "ZC" (*uaddr), "Jr" (oldval), "Jr" (newval),
99− "i" (-EFAULT)
86+ : "ZC" (*uaddr), "Jr" (oldval), "Jr" (newval)
10087 : "memory", "t0");
10188
10289 *uval = val;
arch/loongarch/include/asm/gpr-num.h+22−0
@@ -0,0 +1,22 @@
1+/* SPDX-License-Identifier: GPL-2.0-only */
2+#ifndef __ASM_GPR_NUM_H
3+#define __ASM_GPR_NUM_H
4+
5+#ifdef __ASSEMBLY__
6+
7+ .equ .L__gpr_num_zero, 0
8+ .irp num,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
9+ .equ .L__gpr_num_$r\num, \num
10+ .endr
11+
12+#else /* __ASSEMBLY__ */
13+
14+#define __DEFINE_ASM_GPR_NUMS \
15+" .equ .L__gpr_num_zero, 0\n" \
16+" .irp num,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31\n" \
17+" .equ .L__gpr_num_$r\\num, \\num\n" \
18+" .endr\n" \
19+
20+#endif /* __ASSEMBLY__ */
21+
22+#endif /* __ASM_GPR_NUM_H */
arch/loongarch/include/asm/inst.h+46−0
@@ -8,14 +8,17 @@ · 7 unmodified lines
88 #include <linux/types.h>
99 #include <asm/asm.h>
1010
11+#define INSN_NOP 0x03400000
1112 #define INSN_BREAK 0x002a0000
1213
1314 #define ADDR_IMMMASK_LU52ID 0xFFF0000000000000
1415 #define ADDR_IMMMASK_LU32ID 0x000FFFFF00000000
16+#define ADDR_IMMMASK_LU12IW 0x00000000FFFFF000
1517 #define ADDR_IMMMASK_ADDU16ID 0x00000000FFFF0000
1618
1719 #define ADDR_IMMSHIFT_LU52ID 52
1820 #define ADDR_IMMSHIFT_LU32ID 32
21+#define ADDR_IMMSHIFT_LU12IW 12
1922 #define ADDR_IMMSHIFT_ADDU16ID 16
2023
2124 #define ADDR_IMM(addr, INSN) ((addr & ADDR_IMMMASK_##INSN) >> ADDR_IMMSHIFT_##INSN)
@@ -28,6 +31,7 @@ enum reg0i26_op { · 6 unmodified lines
2831 enum reg1i20_op {
2932 lu12iw_op = 0x0a,
3033 lu32id_op = 0x0b,
34+ pcaddi_op = 0x0c,
3135 pcaddu12i_op = 0x0e,
3236 pcaddu18i_op = 0x0f,
3337 };
@@ -35,6 +39,8 @@ enum reg1i20_op { · 1 unmodified line
3539 enum reg1i21_op {
3640 beqz_op = 0x10,
3741 bnez_op = 0x11,
42+ bceqz_op = 0x12, /* bits[9:8] = 0x00 */
43+ bcnez_op = 0x12, /* bits[9:8] = 0x01 */
3844 };
3945
4046 enum reg2_op {
@@ -76,6 +82,10 @@ enum reg2i12_op { · 35 unmodified lines
7682 ldbu_op = 0xa8,
7783 ldhu_op = 0xa9,
7884 ldwu_op = 0xaa,
85+ flds_op = 0xac,
86+ fsts_op = 0xad,
87+ fldd_op = 0xae,
88+ fstd_op = 0xaf,
7989 };
8090
8191 enum reg2i14_op {
@@ -146,6 +156,10 @@ enum reg3_op { · 64 unmodified lines
146156 ldxbu_op = 0x7040,
147157 ldxhu_op = 0x7048,
148158 ldxwu_op = 0x7050,
159+ fldxs_op = 0x7060,
160+ fldxd_op = 0x7068,
161+ fstxs_op = 0x7070,
162+ fstxd_op = 0x7078,
149163 amswapw_op = 0x70c0,
150164 amswapd_op = 0x70c1,
151165 amaddw_op = 0x70c2,
@@ -307,6 +321,12 @@ static inline bool is_imm_negative(unsigned long val, unsigned int bit) · 155 unmodified lines
307321 return val & (1UL << (bit - 1));
308322 }
309323
324+static inline bool is_pc_ins(union loongarch_instruction *ip)
325+{
326+ return ip->reg1i20_format.opcode >= pcaddi_op &&
327+ ip->reg1i20_format.opcode <= pcaddu18i_op;
328+}
329+
310330 static inline bool is_branch_ins(union loongarch_instruction *ip)
311331 {
312332 return ip->reg1i21_format.opcode >= beqz_op &&
@@ -331,6 +351,18 @@ static inline bool is_stack_alloc_ins(union loongarch_instruction *ip) · 18 unmodified lines
331351 is_imm12_negative(ip->reg2i12_format.immediate);
332352 }
333353
354+int larch_insn_read(void *addr, u32 *insnp);
355+int larch_insn_write(void *addr, u32 insn);
356+int larch_insn_patch_text(void *addr, u32 insn);
357+
358+u32 larch_insn_gen_nop(void);
359+u32 larch_insn_gen_b(unsigned long pc, unsigned long dest);
360+u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest);
361+
362+u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj, enum loongarch_gpr rk);
363+u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj);
364+
365+u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm);
334366 u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm);
335367 u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm);
336368 u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest);
@@ -345,6 +377,14 @@ static inline bool unsigned_imm_check(unsigned long val, unsigned int bit) · 8 unmodified lines
345377 return val < (1UL << bit);
346378 }
347379
380+static inline unsigned long sign_extend(unsigned long val, unsigned int idx)
381+{
382+ if (!is_imm_negative(val, idx + 1))
383+ return ((1UL << idx) - 1) & val;
384+ else
385+ return ~((1UL << idx) - 1) | val;
386+}
387+
348388 #define DEF_EMIT_REG0I26_FORMAT(NAME, OP) \
349389 static inline void emit_##NAME(union loongarch_instruction *insn, \
350390 int offset) \
@@ -566,4 +606,10 @@ static inline void emit_##NAME(union loongarch_instruction *insn, \ · 215 unmodified lines
566606
567607 DEF_EMIT_REG3SA2_FORMAT(alsld, alsld_op)
568608
609+struct pt_regs;
610+
611+void emulate_load_store_insn(struct pt_regs *regs, void __user *addr, unsigned int *pc);
612+unsigned long unaligned_read(void __user *addr, void *value, unsigned long n, bool sign);
613+unsigned long unaligned_write(void __user *addr, unsigned long value, unsigned long n);
614+
569615 #endif /* _ASM_INST_H */
arch/loongarch/include/asm/loongson.h+3−0
@@ -136,4 +136,7 @@ typedef enum { · 135 unmodified lines
136136 #define ls7a_writel(val, addr) *(volatile unsigned int *)TO_UNCACHE(addr) = (val)
137137 #define ls7a_writeq(val, addr) *(volatile unsigned long *)TO_UNCACHE(addr) = (val)
138138
139+void enable_gpe_wakeup(void);
140+void enable_pci_wakeup(void);
141+
139142 #endif /* __ASM_LOONGSON_H */
arch/loongarch/include/asm/module.h+16−11
@@ -11,7 +11,7 @@ · 10 unmodified lines
1111 #define RELA_STACK_DEPTH 16
1212
1313 struct mod_section {
14− Elf_Shdr *shdr;
14+ int shndx;
1515 int num_entries;
1616 int max_entries;
1717 };
@@ -20,6 +20,9 @@ struct mod_arch_specific { · 2 unmodified lines
2020 struct mod_section got;
2121 struct mod_section plt;
2222 struct mod_section plt_idx;
23+
24+ /* For CONFIG_DYNAMIC_FTRACE */
25+ struct plt_entry *ftrace_trampolines;
2326 };
2427
2528 struct got_entry {
@@ -37,8 +40,8 @@ struct plt_idx_entry { · 11 unmodified lines
3740 Elf_Addr symbol_addr;
3841 };
3942
40−Elf_Addr module_emit_got_entry(struct module *mod, Elf_Addr val);
41−Elf_Addr module_emit_plt_entry(struct module *mod, Elf_Addr val);
43+Elf_Addr module_emit_got_entry(struct module *mod, Elf_Shdr *sechdrs, Elf_Addr val);
44+Elf_Addr module_emit_plt_entry(struct module *mod, Elf_Shdr *sechdrs, Elf_Addr val);
4245
4346 static inline struct got_entry emit_got_entry(Elf_Addr val)
4447 {
@@ -49,7 +52,7 @@ static inline struct plt_entry emit_plt_entry(unsigned long val) · 4 unmodified lines
4952 {
5053 u32 lu12iw, lu32id, lu52id, jirl;
5154
52− lu12iw = (lu12iw_op << 25 | (((val >> 12) & 0xfffff) << 5) | LOONGARCH_GPR_T1);
55+ lu12iw = larch_insn_gen_lu12iw(LOONGARCH_GPR_T1, ADDR_IMM(val, LU12IW));
5356 lu32id = larch_insn_gen_lu32id(LOONGARCH_GPR_T1, ADDR_IMM(val, LU32ID));
5457 lu52id = larch_insn_gen_lu52id(LOONGARCH_GPR_T1, LOONGARCH_GPR_T1, ADDR_IMM(val, LU52ID));
5558 jirl = larch_insn_gen_jirl(0, LOONGARCH_GPR_T1, 0, (val & 0xfff));
@@ -62,10 +65,10 @@ static inline struct plt_idx_entry emit_plt_idx_entry(unsigned long val) · 6 unmodified lines
6265 return (struct plt_idx_entry) { val };
6366 }
6467
65−static inline int get_plt_idx(unsigned long val, const struct mod_section *sec)
68+static inline int get_plt_idx(unsigned long val, Elf_Shdr *sechdrs, const struct mod_section *sec)
6669 {
6770 int i;
68− struct plt_idx_entry *plt_idx = (struct plt_idx_entry *)sec->shdr->sh_addr;
71+ struct plt_idx_entry *plt_idx = (struct plt_idx_entry *)sechdrs[sec->shndx].sh_addr;
6972
7073 for (i = 0; i < sec->num_entries; i++) {
7174 if (plt_idx[i].symbol_addr == val)
@@ -76,11 +79,12 @@ static inline int get_plt_idx(unsigned long val, const struct mod_section *sec) · 4 unmodified lines
7679 }
7780
7881 static inline struct plt_entry *get_plt_entry(unsigned long val,
79− const struct mod_section *sec_plt,
80− const struct mod_section *sec_plt_idx)
82+ Elf_Shdr *sechdrs,
83+ const struct mod_section *sec_plt,
84+ const struct mod_section *sec_plt_idx)
8185 {
82− int plt_idx = get_plt_idx(val, sec_plt_idx);
83− struct plt_entry *plt = (struct plt_entry *)sec_plt->shdr->sh_addr;
86+ int plt_idx = get_plt_idx(val, sechdrs, sec_plt_idx);
87+ struct plt_entry *plt = (struct plt_entry *)sechdrs[sec_plt->shndx].sh_addr;
8488
8589 if (plt_idx < 0)
8690 return NULL;
@@ -89,10 +93,11 @@ static inline struct plt_entry *get_plt_entry(unsigned long val, · 2 unmodified lines
8993 }
9094
9195 static inline struct got_entry *get_got_entry(Elf_Addr val,
96+ Elf_Shdr *sechdrs,
9297 const struct mod_section *sec)
9398 {
94− struct got_entry *got = (struct got_entry *)sec->shdr->sh_addr;
9599 int i;
100+ struct got_entry *got = (struct got_entry *)sechdrs[sec->shndx].sh_addr;
96101
97102 for (i = 0; i < sec->num_entries; i++)
98103 if (got[i].symbol_addr == val)
arch/loongarch/include/asm/module.lds.h+1−0
@@ -5,4 +5,5 @@ SECTIONS { · 4 unmodified lines
55 .got : { BYTE(0) }
66 .plt : { BYTE(0) }
77 .plt.idx : { BYTE(0) }
8+ .ftrace_trampoline : { BYTE(0) }
89 }
arch/loongarch/include/asm/setup.h+1−0
@@ -13,6 +13,7 @@ · 12 unmodified lines
1313
1414 extern unsigned long eentry;
1515 extern unsigned long tlbrentry;
16+extern char init_command_line[COMMAND_LINE_SIZE];
1617 extern void tlb_init(int cpu);
1718 extern void cpu_cache_init(void);
1819 extern void cache_error_setup(void);
arch/loongarch/include/asm/stackprotector.h+38−0
@@ -0,0 +1,38 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+/*
3+ * GCC stack protector support.
4+ *
5+ * Stack protector works by putting predefined pattern at the start of
6+ * the stack frame and verifying that it hasn't been overwritten when
7+ * returning from the function. The pattern is called stack canary and
8+ * on LoongArch gcc expects it to be defined by a global variable called
9+ * "__stack_chk_guard".
10+ */
11+
12+#ifndef _ASM_STACKPROTECTOR_H
13+#define _ASM_STACKPROTECTOR_H
14+
15+#include <linux/random.h>
16+#include <linux/version.h>
17+
18+extern unsigned long __stack_chk_guard;
19+
20+/*
21+ * Initialize the stackprotector canary value.
22+ *
23+ * NOTE: this must only be called from functions that never return,
24+ * and it must always be inlined.
25+ */
26+static __always_inline void boot_init_stack_canary(void)
27+{
28+ unsigned long canary;
29+
30+ /* Try to get a semi random initial value. */
31+ get_random_bytes(&canary, sizeof(canary));
32+ canary ^= LINUX_VERSION_CODE;
33+
34+ current->stack_canary = canary;
35+ __stack_chk_guard = current->stack_canary;
36+}
37+
38+#endif /* _ASM_STACKPROTECTOR_H */
arch/loongarch/include/asm/string.h+5−0
@@ -5,8 +5,13 @@ · 4 unmodified lines
55 #ifndef _ASM_STRING_H
66 #define _ASM_STRING_H
77
8+#define __HAVE_ARCH_MEMSET
89 extern void *memset(void *__s, int __c, size_t __count);
10+
11+#define __HAVE_ARCH_MEMCPY
912 extern void *memcpy(void *__to, __const__ void *__from, size_t __n);
13+
14+#define __HAVE_ARCH_MEMMOVE
1015 extern void *memmove(void *__dest, __const__ void *__src, size_t __n);
1116
1217 #endif /* _ASM_STRING_H */
arch/loongarch/include/asm/thread_info.h+1−1
@@ -38,7 +38,7 @@ struct thread_info { · 37 unmodified lines
3838 #define INIT_THREAD_INFO(tsk) \
3939 { \
4040 .task = &tsk, \
41− .flags = 0, \
41+ .flags = _TIF_FIXADE, \
4242 .cpu = 0, \
4343 .preempt_count = INIT_PREEMPT_COUNT, \
4444 }
arch/loongarch/include/asm/time.h+1−0
@@ -12,6 +12,7 @@ · 11 unmodified lines
1212 extern u64 cpu_clock_freq;
1313 extern u64 const_clock_freq;
1414
15+extern void save_counter(void);
1516 extern void sync_counter(void);
1617
1718 static inline unsigned int calc_const_freq(void)
arch/loongarch/include/asm/uaccess.h+6−18
@@ -15,7 +15,8 @@ · 14 unmodified lines
1515 #include <linux/string.h>
1616 #include <linux/extable.h>
1717 #include <asm/pgtable.h>
18−#include <asm-generic/extable.h>
18+#include <asm/extable.h>
19+#include <asm/asm-extable.h>
1920 #include <asm-generic/access_ok.h>
2021
2122 extern u64 __ua_limit;
@@ -160,16 +161,9 @@ do { \ · 138 unmodified lines
160161 __asm__ __volatile__( \
161162 "1: " insn " %1, %2 \n" \
162163 "2: \n" \
163− " .section .fixup,\"ax\" \n" \
164− "3: li.w %0, %3 \n" \
165− " move %1, $zero \n" \
166− " b 2b \n" \
167− " .previous \n" \
168− " .section __ex_table,\"a\" \n" \
169− " "__UA_ADDR "\t1b, 3b \n" \
170− " .previous \n" \
164+ _ASM_EXTABLE_UACCESS_ERR_ZERO(1b, 2b, %0, %1) \
171165 : "+r" (__gu_err), "=r" (__gu_tmp) \
172− : "m" (__m(ptr)), "i" (-EFAULT)); \
166+ : "m" (__m(ptr))); \
173167 \
174168 (val) = (__typeof__(*(ptr))) __gu_tmp; \
175169 }
@@ -192,15 +186,9 @@ do { \ · 16 unmodified lines
192186 __asm__ __volatile__( \
193187 "1: " insn " %z2, %1 # __put_user_asm\n" \
194188 "2: \n" \
195− " .section .fixup,\"ax\" \n" \
196− "3: li.w %0, %3 \n" \
197− " b 2b \n" \
198− " .previous \n" \
199− " .section __ex_table,\"a\" \n" \
200− " " __UA_ADDR " 1b, 3b \n" \
201− " .previous \n" \
189+ _ASM_EXTABLE_UACCESS_ERR(1b, 2b, %0) \
202190 : "+r" (__pu_err), "=m" (__m(ptr)) \
203− : "Jr" (__pu_val), "i" (-EFAULT)); \
191+ : "Jr" (__pu_val)); \
204192 }
205193
206194 #define __get_kernel_nofault(dst, src, type, err_label) \
arch/loongarch/include/asm/unwind.h+2−1
@@ -20,7 +20,8 @@ struct unwind_state { · 19 unmodified lines
2020 char type; /* UNWINDER_XXX */
2121 struct stack_info stack_info;
2222 struct task_struct *task;
23− bool first, error;
23+ bool first, error, is_ftrace;
24+ int graph_idx;
2425 unsigned long sp, pc, ra;
2526 };
2627
arch/loongarch/kernel/Makefile+15−1
@@ -7,13 +7,27 @@ extra-y := vmlinux.lds · 6 unmodified lines
77
88 obj-y += head.o cpu-probe.o cacheinfo.o env.o setup.o entry.o genex.o \
99 traps.o irq.o idle.o process.o dma.o mem.o io.o reset.o switch.o \
10− elf.o syscall.o signal.o time.o topology.o inst.o ptrace.o vdso.o
10+ elf.o syscall.o signal.o time.o topology.o inst.o ptrace.o vdso.o \
11+ alternative.o unaligned.o
1112
1213 obj-$(CONFIG_ACPI) += acpi.o
1314 obj-$(CONFIG_EFI) += efi.o
1415
1516 obj-$(CONFIG_CPU_HAS_FPU) += fpu.o
1617
18+ifdef CONFIG_FUNCTION_TRACER
19+ ifndef CONFIG_DYNAMIC_FTRACE
20+ obj-y += mcount.o ftrace.o
21+ CFLAGS_REMOVE_ftrace.o = $(CC_FLAGS_FTRACE)
22+ else
23+ obj-y += mcount_dyn.o ftrace_dyn.o
24+ CFLAGS_REMOVE_ftrace_dyn.o = $(CC_FLAGS_FTRACE)
25+ endif
26+ CFLAGS_REMOVE_inst.o = $(CC_FLAGS_FTRACE)
27+ CFLAGS_REMOVE_time.o = $(CC_FLAGS_FTRACE)
28+ CFLAGS_REMOVE_perf_event.o = $(CC_FLAGS_FTRACE)
29+endif
30+
1731 obj-$(CONFIG_MODULES) += module.o module-sections.o
1832 obj-$(CONFIG_STACKTRACE) += stacktrace.o
1933
arch/loongarch/kernel/acpi.c+15−2
@@ -12,6 +12,7 @@ · 11 unmodified lines
1212 #include <linux/irq.h>
1313 #include <linux/irqdomain.h>
1414 #include <linux/memblock.h>
15+#include <linux/of_fdt.h>
1516 #include <linux/serial_core.h>
1617 #include <asm/io.h>
1718 #include <asm/numa.h>
@@ -139,20 +140,26 @@ static void __init acpi_process_madt(void) · 121 unmodified lines
139140 loongson_sysconf.nr_cpus = num_processors;
140141 }
141142
143+#ifndef CONFIG_SUSPEND
144+int (*acpi_suspend_lowlevel)(void);
145+#else
146+int (*acpi_suspend_lowlevel)(void) = loongarch_acpi_suspend;
147+#endif
148+
142149 void __init acpi_boot_table_init(void)
143150 {
144151 /*
145152 * If acpi_disabled, bail out
146153 */
147154 if (acpi_disabled)
148− return;
155+ goto fdt_earlycon;
149156
150157 /*
151158 * Initialize the ACPI boot-time table parser.
152159 */
153160 if (acpi_table_init()) {
154161 disable_acpi();
155− return;
162+ goto fdt_earlycon;
156163 }
157164
158165 loongson_sysconf.boot_cpu_id = read_csr_cpuid();
@@ -164,6 +171,12 @@ void __init acpi_boot_table_init(void) · 5 unmodified lines
164171
165172 /* Do not enable ACPI SPCR console by default */
166173 acpi_parse_spcr(earlycon_acpi_spcr_enable, false);
174+
175+ return;
176+
177+fdt_earlycon:
178+ if (earlycon_acpi_spcr_enable)
179+ early_init_dt_scan_chosen_stdout();
167180 }
168181
169182 #ifdef CONFIG_ACPI_NUMA
arch/loongarch/kernel/alternative.c+246−0
@@ -0,0 +1,246 @@
1+// SPDX-License-Identifier: GPL-2.0-only
2+#include <linux/mm.h>
3+#include <linux/module.h>
4+#include <asm/alternative.h>
5+#include <asm/cacheflush.h>
6+#include <asm/inst.h>
7+#include <asm/sections.h>
8+
9+int __read_mostly alternatives_patched;
10+
11+EXPORT_SYMBOL_GPL(alternatives_patched);
12+
13+#define MAX_PATCH_SIZE (((u8)(-1)) / LOONGARCH_INSN_SIZE)
14+
15+static int __initdata_or_module debug_alternative;
16+
17+static int __init debug_alt(char *str)
18+{
19+ debug_alternative = 1;
20+ return 1;
21+}
22+__setup("debug-alternative", debug_alt);
23+
24+#define DPRINTK(fmt, args...) \
25+do { \
26+ if (debug_alternative) \
27+ printk(KERN_DEBUG "%s: " fmt "\n", __func__, ##args); \
28+} while (0)
29+
30+#define DUMP_WORDS(buf, count, fmt, args...) \
31+do { \
32+ if (unlikely(debug_alternative)) { \
33+ int _j; \
34+ union loongarch_instruction *_buf = buf; \
35+ \
36+ if (!(count)) \
37+ break; \
38+ \
39+ printk(KERN_DEBUG fmt, ##args); \
40+ for (_j = 0; _j < count - 1; _j++) \
41+ printk(KERN_CONT "<%08x> ", _buf[_j].word); \
42+ printk(KERN_CONT "<%08x>\n", _buf[_j].word); \
43+ } \
44+} while (0)
45+
46+/* Use this to add nops to a buffer, then text_poke the whole buffer. */
47+static void __init_or_module add_nops(union loongarch_instruction *insn, int count)
48+{
49+ while (count--) {
50+ insn->word = INSN_NOP;
51+ insn++;
52+ }
53+}
54+
55+/* Is the jump addr in local .altinstructions */
56+static inline bool in_alt_jump(unsigned long jump, void *start, void *end)
57+{
58+ return jump >= (unsigned long)start && jump < (unsigned long)end;
59+}
60+
61+static void __init_or_module recompute_jump(union loongarch_instruction *buf,
62+ union loongarch_instruction *dest, union loongarch_instruction *src,
63+ void *start, void *end)
64+{
65+ unsigned int si, si_l, si_h;
66+ unsigned long cur_pc, jump_addr, pc;
67+ long offset;
68+
69+ cur_pc = (unsigned long)src;
70+ pc = (unsigned long)dest;
71+
72+ si_l = src->reg0i26_format.immediate_l;
73+ si_h = src->reg0i26_format.immediate_h;
74+ switch (src->reg0i26_format.opcode) {
75+ case b_op:
76+ case bl_op:
77+ jump_addr = cur_pc + sign_extend((si_h << 16 | si_l) << 2, 27);
78+ if (in_alt_jump(jump_addr, start, end))
79+ return;
80+ offset = jump_addr - pc;
81+ BUG_ON(offset < -SZ_128M || offset >= SZ_128M);
82+ offset >>= 2;
83+ buf->reg0i26_format.immediate_h = offset >> 16;
84+ buf->reg0i26_format.immediate_l = offset;
85+ return;
86+ }
87+
88+ si_l = src->reg1i21_format.immediate_l;
89+ si_h = src->reg1i21_format.immediate_h;
90+ switch (src->reg1i21_format.opcode) {
91+ case bceqz_op: /* bceqz_op = bcnez_op */
92+ BUG_ON(buf->reg1i21_format.rj & BIT(4));
93+ fallthrough;
94+ case beqz_op:
95+ case bnez_op:
96+ jump_addr = cur_pc + sign_extend((si_h << 16 | si_l) << 2, 22);
97+ if (in_alt_jump(jump_addr, start, end))
98+ return;
99+ offset = jump_addr - pc;
100+ BUG_ON(offset < -SZ_4M || offset >= SZ_4M);
101+ offset >>= 2;
102+ buf->reg1i21_format.immediate_h = offset >> 16;
103+ buf->reg1i21_format.immediate_l = offset;
104+ return;
105+ }
106+
107+ si = src->reg2i16_format.immediate;
108+ switch (src->reg2i16_format.opcode) {
109+ case beq_op:
110+ case bne_op:
111+ case blt_op:
112+ case bge_op:
113+ case bltu_op:
114+ case bgeu_op:
115+ jump_addr = cur_pc + sign_extend(si << 2, 17);
116+ if (in_alt_jump(jump_addr, start, end))
117+ return;
118+ offset = jump_addr - pc;
119+ BUG_ON(offset < -SZ_128K || offset >= SZ_128K);
120+ offset >>= 2;
121+ buf->reg2i16_format.immediate = offset;
122+ return;
123+ }
124+}
125+
126+static int __init_or_module copy_alt_insns(union loongarch_instruction *buf,
127+ union loongarch_instruction *dest, union loongarch_instruction *src, int nr)
128+{
129+ int i;
130+
131+ for (i = 0; i < nr; i++) {
132+ buf[i].word = src[i].word;
133+
134+ if (is_pc_ins(&src[i])) {
135+ pr_err("Not support pcrel instruction at present!");
136+ return -EINVAL;
137+ }
138+
139+ if (is_branch_ins(&src[i]) &&
140+ src[i].reg2i16_format.opcode != jirl_op) {
141+ recompute_jump(&buf[i], &dest[i], &src[i], src, src + nr);
142+ }
143+ }
144+
145+ return 0;
146+}
147+
148+/*
149+ * text_poke_early - Update instructions on a live kernel at boot time
150+ *
151+ * When you use this code to patch more than one byte of an instruction
152+ * you need to make sure that other CPUs cannot execute this code in parallel.
153+ * Also no thread must be currently preempted in the middle of these
154+ * instructions. And on the local CPU you need to be protected again NMI or MCE
155+ * handlers seeing an inconsistent instruction while you patch.
156+ */
157+static void *__init_or_module text_poke_early(union loongarch_instruction *insn,
158+ union loongarch_instruction *buf, unsigned int nr)
159+{
160+ int i;
161+ unsigned long flags;
162+
163+ local_irq_save(flags);
164+
165+ for (i = 0; i < nr; i++)
166+ insn[i].word = buf[i].word;
167+
168+ local_irq_restore(flags);
169+
170+ wbflush();
171+ flush_icache_range((unsigned long)insn, (unsigned long)(insn + nr));
172+
173+ return insn;
174+}
175+
176+/*
177+ * Replace instructions with better alternatives for this CPU type. This runs
178+ * before SMP is initialized to avoid SMP problems with self modifying code.
179+ * This implies that asymmetric systems where APs have less capabilities than
180+ * the boot processor are not handled. Tough. Make sure you disable such
181+ * features by hand.
182+ */
183+void __init_or_module apply_alternatives(struct alt_instr *start, struct alt_instr *end)
184+{
185+ struct alt_instr *a;
186+ unsigned int nr_instr, nr_repl, nr_insnbuf;
187+ union loongarch_instruction *instr, *replacement;
188+ union loongarch_instruction insnbuf[MAX_PATCH_SIZE];
189+
190+ DPRINTK("alt table %px, -> %px", start, end);
191+ /*
192+ * The scan order should be from start to end. A later scanned
193+ * alternative code can overwrite previously scanned alternative code.
194+ * Some kernel functions (e.g. memcpy, memset, etc) use this order to
195+ * patch code.
196+ *
197+ * So be careful if you want to change the scan order to any other
198+ * order.
199+ */
200+ for (a = start; a < end; a++) {
201+ nr_insnbuf = 0;
202+
203+ instr = (void *)&a->instr_offset + a->instr_offset;
204+ replacement = (void *)&a->replace_offset + a->replace_offset;
205+
206+ BUG_ON(a->instrlen > sizeof(insnbuf));
207+ BUG_ON(a->instrlen & 0x3);
208+ BUG_ON(a->replacementlen & 0x3);
209+
210+ nr_instr = a->instrlen / LOONGARCH_INSN_SIZE;
211+ nr_repl = a->replacementlen / LOONGARCH_INSN_SIZE;
212+
213+ if (!cpu_has(a->feature)) {
214+ DPRINTK("feat not exist: %d, old: (%px len: %d), repl: (%px, len: %d)",
215+ a->feature, instr, a->instrlen,
216+ replacement, a->replacementlen);
217+
218+ continue;
219+ }
220+
221+ DPRINTK("feat: %d, old: (%px len: %d), repl: (%px, len: %d)",
222+ a->feature, instr, a->instrlen,
223+ replacement, a->replacementlen);
224+
225+ DUMP_WORDS(instr, nr_instr, "%px: old_insn: ", instr);
226+ DUMP_WORDS(replacement, nr_repl, "%px: rpl_insn: ", replacement);
227+
228+ copy_alt_insns(insnbuf, instr, replacement, nr_repl);
229+ nr_insnbuf = nr_repl;
230+
231+ if (nr_instr > nr_repl) {
232+ add_nops(insnbuf + nr_repl, nr_instr - nr_repl);
233+ nr_insnbuf += nr_instr - nr_repl;
234+ }
235+ DUMP_WORDS(insnbuf, nr_insnbuf, "%px: final_insn: ", instr);
236+
237+ text_poke_early(instr, insnbuf, nr_insnbuf);
238+ }
239+}
240+
241+void __init alternative_instructions(void)
242+{
243+ apply_alternatives(__alt_instructions, __alt_instructions_end);
244+
245+ alternatives_patched = 1;
246+}
arch/loongarch/kernel/asm-offsets.c+15−0
@@ -68,6 +68,9 @@ void output_task_defines(void) · 67 unmodified lines
6868 OFFSET(TASK_FLAGS, task_struct, flags);
6969 OFFSET(TASK_MM, task_struct, mm);
7070 OFFSET(TASK_PID, task_struct, pid);
71+#if defined(CONFIG_STACKPROTECTOR)
72+ OFFSET(TASK_STACK_CANARY, task_struct, stack_canary);
73+#endif
7174 DEFINE(TASK_STRUCT_SIZE, sizeof(struct task_struct));
7275 BLANK();
7376 }
@@ -257,3 +260,15 @@ void output_smpboot_defines(void) · 183 unmodified lines
257260 BLANK();
258261 }
259262 #endif
263+
264+#ifdef CONFIG_HIBERNATION
265+void output_pbe_defines(void)
266+{
267+ COMMENT(" Linux struct pbe offsets. ");
268+ OFFSET(PBE_ADDRESS, pbe, address);
269+ OFFSET(PBE_ORIG_ADDRESS, pbe, orig_address);
270+ OFFSET(PBE_NEXT, pbe, next);
271+ DEFINE(PBE_SIZE, sizeof(struct pbe));
272+ BLANK();
273+}
274+#endif
arch/loongarch/kernel/efi.c+14−1
@@ -28,16 +28,29 @@ static unsigned long efi_nr_tables; · 27 unmodified lines
2828 static unsigned long efi_config_table;
2929
3030 static unsigned long __initdata boot_memmap = EFI_INVALID_TABLE_ADDR;
31+static unsigned long __initdata fdt_pointer = EFI_INVALID_TABLE_ADDR;
3132
3233 static efi_system_table_t *efi_systab;
3334 static efi_config_table_type_t arch_tables[] __initdata = {
3435 {LINUX_EFI_BOOT_MEMMAP_GUID, &boot_memmap, "MEMMAP" },
36+ {DEVICE_TREE_GUID, &fdt_pointer, "FDTPTR" },
3537 {},
3638 };
3739
40+void __init *efi_fdt_pointer(void)
41+{
42+ if (!efi_systab)
43+ return NULL;
44+
45+ if (fdt_pointer == EFI_INVALID_TABLE_ADDR)
46+ return NULL;
47+
48+ return early_memremap_ro(fdt_pointer, SZ_64K);
49+}
50+
3851 void __init efi_runtime_init(void)
3952 {
40− if (!efi_enabled(EFI_BOOT))
53+ if (!efi_enabled(EFI_BOOT) || !efi_systab->runtime)
4154 return;
4255
4356 if (efi_runtime_disabled()) {
arch/loongarch/kernel/env.c+2−0
@@ -11,6 +11,7 @@ · 10 unmodified lines
1111 #include <asm/early_ioremap.h>
1212 #include <asm/bootinfo.h>
1313 #include <asm/loongson.h>
14+#include <asm/setup.h>
1415
1516 u64 efi_system_table;
1617 struct loongson_system_configuration loongson_sysconf;
@@ -27,6 +28,7 @@ void __init init_environ(void) · 10 unmodified lines
2728 clear_bit(EFI_BOOT, &efi.flags);
2829
2930 strscpy(boot_command_line, cmdline, COMMAND_LINE_SIZE);
31+ strscpy(init_command_line, cmdline, COMMAND_LINE_SIZE);
3032 early_memunmap(cmdline, COMMAND_LINE_SIZE);
3133
3234 efi_system_table = fw_arg2;
arch/loongarch/kernel/fpu.S+2−3
@@ -8,6 +8,7 @@ · 7 unmodified lines
88 */
99 #include <asm/asm.h>
1010 #include <asm/asmmacro.h>
11+#include <asm/asm-extable.h>
1112 #include <asm/asm-offsets.h>
1213 #include <asm/errno.h>
1314 #include <asm/export.h>
@@ -21,9 +22,7 @@ · 7 unmodified lines
2122
2223 .macro EX insn, reg, src, offs
2324 .ex\@: \insn \reg, \src, \offs
24− .section __ex_table,"a"
25− PTR .ex\@, fault
26− .previous
25+ _asm_extable .ex\@, fault
2726 .endm
2827
2928 .macro sc_save_fp base
arch/loongarch/kernel/ftrace.c+73−0
@@ -0,0 +1,73 @@
1+// SPDX-License-Identifier: GPL-2.0
2+/*
3+ * Copyright (C) 2022 Loongson Technology Corporation Limited
4+ */
5+
6+#include <linux/init.h>
7+#include <linux/ftrace.h>
8+#include <linux/syscalls.h>
9+#include <linux/uaccess.h>
10+
11+#include <asm/asm.h>
12+#include <asm/asm-offsets.h>
13+#include <asm/cacheflush.h>
14+#include <asm/inst.h>
15+#include <asm/loongarch.h>
16+#include <asm/syscall.h>
17+
18+#include <asm-generic/sections.h>
19+
20+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
21+
22+/*
23+ * As `call _mcount` follows LoongArch psABI, ra-saved operation and
24+ * stack operation can be found before this insn.
25+ */
26+
27+static int ftrace_get_parent_ra_addr(unsigned long insn_addr, int *ra_off)
28+{
29+ int limit = 32;
30+ union loongarch_instruction *insn;
31+
32+ insn = (union loongarch_instruction *)insn_addr;
33+
34+ do {
35+ insn--;
36+ limit--;
37+
38+ if (is_ra_save_ins(insn))
39+ *ra_off = -((1 << 12) - insn->reg2i12_format.immediate);
40+
41+ } while (!is_stack_alloc_ins(insn) && limit);
42+
43+ if (!limit)
44+ return -EINVAL;
45+
46+ return 0;
47+}
48+
49+void prepare_ftrace_return(unsigned long self_addr,
50+ unsigned long callsite_sp, unsigned long old)
51+{
52+ int ra_off;
53+ unsigned long return_hooker = (unsigned long)&return_to_handler;
54+
55+ if (unlikely(ftrace_graph_is_dead()))
56+ return;
57+
58+ if (unlikely(atomic_read(&current->tracing_graph_pause)))
59+ return;
60+
61+ if (ftrace_get_parent_ra_addr(self_addr, &ra_off))
62+ goto out;
63+
64+ if (!function_graph_enter(old, self_addr, 0, NULL))
65+ *(unsigned long *)(callsite_sp + ra_off) = return_hooker;
66+
67+ return;
68+
69+out:
70+ ftrace_graph_stop();
71+ WARN_ON(1);
72+}
73+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
arch/loongarch/kernel/ftrace_dyn.c+273−0
@@ -0,0 +1,273 @@
1+// SPDX-License-Identifier: GPL-2.0
2+/*
3+ * Based on arch/arm64/kernel/ftrace.c
4+ *
5+ * Copyright (C) 2022 Loongson Technology Corporation Limited
6+ */
7+
8+#include <linux/ftrace.h>
9+#include <linux/uaccess.h>
10+
11+#include <asm/inst.h>
12+#include <asm/module.h>
13+
14+static int ftrace_modify_code(unsigned long pc, u32 old, u32 new, bool validate)
15+{
16+ u32 replaced;
17+
18+ if (validate) {
19+ if (larch_insn_read((void *)pc, &replaced))
20+ return -EFAULT;
21+
22+ if (replaced != old)
23+ return -EINVAL;
24+ }
25+
26+ if (larch_insn_patch_text((void *)pc, new))
27+ return -EPERM;
28+
29+ return 0;
30+}
31+
32+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
33+
34+#ifdef CONFIG_MODULES
35+static inline int __get_mod(struct module **mod, unsigned long addr)
36+{
37+ preempt_disable();
38+ *mod = __module_text_address(addr);
39+ preempt_enable();
40+
41+ if (WARN_ON(!(*mod)))
42+ return -EINVAL;
43+
44+ return 0;
45+}
46+
47+static struct plt_entry *get_ftrace_plt(struct module *mod, unsigned long addr)
48+{
49+ struct plt_entry *plt = mod->arch.ftrace_trampolines;
50+
51+ if (addr == FTRACE_ADDR)
52+ return &plt[FTRACE_PLT_IDX];
53+ if (addr == FTRACE_REGS_ADDR &&
54+ IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS))
55+ return &plt[FTRACE_REGS_PLT_IDX];
56+
57+ return NULL;
58+}
59+
60+static unsigned long get_plt_addr(struct module *mod, unsigned long addr)
61+{
62+ struct plt_entry *plt;
63+
64+ plt = get_ftrace_plt(mod, addr);
65+ if (!plt) {
66+ pr_err("ftrace: no module PLT for %ps\n", (void *)addr);
67+ return -EINVAL;
68+ }
69+
70+ return (unsigned long)plt;
71+}
72+#endif
73+
74+int ftrace_modify_call(struct dyn_ftrace *rec, unsigned long old_addr, unsigned long addr)
75+{
76+ u32 old, new;
77+ unsigned long pc;
78+ long offset __maybe_unused;
79+
80+ pc = rec->ip + LOONGARCH_INSN_SIZE;
81+
82+#ifdef CONFIG_MODULES
83+ offset = (long)pc - (long)addr;
84+
85+ if (offset < -SZ_128M || offset >= SZ_128M) {
86+ int ret;
87+ struct module *mod;
88+
89+ ret = __get_mod(&mod, pc);
90+ if (ret)
91+ return ret;
92+
93+ addr = get_plt_addr(mod, addr);
94+
95+ old_addr = get_plt_addr(mod, old_addr);
96+ }
97+#endif
98+
99+ new = larch_insn_gen_bl(pc, addr);
100+ old = larch_insn_gen_bl(pc, old_addr);
101+
102+ return ftrace_modify_code(pc, old, new, true);
103+}
104+
105+#endif /* CONFIG_DYNAMIC_FTRACE_WITH_REGS */
106+
107+int ftrace_update_ftrace_func(ftrace_func_t func)
108+{
109+ u32 new;
110+ unsigned long pc;
111+
112+ pc = (unsigned long)&ftrace_call;
113+ new = larch_insn_gen_bl(pc, (unsigned long)func);
114+
115+ return ftrace_modify_code(pc, 0, new, false);
116+}
117+
118+/*
119+ * The compiler has inserted 2 NOPs before the regular function prologue.
120+ * T series registers are available and safe because of LoongArch's psABI.
121+ *
122+ * At runtime, we can replace nop with bl to enable ftrace call and replace bl
123+ * with nop to disable ftrace call. The bl requires us to save the original RA
124+ * value, so it saves RA at t0 here.
125+ *
126+ * Details are:
127+ *
128+ * | Compiled | Disabled | Enabled |
129+ * +------------+------------------------+------------------------+
130+ * | nop | move t0, ra | move t0, ra |
131+ * | nop | nop | bl ftrace_caller |
132+ * | func_body | func_body | func_body |
133+ *
134+ * The RA value will be recovered by ftrace_regs_entry, and restored into RA
135+ * before returning to the regular function prologue. When a function is not
136+ * being traced, the "move t0, ra" is not harmful.
137+ */
138+
139+int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec)
140+{
141+ u32 old, new;
142+ unsigned long pc;
143+
144+ pc = rec->ip;
145+ old = larch_insn_gen_nop();
146+ new = larch_insn_gen_move(LOONGARCH_GPR_T0, LOONGARCH_GPR_RA);
147+
148+ return ftrace_modify_code(pc, old, new, true);
149+}
150+
151+int ftrace_make_call(struct dyn_ftrace *rec, unsigned long addr)
152+{
153+ u32 old, new;
154+ unsigned long pc;
155+ long offset __maybe_unused;
156+
157+ pc = rec->ip + LOONGARCH_INSN_SIZE;
158+
159+#ifdef CONFIG_MODULES
160+ offset = (long)pc - (long)addr;
161+
162+ if (offset < -SZ_128M || offset >= SZ_128M) {
163+ int ret;
164+ struct module *mod;
165+
166+ ret = __get_mod(&mod, pc);
167+ if (ret)
168+ return ret;
169+
170+ addr = get_plt_addr(mod, addr);
171+ }
172+#endif
173+
174+ old = larch_insn_gen_nop();
175+ new = larch_insn_gen_bl(pc, addr);
176+
177+ return ftrace_modify_code(pc, old, new, true);
178+}
179+
180+int ftrace_make_nop(struct module *mod, struct dyn_ftrace *rec, unsigned long addr)
181+{
182+ u32 old, new;
183+ unsigned long pc;
184+ long offset __maybe_unused;
185+
186+ pc = rec->ip + LOONGARCH_INSN_SIZE;
187+
188+#ifdef CONFIG_MODULES
189+ offset = (long)pc - (long)addr;
190+
191+ if (offset < -SZ_128M || offset >= SZ_128M) {
192+ int ret;
193+ struct module *mod;
194+
195+ ret = __get_mod(&mod, pc);
196+ if (ret)
197+ return ret;
198+
199+ addr = get_plt_addr(mod, addr);
200+ }
201+#endif
202+
203+ new = larch_insn_gen_nop();
204+ old = larch_insn_gen_bl(pc, addr);
205+
206+ return ftrace_modify_code(pc, old, new, true);
207+}
208+
209+void arch_ftrace_update_code(int command)
210+{
211+ command |= FTRACE_MAY_SLEEP;
212+ ftrace_modify_all_code(command);
213+}
214+
215+int __init ftrace_dyn_arch_init(void)
216+{
217+ return 0;
218+}
219+
220+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
221+void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent)
222+{
223+ unsigned long old;
224+ unsigned long return_hooker = (unsigned long)&return_to_handler;
225+
226+ if (unlikely(atomic_read(&current->tracing_graph_pause)))
227+ return;
228+
229+ old = *parent;
230+
231+ if (!function_graph_enter(old, self_addr, 0, parent))
232+ *parent = return_hooker;
233+}
234+
235+#ifdef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS
236+void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
237+ struct ftrace_ops *op, struct ftrace_regs *fregs)
238+{
239+ struct pt_regs *regs = &fregs->regs;
240+ unsigned long *parent = (unsigned long *)&regs->regs[1];
241+
242+ prepare_ftrace_return(ip, (unsigned long *)parent);
243+}
244+#else
245+static int ftrace_modify_graph_caller(bool enable)
246+{
247+ u32 branch, nop;
248+ unsigned long pc, func;
249+ extern void ftrace_graph_call(void);
250+
251+ pc = (unsigned long)&ftrace_graph_call;
252+ func = (unsigned long)&ftrace_graph_caller;
253+
254+ nop = larch_insn_gen_nop();
255+ branch = larch_insn_gen_b(pc, func);
256+
257+ if (enable)
258+ return ftrace_modify_code(pc, nop, branch, true);
259+ else
260+ return ftrace_modify_code(pc, branch, nop, true);
261+}
262+
263+int ftrace_enable_ftrace_graph_caller(void)
264+{
265+ return ftrace_modify_graph_caller(true);
266+}
267+
268+int ftrace_disable_ftrace_graph_caller(void)
269+{
270+ return ftrace_modify_graph_caller(false);
271+}
272+#endif /* CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS */
273+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
arch/loongarch/kernel/inst.c+127−0
@@ -2,8 +2,135 @@ · 1 unmodified line
22 /*
33 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
44 */
5+#include <linux/sizes.h>
6+#include <linux/uaccess.h>
7+
8+#include <asm/cacheflush.h>
59 #include <asm/inst.h>
610
11+static DEFINE_RAW_SPINLOCK(patch_lock);
12+
13+int larch_insn_read(void *addr, u32 *insnp)
14+{
15+ int ret;
16+ u32 val;
17+
18+ ret = copy_from_kernel_nofault(&val, addr, LOONGARCH_INSN_SIZE);
19+ if (!ret)
20+ *insnp = val;
21+
22+ return ret;
23+}
24+
25+int larch_insn_write(void *addr, u32 insn)
26+{
27+ int ret;
28+ unsigned long flags = 0;
29+
30+ raw_spin_lock_irqsave(&patch_lock, flags);
31+ ret = copy_to_kernel_nofault(addr, &insn, LOONGARCH_INSN_SIZE);
32+ raw_spin_unlock_irqrestore(&patch_lock, flags);
33+
34+ return ret;
35+}
36+
37+int larch_insn_patch_text(void *addr, u32 insn)
38+{
39+ int ret;
40+ u32 *tp = addr;
41+
42+ if ((unsigned long)tp & 3)
43+ return -EINVAL;
44+
45+ ret = larch_insn_write(tp, insn);
46+ if (!ret)
47+ flush_icache_range((unsigned long)tp,
48+ (unsigned long)tp + LOONGARCH_INSN_SIZE);
49+
50+ return ret;
51+}
52+
53+u32 larch_insn_gen_nop(void)
54+{
55+ return INSN_NOP;
56+}
57+
58+u32 larch_insn_gen_b(unsigned long pc, unsigned long dest)
59+{
60+ long offset = dest - pc;
61+ unsigned int immediate_l, immediate_h;
62+ union loongarch_instruction insn;
63+
64+ if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) {
65+ pr_warn("The generated b instruction is out of range.\n");
66+ return INSN_BREAK;
67+ }
68+
69+ offset >>= 2;
70+
71+ immediate_l = offset & 0xffff;
72+ offset >>= 16;
73+ immediate_h = offset & 0x3ff;
74+
75+ insn.reg0i26_format.opcode = b_op;
76+ insn.reg0i26_format.immediate_l = immediate_l;
77+ insn.reg0i26_format.immediate_h = immediate_h;
78+
79+ return insn.word;
80+}
81+
82+u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest)
83+{
84+ long offset = dest - pc;
85+ unsigned int immediate_l, immediate_h;
86+ union loongarch_instruction insn;
87+
88+ if ((offset & 3) || offset < -SZ_128M || offset >= SZ_128M) {
89+ pr_warn("The generated bl instruction is out of range.\n");
90+ return INSN_BREAK;
91+ }
92+
93+ offset >>= 2;
94+
95+ immediate_l = offset & 0xffff;
96+ offset >>= 16;
97+ immediate_h = offset & 0x3ff;
98+
99+ insn.reg0i26_format.opcode = bl_op;
100+ insn.reg0i26_format.immediate_l = immediate_l;
101+ insn.reg0i26_format.immediate_h = immediate_h;
102+
103+ return insn.word;
104+}
105+
106+u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj, enum loongarch_gpr rk)
107+{
108+ union loongarch_instruction insn;
109+
110+ insn.reg3_format.opcode = or_op;
111+ insn.reg3_format.rd = rd;
112+ insn.reg3_format.rj = rj;
113+ insn.reg3_format.rk = rk;
114+
115+ return insn.word;
116+}
117+
118+u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj)
119+{
120+ return larch_insn_gen_or(rd, rj, 0);
121+}
122+
123+u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm)
124+{
125+ union loongarch_instruction insn;
126+
127+ insn.reg1i20_format.opcode = lu12iw_op;
128+ insn.reg1i20_format.rd = rd;
129+ insn.reg1i20_format.immediate = imm;
130+
131+ return insn.word;
132+}
133+
7134 u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm)
8135 {
9136 union loongarch_instruction insn;
arch/loongarch/kernel/mcount.S+96−0
@@ -0,0 +1,96 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+/*
3+ * LoongArch specific _mcount support
4+ *
5+ * Copyright (C) 2022 Loongson Technology Corporation Limited
6+ */
7+
8+#include <asm/export.h>
9+#include <asm/ftrace.h>
10+#include <asm/regdef.h>
11+#include <asm/stackframe.h>
12+
13+ .text
14+
15+#define MCOUNT_S0_OFFSET (0)
16+#define MCOUNT_RA_OFFSET (SZREG)
17+#define MCOUNT_STACK_SIZE (2 * SZREG)
18+
19+ .macro MCOUNT_SAVE_REGS
20+ PTR_ADDI sp, sp, -MCOUNT_STACK_SIZE
21+ PTR_S s0, sp, MCOUNT_S0_OFFSET
22+ PTR_S ra, sp, MCOUNT_RA_OFFSET
23+ move s0, a0
24+ .endm
25+
26+ .macro MCOUNT_RESTORE_REGS
27+ move a0, s0
28+ PTR_L ra, sp, MCOUNT_RA_OFFSET
29+ PTR_L s0, sp, MCOUNT_S0_OFFSET
30+ PTR_ADDI sp, sp, MCOUNT_STACK_SIZE
31+ .endm
32+
33+SYM_FUNC_START(_mcount)
34+ la.pcrel t1, ftrace_stub
35+ la.pcrel t2, ftrace_trace_function /* Prepare t2 for (1) */
36+ PTR_L t2, t2, 0
37+ beq t1, t2, fgraph_trace
38+
39+ MCOUNT_SAVE_REGS
40+
41+ move a0, ra /* arg0: self return address */
42+ move a1, s0 /* arg1: parent's return address */
43+ jirl ra, t2, 0 /* (1) call *ftrace_trace_function */
44+
45+ MCOUNT_RESTORE_REGS
46+
47+fgraph_trace:
48+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
49+ la.pcrel t1, ftrace_stub
50+ la.pcrel t3, ftrace_graph_return
51+ PTR_L t3, t3, 0
52+ bne t1, t3, ftrace_graph_caller
53+ la.pcrel t1, ftrace_graph_entry_stub
54+ la.pcrel t3, ftrace_graph_entry
55+ PTR_L t3, t3, 0
56+ bne t1, t3, ftrace_graph_caller
57+#endif
58+
59+SYM_INNER_LABEL(ftrace_stub, SYM_L_GLOBAL)
60+ jr ra
61+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
62+SYM_INNER_LABEL(ftrace_graph_func, SYM_L_GLOBAL)
63+ bl ftrace_stub
64+#endif
65+SYM_FUNC_END(_mcount)
66+EXPORT_SYMBOL(_mcount)
67+
68+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
69+SYM_FUNC_START(ftrace_graph_caller)
70+ MCOUNT_SAVE_REGS
71+
72+ PTR_ADDI a0, ra, -4 /* arg0: Callsite self return addr */
73+ PTR_ADDI a1, sp, MCOUNT_STACK_SIZE /* arg1: Callsite sp */
74+ move a2, s0 /* arg2: Callsite parent ra */
75+ bl prepare_ftrace_return
76+
77+ MCOUNT_RESTORE_REGS
78+ jr ra
79+SYM_FUNC_END(ftrace_graph_caller)
80+
81+SYM_FUNC_START(return_to_handler)
82+ PTR_ADDI sp, sp, -2 * SZREG
83+ PTR_S a0, sp, 0
84+ PTR_S a1, sp, SZREG
85+
86+ bl ftrace_return_to_handler
87+
88+ /* Restore the real parent address: a0 -> ra */
89+ move ra, a0
90+
91+ PTR_L a0, sp, 0
92+ PTR_L a1, sp, SZREG
93+ PTR_ADDI sp, sp, 2 * SZREG
94+ jr ra
95+SYM_FUNC_END(return_to_handler)
96+#endif /* CONFIG_FUNCTION_GRAPH_TRACER */
arch/loongarch/kernel/mcount_dyn.S+149−0
@@ -0,0 +1,149 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+/*
3+ * Copyright (C) 2022 Loongson Technology Corporation Limited
4+ */
5+
6+#include <asm/export.h>
7+#include <asm/ftrace.h>
8+#include <asm/regdef.h>
9+#include <asm/stackframe.h>
10+
11+ .text
12+/*
13+ * Due to -fpatchable-function-entry=2: the compiler inserted 2 NOPs before the
14+ * regular C function prologue. When PC arrived here, the last 2 instructions
15+ * are as follows:
16+ * move t0, ra
17+ * bl callsite (for modules, callsite is a tramplione)
18+ *
19+ * modules trampoline is as follows:
20+ * lu12i.w t1, callsite[31:12]
21+ * lu32i.d t1, callsite[51:32]
22+ * lu52i.d t1, t1, callsite[63:52]
23+ * jirl zero, t1, callsite[11:0] >> 2
24+ *
25+ * See arch/loongarch/kernel/ftrace_dyn.c for details. Here, pay attention to
26+ * that the T series regs are available and safe because each C functions
27+ * follows the LoongArch's psABI as well.
28+ */
29+
30+ .macro ftrace_regs_entry allregs=0
31+ PTR_ADDI sp, sp, -PT_SIZE
32+ PTR_S t0, sp, PT_R1 /* Save parent ra at PT_R1(RA) */
33+ PTR_S a0, sp, PT_R4
34+ PTR_S a1, sp, PT_R5
35+ PTR_S a2, sp, PT_R6
36+ PTR_S a3, sp, PT_R7
37+ PTR_S a4, sp, PT_R8
38+ PTR_S a5, sp, PT_R9
39+ PTR_S a6, sp, PT_R10
40+ PTR_S a7, sp, PT_R11
41+ PTR_S fp, sp, PT_R22
42+ .if \allregs
43+ PTR_S tp, sp, PT_R2
44+ PTR_S t0, sp, PT_R12
45+ PTR_S t1, sp, PT_R13
46+ PTR_S t2, sp, PT_R14
47+ PTR_S t3, sp, PT_R15
48+ PTR_S t4, sp, PT_R16
49+ PTR_S t5, sp, PT_R17
50+ PTR_S t6, sp, PT_R18
51+ PTR_S t7, sp, PT_R19
52+ PTR_S t8, sp, PT_R20
53+ PTR_S u0, sp, PT_R21
54+ PTR_S s0, sp, PT_R23
55+ PTR_S s1, sp, PT_R24
56+ PTR_S s2, sp, PT_R25
57+ PTR_S s3, sp, PT_R26
58+ PTR_S s4, sp, PT_R27
59+ PTR_S s5, sp, PT_R28
60+ PTR_S s6, sp, PT_R29
61+ PTR_S s7, sp, PT_R30
62+ PTR_S s8, sp, PT_R31
63+ /* Clear it for later use as a flag sometimes. */
64+ PTR_S zero, sp, PT_R0
65+ .endif
66+ PTR_S ra, sp, PT_ERA /* Save trace function ra at PT_ERA */
67+ PTR_ADDI t8, sp, PT_SIZE
68+ PTR_S t8, sp, PT_R3
69+ .endm
70+
71+SYM_FUNC_START(ftrace_stub)
72+ jr ra
73+SYM_FUNC_END(ftrace_stub)
74+
75+SYM_CODE_START(ftrace_common)
76+ PTR_ADDI a0, ra, -8 /* arg0: ip */
77+ move a1, t0 /* arg1: parent_ip */
78+ la.pcrel t1, function_trace_op
79+ PTR_L a2, t1, 0 /* arg2: op */
80+ move a3, sp /* arg3: regs */
81+
82+SYM_INNER_LABEL(ftrace_call, SYM_L_GLOBAL)
83+ bl ftrace_stub
84+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
85+SYM_INNER_LABEL(ftrace_graph_call, SYM_L_GLOBAL)
86+ nop /* b ftrace_graph_caller */
87+#endif
88+
89+/*
90+ * As we didn't use S series regs in this assmembly code and all calls
91+ * are C function which will save S series regs by themselves, there is
92+ * no need to restore S series regs. The T series is available and safe
93+ * at the callsite, so there is no need to restore the T series regs.
94+ */
95+ftrace_common_return:
96+ PTR_L ra, sp, PT_R1
97+ PTR_L a0, sp, PT_R4
98+ PTR_L a1, sp, PT_R5
99+ PTR_L a2, sp, PT_R6
100+ PTR_L a3, sp, PT_R7
101+ PTR_L a4, sp, PT_R8
102+ PTR_L a5, sp, PT_R9
103+ PTR_L a6, sp, PT_R10
104+ PTR_L a7, sp, PT_R11
105+ PTR_L fp, sp, PT_R22
106+ PTR_L t0, sp, PT_ERA
107+ PTR_ADDI sp, sp, PT_SIZE
108+ jr t0
109+SYM_CODE_END(ftrace_common)
110+
111+SYM_CODE_START(ftrace_caller)
112+ ftrace_regs_entry allregs=0
113+ b ftrace_common
114+SYM_CODE_END(ftrace_caller)
115+
116+#ifdef CONFIG_DYNAMIC_FTRACE_WITH_REGS
117+SYM_CODE_START(ftrace_regs_caller)
118+ ftrace_regs_entry allregs=1
119+ b ftrace_common
120+SYM_CODE_END(ftrace_regs_caller)
121+#endif
122+
123+#ifdef CONFIG_FUNCTION_GRAPH_TRACER
124+SYM_CODE_START(ftrace_graph_caller)
125+ PTR_L a0, sp, PT_ERA
126+ PTR_ADDI a0, a0, -8 /* arg0: self_addr */
127+ PTR_ADDI a1, sp, PT_R1 /* arg1: parent */
128+ bl prepare_ftrace_return
129+ b ftrace_common_return
130+SYM_CODE_END(ftrace_graph_caller)
131+
132+SYM_CODE_START(return_to_handler)
133+ /* Save return value regs */
134+ PTR_ADDI sp, sp, -2 * SZREG
135+ PTR_S a0, sp, 0
136+ PTR_S a1, sp, SZREG
137+
138+ move a0, zero
139+ bl ftrace_return_to_handler
140+ move ra, a0
141+
142+ /* Restore return value regs */
143+ PTR_L a0, sp, 0
144+ PTR_L a1, sp, SZREG
145+ PTR_ADDI sp, sp, 2 * SZREG
146+
147+ jr ra
148+SYM_CODE_END(return_to_handler)
149+#endif
arch/loongarch/kernel/module-sections.c+39−25
@@ -6,18 +6,19 @@ · 5 unmodified lines
66 #include <linux/elf.h>
77 #include <linux/kernel.h>
88 #include <linux/module.h>
9+#include <linux/ftrace.h>
910
10−Elf_Addr module_emit_got_entry(struct module *mod, Elf_Addr val)
11+Elf_Addr module_emit_got_entry(struct module *mod, Elf_Shdr *sechdrs, Elf_Addr val)
1112 {
1213 struct mod_section *got_sec = &mod->arch.got;
1314 int i = got_sec->num_entries;
14− struct got_entry *got = get_got_entry(val, got_sec);
15+ struct got_entry *got = get_got_entry(val, sechdrs, got_sec);
1516
1617 if (got)
1718 return (Elf_Addr)got;
1819
1920 /* There is no GOT entry for val yet, create a new one. */
20− got = (struct got_entry *)got_sec->shdr->sh_addr;
21+ got = (struct got_entry *)sechdrs[got_sec->shndx].sh_addr;
2122 got[i] = emit_got_entry(val);
2223
2324 got_sec->num_entries++;
@@ -33,12 +34,12 @@ Elf_Addr module_emit_got_entry(struct module *mod, Elf_Addr val) · 9 unmodified lines
3334 return (Elf_Addr)&got[i];
3435 }
3536
36−Elf_Addr module_emit_plt_entry(struct module *mod, Elf_Addr val)
37+Elf_Addr module_emit_plt_entry(struct module *mod, Elf_Shdr *sechdrs, Elf_Addr val)
3738 {
3839 int nr;
3940 struct mod_section *plt_sec = &mod->arch.plt;
4041 struct mod_section *plt_idx_sec = &mod->arch.plt_idx;
41− struct plt_entry *plt = get_plt_entry(val, plt_sec, plt_idx_sec);
42+ struct plt_entry *plt = get_plt_entry(val, sechdrs, plt_sec, plt_idx_sec);
4243 struct plt_idx_entry *plt_idx;
4344
4445 if (plt)
@@ -47,9 +48,9 @@ Elf_Addr module_emit_plt_entry(struct module *mod, Elf_Addr val) · 2 unmodified lines
4748 nr = plt_sec->num_entries;
4849
4950 /* There is no duplicate entry, create a new one */
50− plt = (struct plt_entry *)plt_sec->shdr->sh_addr;
51+ plt = (struct plt_entry *)sechdrs[plt_sec->shndx].sh_addr;
5152 plt[nr] = emit_plt_entry(val);
52− plt_idx = (struct plt_idx_entry *)plt_idx_sec->shdr->sh_addr;
53+ plt_idx = (struct plt_idx_entry *)sechdrs[plt_idx_sec->shndx].sh_addr;
5354 plt_idx[nr] = emit_plt_idx_entry(val);
5455
5556 plt_sec->num_entries++;
@@ -103,28 +104,31 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, · 47 unmodified lines
103104 char *secstrings, struct module *mod)
104105 {
105106 unsigned int i, num_plts = 0, num_gots = 0;
107+ Elf_Shdr *got_sec, *plt_sec, *plt_idx_sec, *tramp = NULL;
106108
107109 /*
108110 * Find the empty .plt sections.
109111 */
110112 for (i = 0; i < ehdr->e_shnum; i++) {
111113 if (!strcmp(secstrings + sechdrs[i].sh_name, ".got"))
112− mod->arch.got.shdr = sechdrs + i;
114+ mod->arch.got.shndx = i;
113115 else if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt"))
114− mod->arch.plt.shdr = sechdrs + i;
116+ mod->arch.plt.shndx = i;
115117 else if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt.idx"))
116− mod->arch.plt_idx.shdr = sechdrs + i;
118+ mod->arch.plt_idx.shndx = i;
119+ else if (!strcmp(secstrings + sechdrs[i].sh_name, ".ftrace_trampoline"))
120+ tramp = sechdrs + i;
117121 }
118122
119− if (!mod->arch.got.shdr) {
123+ if (!mod->arch.got.shndx) {
120124 pr_err("%s: module GOT section(s) missing\n", mod->name);
121125 return -ENOEXEC;
122126 }
123− if (!mod->arch.plt.shdr) {
127+ if (!mod->arch.plt.shndx) {
124128 pr_err("%s: module PLT section(s) missing\n", mod->name);
125129 return -ENOEXEC;
126130 }
127− if (!mod->arch.plt_idx.shdr) {
131+ if (!mod->arch.plt_idx.shndx) {
128132 pr_err("%s: module PLT.IDX section(s) missing\n", mod->name);
129133 return -ENOEXEC;
130134 }
@@ -145,26 +149,36 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, · 14 unmodified lines
145149 count_max_entries(relas, num_rela, &num_plts, &num_gots);
146150 }
147151
148− mod->arch.got.shdr->sh_type = SHT_NOBITS;
149− mod->arch.got.shdr->sh_flags = SHF_ALLOC;
150− mod->arch.got.shdr->sh_addralign = L1_CACHE_BYTES;
151− mod->arch.got.shdr->sh_size = (num_gots + 1) * sizeof(struct got_entry);
152+ got_sec = sechdrs + mod->arch.got.shndx;
153+ got_sec->sh_type = SHT_NOBITS;
154+ got_sec->sh_flags = SHF_ALLOC;
155+ got_sec->sh_addralign = L1_CACHE_BYTES;
156+ got_sec->sh_size = (num_gots + 1) * sizeof(struct got_entry);
152157 mod->arch.got.num_entries = 0;
153158 mod->arch.got.max_entries = num_gots;
154159
155− mod->arch.plt.shdr->sh_type = SHT_NOBITS;
156− mod->arch.plt.shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
157− mod->arch.plt.shdr->sh_addralign = L1_CACHE_BYTES;
158− mod->arch.plt.shdr->sh_size = (num_plts + 1) * sizeof(struct plt_entry);
160+ plt_sec = sechdrs + mod->arch.plt.shndx;
161+ plt_sec->sh_type = SHT_NOBITS;
162+ plt_sec->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
163+ plt_sec->sh_addralign = L1_CACHE_BYTES;
164+ plt_sec->sh_size = (num_plts + 1) * sizeof(struct plt_entry);
159165 mod->arch.plt.num_entries = 0;
160166 mod->arch.plt.max_entries = num_plts;
161167
162− mod->arch.plt_idx.shdr->sh_type = SHT_NOBITS;
163− mod->arch.plt_idx.shdr->sh_flags = SHF_ALLOC;
164− mod->arch.plt_idx.shdr->sh_addralign = L1_CACHE_BYTES;
165− mod->arch.plt_idx.shdr->sh_size = (num_plts + 1) * sizeof(struct plt_idx_entry);
168+ plt_idx_sec = sechdrs + mod->arch.plt_idx.shndx;
169+ plt_idx_sec->sh_type = SHT_NOBITS;
170+ plt_idx_sec->sh_flags = SHF_ALLOC;
171+ plt_idx_sec->sh_addralign = L1_CACHE_BYTES;
172+ plt_idx_sec->sh_size = (num_plts + 1) * sizeof(struct plt_idx_entry);
166173 mod->arch.plt_idx.num_entries = 0;
167174 mod->arch.plt_idx.max_entries = num_plts;
168175
176+ if (tramp) {
177+ tramp->sh_type = SHT_NOBITS;
178+ tramp->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
179+ tramp->sh_addralign = __alignof__(struct plt_entry);
180+ tramp->sh_size = NR_FTRACE_PLTS * sizeof(struct plt_entry);
181+ }
182+
169183 return 0;
170184 }
arch/loongarch/kernel/module.c+63−12
@@ -15,8 +15,11 @@ · 14 unmodified lines
1515 #include <linux/vmalloc.h>
1616 #include <linux/slab.h>
1717 #include <linux/fs.h>
18+#include <linux/ftrace.h>
1819 #include <linux/string.h>
1920 #include <linux/kernel.h>
21+#include <asm/alternative.h>
22+#include <asm/inst.h>
2023
2124 static int rela_stack_push(s64 stack_value, s64 *rela_stack, size_t *rela_stack_top)
2225 {
@@ -98,16 +101,17 @@ static int apply_r_larch_sop_push_dup(struct module *mod, u32 *location, Elf_Add · 75 unmodified lines
98101 return 0;
99102 }
100103
101−static int apply_r_larch_sop_push_plt_pcrel(struct module *mod, u32 *location, Elf_Addr v,
104+static int apply_r_larch_sop_push_plt_pcrel(struct module *mod,
105+ Elf_Shdr *sechdrs, u32 *location, Elf_Addr v,
102106 s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
103107 {
104108 ptrdiff_t offset = (void *)v - (void *)location;
105109
106110 if (offset >= SZ_128M)
107− v = module_emit_plt_entry(mod, v);
111+ v = module_emit_plt_entry(mod, sechdrs, v);
108112
109113 if (offset < -SZ_128M)
110− v = module_emit_plt_entry(mod, v);
114+ v = module_emit_plt_entry(mod, sechdrs, v);
111115
112116 return apply_r_larch_sop_push_pcrel(mod, location, v, rela_stack, rela_stack_top, type);
113117 }
@@ -271,17 +275,18 @@ static int apply_r_larch_add_sub(struct module *mod, u32 *location, Elf_Addr v, · 157 unmodified lines
271275 }
272276 }
273277
274−static int apply_r_larch_b26(struct module *mod, u32 *location, Elf_Addr v,
278+static int apply_r_larch_b26(struct module *mod,
279+ Elf_Shdr *sechdrs, u32 *location, Elf_Addr v,
275280 s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
276281 {
277282 ptrdiff_t offset = (void *)v - (void *)location;
278283 union loongarch_instruction *insn = (union loongarch_instruction *)location;
279284
280285 if (offset >= SZ_128M)
281− v = module_emit_plt_entry(mod, v);
286+ v = module_emit_plt_entry(mod, sechdrs, v);
282287
283288 if (offset < -SZ_128M)
284− v = module_emit_plt_entry(mod, v);
289+ v = module_emit_plt_entry(mod, sechdrs, v);
285290
286291 offset = (void *)v - (void *)location;
287292
@@ -338,10 +343,11 @@ static int apply_r_larch_pcala(struct module *mod, u32 *location, Elf_Addr v, · 50 unmodified lines
338343 return 0;
339344 }
340345
341−static int apply_r_larch_got_pc(struct module *mod, u32 *location, Elf_Addr v,
346+static int apply_r_larch_got_pc(struct module *mod,
347+ Elf_Shdr *sechdrs, u32 *location, Elf_Addr v,
342348 s64 *rela_stack, size_t *rela_stack_top, unsigned int type)
343349 {
344− Elf_Addr got = module_emit_got_entry(mod, v);
350+ Elf_Addr got = module_emit_got_entry(mod, sechdrs, v);
345351
346352 if (!got)
347353 return -EINVAL;
@@ -386,13 +392,10 @@ static reloc_rela_handler reloc_rela_handlers[] = { · 38 unmodified lines
386392 [R_LARCH_SOP_PUSH_PCREL] = apply_r_larch_sop_push_pcrel,
387393 [R_LARCH_SOP_PUSH_ABSOLUTE] = apply_r_larch_sop_push_absolute,
388394 [R_LARCH_SOP_PUSH_DUP] = apply_r_larch_sop_push_dup,
389− [R_LARCH_SOP_PUSH_PLT_PCREL] = apply_r_larch_sop_push_plt_pcrel,
390395 [R_LARCH_SOP_SUB ... R_LARCH_SOP_IF_ELSE] = apply_r_larch_sop,
391396 [R_LARCH_SOP_POP_32_S_10_5 ... R_LARCH_SOP_POP_32_U] = apply_r_larch_sop_imm_field,
392397 [R_LARCH_ADD32 ... R_LARCH_SUB64] = apply_r_larch_add_sub,
393− [R_LARCH_B26] = apply_r_larch_b26,
394398 [R_LARCH_PCALA_HI20...R_LARCH_PCALA64_HI12] = apply_r_larch_pcala,
395− [R_LARCH_GOT_PC_HI20...R_LARCH_GOT_PC_LO12] = apply_r_larch_got_pc,
396399 };
397400
398401 int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
@@ -443,7 +446,22 @@ int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab, · 44 unmodified lines
443446 sym->st_value, rel[i].r_addend, (u64)location);
444447
445448 v = sym->st_value + rel[i].r_addend;
446− err = handler(mod, location, v, rela_stack, &rela_stack_top, type);
449+ switch (type) {
450+ case R_LARCH_B26:
451+ err = apply_r_larch_b26(mod, sechdrs, location,
452+ v, rela_stack, &rela_stack_top, type);
453+ break;
454+ case R_LARCH_GOT_PC_HI20...R_LARCH_GOT_PC_LO12:
455+ err = apply_r_larch_got_pc(mod, sechdrs, location,
456+ v, rela_stack, &rela_stack_top, type);
457+ break;
458+ case R_LARCH_SOP_PUSH_PLT_PCREL:
459+ err = apply_r_larch_sop_push_plt_pcrel(mod, sechdrs, location,
460+ v, rela_stack, &rela_stack_top, type);
461+ break;
462+ default:
463+ err = handler(mod, location, v, rela_stack, &rela_stack_top, type);
464+ }
447465 if (err)
448466 return err;
449467 }
@@ -456,3 +474,36 @@ void *module_alloc(unsigned long size) · 6 unmodified lines
456474 return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END,
457475 GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, __builtin_return_address(0));
458476 }
477+
478+static void module_init_ftrace_plt(const Elf_Ehdr *hdr,
479+ const Elf_Shdr *sechdrs, struct module *mod)
480+{
481+#ifdef CONFIG_DYNAMIC_FTRACE
482+ struct plt_entry *ftrace_plts;
483+
484+ ftrace_plts = (void *)sechdrs->sh_addr;
485+
486+ ftrace_plts[FTRACE_PLT_IDX] = emit_plt_entry(FTRACE_ADDR);
487+
488+ if (IS_ENABLED(CONFIG_DYNAMIC_FTRACE_WITH_REGS))
489+ ftrace_plts[FTRACE_REGS_PLT_IDX] = emit_plt_entry(FTRACE_REGS_ADDR);
490+
491+ mod->arch.ftrace_trampolines = ftrace_plts;
492+#endif
493+}
494+
495+int module_finalize(const Elf_Ehdr *hdr,
496+ const Elf_Shdr *sechdrs, struct module *mod)
497+{
498+ const Elf_Shdr *s, *se;
499+ const char *secstrs = (void *)hdr + sechdrs[hdr->e_shstrndx].sh_offset;
500+
501+ for (s = sechdrs, se = sechdrs + hdr->e_shnum; s < se; s++) {
502+ if (!strcmp(".altinstructions", secstrs + s->sh_name))
503+ apply_alternatives((void *)s->sh_addr, (void *)s->sh_addr + s->sh_size);
504+ if (!strcmp(".ftrace_trampoline", secstrs + s->sh_name))
505+ module_init_ftrace_plt(hdr, s, mod);
506+ }
507+
508+ return 0;
509+}
arch/loongarch/kernel/numa.c+16−1
@@ -388,6 +388,21 @@ static void __init numa_default_distance(void) · 387 unmodified lines
388388 }
389389 }
390390
391+/*
392+ * fake_numa_init() - For Non-ACPI systems
393+ * Return: 0 on success, -errno on failure.
394+ */
395+static int __init fake_numa_init(void)
396+{
397+ phys_addr_t start = memblock_start_of_DRAM();
398+ phys_addr_t end = memblock_end_of_DRAM() - 1;
399+
400+ node_set(0, numa_nodes_parsed);
401+ pr_info("Faking a node at [mem %pap-%pap]\n", &start, &end);
402+
403+ return numa_add_memblk(0, start, end + 1);
404+}
405+
391406 int __init init_numa_memory(void)
392407 {
393408 int i;
@@ -404,7 +419,7 @@ int __init init_numa_memory(void) · 10 unmodified lines
404419 memset(&numa_meminfo, 0, sizeof(numa_meminfo));
405420
406421 /* Parse SRAT and SLIT if provided by firmware. */
407− ret = acpi_numa_init();
422+ ret = acpi_disabled ? fake_numa_init() : acpi_numa_init();
408423 if (ret < 0)
409424 return ret;
410425
arch/loongarch/kernel/process.c+6−0
@@ -47,6 +47,12 @@ · 46 unmodified lines
4747 #include <asm/unwind.h>
4848 #include <asm/vdso.h>
4949
50+#ifdef CONFIG_STACKPROTECTOR
51+#include <linux/stackprotector.h>
52+unsigned long __stack_chk_guard __read_mostly;
53+EXPORT_SYMBOL(__stack_chk_guard);
54+#endif
55+
5056 /*
5157 * Idle related variables and functions
5258 */
arch/loongarch/kernel/reset.c+5−0
@@ -15,6 +15,7 @@ · 14 unmodified lines
1515 #include <acpi/reboot.h>
1616 #include <asm/idle.h>
1717 #include <asm/loongarch.h>
18+#include <asm/loongson.h>
1819
1920 void (*pm_power_off)(void);
2021 EXPORT_SYMBOL(pm_power_off);
@@ -41,6 +42,10 @@ void machine_power_off(void) · 20 unmodified lines
4142 #ifdef CONFIG_SMP
4243 preempt_disable();
4344 smp_send_stop();
45+#endif
46+#ifdef CONFIG_PM
47+ if (!acpi_disabled)
48+ enable_pci_wakeup();
4449 #endif
4550 do_kernel_power_off();
4651 #ifdef CONFIG_EFI
arch/loongarch/kernel/setup.c+148−1
@@ -28,10 +28,16 @@ · 27 unmodified lines
2828 #include <linux/sizes.h>
2929 #include <linux/device.h>
3030 #include <linux/dma-map-ops.h>
31+#include <linux/libfdt.h>
32+#include <linux/of_fdt.h>
33+#include <linux/of_address.h>
34+#include <linux/suspend.h>
3135 #include <linux/swiotlb.h>
3236
3337 #include <asm/addrspace.h>
38+#include <asm/alternative.h>
3439 #include <asm/bootinfo.h>
40+#include <asm/bugs.h>
3541 #include <asm/cache.h>
3642 #include <asm/cpu.h>
3743 #include <asm/dma.h>
@@ -67,6 +73,7 @@ static const char dmi_empty_string[] = " "; · 29 unmodified lines
6773 *
6874 * These are initialized so they are in the .data section
6975 */
76+char init_command_line[COMMAND_LINE_SIZE] __initdata;
7077
7178 static int num_standard_resources;
7279 static struct resource *standard_resources;
@@ -80,6 +87,11 @@ const char *get_system_type(void) · 7 unmodified lines
8087 return "generic-loongson-machine";
8188 }
8289
90+void __init check_bugs(void)
91+{
92+ alternative_instructions();
93+}
94+
8395 static const char *dmi_string_parse(const struct dmi_header *dm, u8 s)
8496 {
8597 const u8 *bp = ((u8 *) dm) + dm->length;
@@ -246,6 +258,58 @@ static void __init arch_parse_crashkernel(void) · 160 unmodified lines
246258 #endif
247259 }
248260
261+static void __init fdt_setup(void)
262+{
263+#ifdef CONFIG_OF_EARLY_FLATTREE
264+ void *fdt_pointer;
265+
266+ /* ACPI-based systems do not require parsing fdt */
267+ if (acpi_os_get_root_pointer())
268+ return;
269+
270+ /* Look for a device tree configuration table entry */
271+ fdt_pointer = efi_fdt_pointer();
272+ if (!fdt_pointer || fdt_check_header(fdt_pointer))
273+ return;
274+
275+ early_init_dt_scan(fdt_pointer);
276+ early_init_fdt_reserve_self();
277+
278+ max_low_pfn = PFN_PHYS(memblock_end_of_DRAM());
279+#endif
280+}
281+
282+static void __init bootcmdline_init(char **cmdline_p)
283+{
284+ /*
285+ * If CONFIG_CMDLINE_FORCE is enabled then initializing the command line
286+ * is trivial - we simply use the built-in command line unconditionally &
287+ * unmodified.
288+ */
289+ if (IS_ENABLED(CONFIG_CMDLINE_FORCE)) {
290+ strscpy(boot_command_line, CONFIG_CMDLINE, COMMAND_LINE_SIZE);
291+ goto out;
292+ }
293+
294+#ifdef CONFIG_OF_FLATTREE
295+ /*
296+ * If CONFIG_CMDLINE_BOOTLOADER is enabled and we are in FDT-based system,
297+ * the boot_command_line will be overwritten by early_init_dt_scan_chosen().
298+ * So we need to append init_command_line (the original copy of boot_command_line)
299+ * to boot_command_line.
300+ */
301+ if (initial_boot_params) {
302+ if (boot_command_line[0])
303+ strlcat(boot_command_line, " ", COMMAND_LINE_SIZE);
304+
305+ strlcat(boot_command_line, init_command_line, COMMAND_LINE_SIZE);
306+ }
307+#endif
308+
309+out:
310+ *cmdline_p = boot_command_line;
311+}
312+
249313 void __init platform_init(void)
250314 {
251315 arch_reserve_vmcore();
@@ -258,6 +322,7 @@ void __init platform_init(void) · 6 unmodified lines
258322 acpi_gbl_use_default_register_widths = false;
259323 acpi_boot_table_init();
260324 #endif
325+ unflatten_and_copy_device_tree();
261326
262327 #ifdef CONFIG_NUMA
263328 init_numa_memory();
@@ -290,6 +355,8 @@ static void __init arch_mem_init(char **cmdline_p) · 26 unmodified lines
290355
291356 check_kernel_sections_mem();
292357
358+ early_init_fdt_scan_reserved_mem();
359+
293360 /*
294361 * In order to reduce the possibility of kernel panic when failed to
295362 * get IO TLB memory under CONFIG_SWIOTLB, it is better to allocate
@@ -304,6 +371,10 @@ static void __init arch_mem_init(char **cmdline_p) · 8 unmodified lines
304371
305372 dma_contiguous_reserve(PFN_PHYS(max_low_pfn));
306373
374+ /* Reserve for hibernation. */
375+ register_nosave_region(PFN_DOWN(__pa_symbol(&__nosave_begin)),
376+ PFN_UP(__pa_symbol(&__nosave_end)));
377+
307378 memblock_dump_all();
308379
309380 early_memtest(PFN_PHYS(ARCH_PFN_OFFSET), PFN_PHYS(max_low_pfn));
@@ -363,6 +434,81 @@ static void __init resource_init(void) · 53 unmodified lines
363434 #endif
364435 }
365436
437+static int __init add_legacy_isa_io(struct fwnode_handle *fwnode,
438+ resource_size_t hw_start, resource_size_t size)
439+{
440+ int ret = 0;
441+ unsigned long vaddr;
442+ struct logic_pio_hwaddr *range;
443+
444+ range = kzalloc(sizeof(*range), GFP_ATOMIC);
445+ if (!range)
446+ return -ENOMEM;
447+
448+ range->fwnode = fwnode;
449+ range->size = size = round_up(size, PAGE_SIZE);
450+ range->hw_start = hw_start;
451+ range->flags = LOGIC_PIO_CPU_MMIO;
452+
453+ ret = logic_pio_register_range(range);
454+ if (ret) {
455+ kfree(range);
456+ return ret;
457+ }
458+
459+ /* Legacy ISA must placed at the start of PCI_IOBASE */
460+ if (range->io_start != 0) {
461+ logic_pio_unregister_range(range);
462+ kfree(range);
463+ return -EINVAL;
464+ }
465+
466+ vaddr = (unsigned long)(PCI_IOBASE + range->io_start);
467+ ioremap_page_range(vaddr, vaddr + size, hw_start, pgprot_device(PAGE_KERNEL));
468+
469+ return 0;
470+}
471+
472+static __init int arch_reserve_pio_range(void)
473+{
474+ struct device_node *np;
475+
476+ for_each_node_by_name(np, "isa") {
477+ struct of_range range;
478+ struct of_range_parser parser;
479+
480+ pr_info("ISA Bridge: %pOF\n", np);
481+
482+ if (of_range_parser_init(&parser, np)) {
483+ pr_info("Failed to parse resources.\n");
484+ of_node_put(np);
485+ break;
486+ }
487+
488+ for_each_of_range(&parser, &range) {
489+ switch (range.flags & IORESOURCE_TYPE_BITS) {
490+ case IORESOURCE_IO:
491+ pr_info(" IO 0x%016llx..0x%016llx -> 0x%016llx\n",
492+ range.cpu_addr,
493+ range.cpu_addr + range.size - 1,
494+ range.bus_addr);
495+ if (add_legacy_isa_io(&np->fwnode, range.cpu_addr, range.size))
496+ pr_warn("Failed to reserve legacy IO in Logic PIO\n");
497+ break;
498+ case IORESOURCE_MEM:
499+ pr_info(" MEM 0x%016llx..0x%016llx -> 0x%016llx\n",
500+ range.cpu_addr,
501+ range.cpu_addr + range.size - 1,
502+ range.bus_addr);
503+ break;
504+ }
505+ }
506+ }
507+
508+ return 0;
509+}
510+arch_initcall(arch_reserve_pio_range);
511+
366512 static int __init reserve_memblock_reserved_regions(void)
367513 {
368514 u64 i, j;
@@ -415,12 +561,13 @@ static void __init prefill_possible_map(void) · 46 unmodified lines
415561 void __init setup_arch(char **cmdline_p)
416562 {
417563 cpu_probe();
418− *cmdline_p = boot_command_line;
419564
420565 init_environ();
421566 efi_init();
567+ fdt_setup();
422568 memblock_init();
423569 pagetable_init();
570+ bootcmdline_init(cmdline_p);
424571 parse_early_param();
425572 reserve_initrd_mem();
426573
arch/loongarch/kernel/smp.c+35−0
@@ -16,6 +16,7 @@ · 15 unmodified lines
1616 #include <linux/smp.h>
1717 #include <linux/threads.h>
1818 #include <linux/export.h>
19+#include <linux/syscore_ops.h>
1920 #include <linux/time.h>
2021 #include <linux/tracepoint.h>
2122 #include <linux/sched/hotplug.h>
@@ -180,8 +181,42 @@ irqreturn_t loongson_ipi_interrupt(int irq, void *dev) · 158 unmodified lines
180181 return IRQ_HANDLED;
181182 }
182183
184+static void __init fdt_smp_setup(void)
185+{
186+#ifdef CONFIG_OF
187+ unsigned int cpu, cpuid;
188+ struct device_node *node = NULL;
189+
190+ for_each_of_cpu_node(node) {
191+ if (!of_device_is_available(node))
192+ continue;
193+
194+ cpuid = of_get_cpu_hwid(node, 0);
195+ if (cpuid >= nr_cpu_ids)
196+ continue;
197+
198+ if (cpuid == loongson_sysconf.boot_cpu_id) {
199+ cpu = 0;
200+ numa_add_cpu(cpu);
201+ } else {
202+ cpu = cpumask_next_zero(-1, cpu_present_mask);
203+ }
204+
205+ num_processors++;
206+ set_cpu_possible(cpu, true);
207+ set_cpu_present(cpu, true);
208+ __cpu_number_map[cpuid] = cpu;
209+ __cpu_logical_map[cpu] = cpuid;
210+ }
211+
212+ loongson_sysconf.nr_cpus = num_processors;
213+#endif
214+}
215+
183216 void __init loongson_smp_setup(void)
184217 {
218+ fdt_smp_setup();
219+
185220 cpu_data[0].core = cpu_logical_map(0) % loongson_sysconf.cores_per_package;
186221 cpu_data[0].package = cpu_logical_map(0) / loongson_sysconf.cores_per_package;
187222
arch/loongarch/kernel/switch.S+5−0
@@ -23,6 +23,11 @@ SYM_FUNC_START(__switch_to) · 22 unmodified lines
2323 stptr.d ra, a0, THREAD_REG01
2424 stptr.d a3, a0, THREAD_SCHED_RA
2525 stptr.d a4, a0, THREAD_SCHED_CFA
26+#if defined(CONFIG_STACKPROTECTOR) && !defined(CONFIG_SMP)
27+ la t7, __stack_chk_guard
28+ LONG_L t8, a1, TASK_STACK_CANARY
29+ LONG_S t8, t7, 0
30+#endif
2631 move tp, a2
2732 cpu_restore_nonscratch a1
2833
arch/loongarch/kernel/time.c+8−3
@@ -115,12 +115,17 @@ static unsigned long __init get_loops_per_jiffy(void) · 114 unmodified lines
115115 return lpj;
116116 }
117117
118−static long init_timeval;
118+static long init_offset __nosavedata;
119+
120+void save_counter(void)
121+{
122+ init_offset = drdtime();
123+}
119124
120125 void sync_counter(void)
121126 {
122127 /* Ensure counter begin at 0 */
123− csr_write64(-init_timeval, LOONGARCH_CSR_CNTC);
128+ csr_write64(init_offset, LOONGARCH_CSR_CNTC);
124129 }
125130
126131 static int get_timer_irq(void)
@@ -219,7 +224,7 @@ void __init time_init(void) · 92 unmodified lines
219224 else
220225 const_clock_freq = calc_const_freq();
221226
222− init_timeval = drdtime() - csr_read64(LOONGARCH_CSR_CNTC);
227+ init_offset = -(drdtime() - csr_read64(LOONGARCH_CSR_CNTC));
223228
224229 constant_clockevent_init();
225230 constant_clocksource_init();
arch/loongarch/kernel/traps.c+27−0
@@ -368,13 +368,40 @@ asmlinkage void noinstr do_ade(struct pt_regs *regs) · 367 unmodified lines
368368 irqentry_exit(regs, state);
369369 }
370370
371+/* sysctl hooks */
372+int unaligned_enabled __read_mostly = 1; /* Enabled by default */
373+int no_unaligned_warning __read_mostly = 1; /* Only 1 warning by default */
374+
371375 asmlinkage void noinstr do_ale(struct pt_regs *regs)
372376 {
377+ unsigned int *pc;
373378 irqentry_state_t state = irqentry_enter(regs);
374379
380+ perf_sw_event(PERF_COUNT_SW_ALIGNMENT_FAULTS, 1, regs, regs->csr_badvaddr);
381+
382+ /*
383+ * Did we catch a fault trying to load an instruction?
384+ */
385+ if (regs->csr_badvaddr == regs->csr_era)
386+ goto sigbus;
387+ if (user_mode(regs) && !test_thread_flag(TIF_FIXADE))
388+ goto sigbus;
389+ if (!unaligned_enabled)
390+ goto sigbus;
391+ if (!no_unaligned_warning)
392+ show_registers(regs);
393+
394+ pc = (unsigned int *)exception_era(regs);
395+
396+ emulate_load_store_insn(regs, (void __user *)regs->csr_badvaddr, pc);
397+
398+ goto out;
399+
400+sigbus:
375401 die_if_kernel("Kernel ale access", regs);
376402 force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)regs->csr_badvaddr);
377403
404+out:
378405 irqentry_exit(regs, state);
379406 }
380407
arch/loongarch/kernel/unaligned.c+499−0
@@ -0,0 +1,499 @@
1+// SPDX-License-Identifier: GPL-2.0
2+/*
3+ * Handle unaligned accesses by emulation.
4+ *
5+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
6+ *
7+ * Derived from MIPS:
8+ * Copyright (C) 1996, 1998, 1999, 2002 by Ralf Baechle
9+ * Copyright (C) 1999 Silicon Graphics, Inc.
10+ * Copyright (C) 2014 Imagination Technologies Ltd.
11+ */
12+#include <linux/mm.h>
13+#include <linux/sched.h>
14+#include <linux/signal.h>
15+#include <linux/debugfs.h>
16+#include <linux/perf_event.h>
17+
18+#include <asm/asm.h>
19+#include <asm/branch.h>
20+#include <asm/fpu.h>
21+#include <asm/inst.h>
22+
23+#include "access-helper.h"
24+
25+#ifdef CONFIG_DEBUG_FS
26+static u32 unaligned_instructions_user;
27+static u32 unaligned_instructions_kernel;
28+#endif
29+
30+static inline unsigned long read_fpr(unsigned int idx)
31+{
32+#define READ_FPR(idx, __value) \
33+ __asm__ __volatile__("movfr2gr.d %0, $f"#idx"\n\t" : "=r"(__value));
34+
35+ unsigned long __value;
36+
37+ switch (idx) {
38+ case 0:
39+ READ_FPR(0, __value);
40+ break;
41+ case 1:
42+ READ_FPR(1, __value);
43+ break;
44+ case 2:
45+ READ_FPR(2, __value);
46+ break;
47+ case 3:
48+ READ_FPR(3, __value);
49+ break;
50+ case 4:
51+ READ_FPR(4, __value);
52+ break;
53+ case 5:
54+ READ_FPR(5, __value);
55+ break;
56+ case 6:
57+ READ_FPR(6, __value);
58+ break;
59+ case 7:
60+ READ_FPR(7, __value);
61+ break;
62+ case 8:
63+ READ_FPR(8, __value);
64+ break;
65+ case 9:
66+ READ_FPR(9, __value);
67+ break;
68+ case 10:
69+ READ_FPR(10, __value);
70+ break;
71+ case 11:
72+ READ_FPR(11, __value);
73+ break;
74+ case 12:
75+ READ_FPR(12, __value);
76+ break;
77+ case 13:
78+ READ_FPR(13, __value);
79+ break;
80+ case 14:
81+ READ_FPR(14, __value);
82+ break;
83+ case 15:
84+ READ_FPR(15, __value);
85+ break;
86+ case 16:
87+ READ_FPR(16, __value);
88+ break;
89+ case 17:
90+ READ_FPR(17, __value);
91+ break;
92+ case 18:
93+ READ_FPR(18, __value);
94+ break;
95+ case 19:
96+ READ_FPR(19, __value);
97+ break;
98+ case 20:
99+ READ_FPR(20, __value);
100+ break;
101+ case 21:
102+ READ_FPR(21, __value);
103+ break;
104+ case 22:
105+ READ_FPR(22, __value);
106+ break;
107+ case 23:
108+ READ_FPR(23, __value);
109+ break;
110+ case 24:
111+ READ_FPR(24, __value);
112+ break;
113+ case 25:
114+ READ_FPR(25, __value);
115+ break;
116+ case 26:
117+ READ_FPR(26, __value);
118+ break;
119+ case 27:
120+ READ_FPR(27, __value);
121+ break;
122+ case 28:
123+ READ_FPR(28, __value);
124+ break;
125+ case 29:
126+ READ_FPR(29, __value);
127+ break;
128+ case 30:
129+ READ_FPR(30, __value);
130+ break;
131+ case 31:
132+ READ_FPR(31, __value);
133+ break;
134+ default:
135+ panic("unexpected idx '%d'", idx);
136+ }
137+#undef READ_FPR
138+ return __value;
139+}
140+
141+static inline void write_fpr(unsigned int idx, unsigned long value)
142+{
143+#define WRITE_FPR(idx, value) \
144+ __asm__ __volatile__("movgr2fr.d $f"#idx", %0\n\t" :: "r"(value));
145+
146+ switch (idx) {
147+ case 0:
148+ WRITE_FPR(0, value);
149+ break;
150+ case 1:
151+ WRITE_FPR(1, value);
152+ break;
153+ case 2:
154+ WRITE_FPR(2, value);
155+ break;
156+ case 3:
157+ WRITE_FPR(3, value);
158+ break;
159+ case 4:
160+ WRITE_FPR(4, value);
161+ break;
162+ case 5:
163+ WRITE_FPR(5, value);
164+ break;
165+ case 6:
166+ WRITE_FPR(6, value);
167+ break;
168+ case 7:
169+ WRITE_FPR(7, value);
170+ break;
171+ case 8:
172+ WRITE_FPR(8, value);
173+ break;
174+ case 9:
175+ WRITE_FPR(9, value);
176+ break;
177+ case 10:
178+ WRITE_FPR(10, value);
179+ break;
180+ case 11:
181+ WRITE_FPR(11, value);
182+ break;
183+ case 12:
184+ WRITE_FPR(12, value);
185+ break;
186+ case 13:
187+ WRITE_FPR(13, value);
188+ break;
189+ case 14:
190+ WRITE_FPR(14, value);
191+ break;
192+ case 15:
193+ WRITE_FPR(15, value);
194+ break;
195+ case 16:
196+ WRITE_FPR(16, value);
197+ break;
198+ case 17:
199+ WRITE_FPR(17, value);
200+ break;
201+ case 18:
202+ WRITE_FPR(18, value);
203+ break;
204+ case 19:
205+ WRITE_FPR(19, value);
206+ break;
207+ case 20:
208+ WRITE_FPR(20, value);
209+ break;
210+ case 21:
211+ WRITE_FPR(21, value);
212+ break;
213+ case 22:
214+ WRITE_FPR(22, value);
215+ break;
216+ case 23:
217+ WRITE_FPR(23, value);
218+ break;
219+ case 24:
220+ WRITE_FPR(24, value);
221+ break;
222+ case 25:
223+ WRITE_FPR(25, value);
224+ break;
225+ case 26:
226+ WRITE_FPR(26, value);
227+ break;
228+ case 27:
229+ WRITE_FPR(27, value);
230+ break;
231+ case 28:
232+ WRITE_FPR(28, value);
233+ break;
234+ case 29:
235+ WRITE_FPR(29, value);
236+ break;
237+ case 30:
238+ WRITE_FPR(30, value);
239+ break;
240+ case 31:
241+ WRITE_FPR(31, value);
242+ break;
243+ default:
244+ panic("unexpected idx '%d'", idx);
245+ }
246+#undef WRITE_FPR
247+}
248+
249+void emulate_load_store_insn(struct pt_regs *regs, void __user *addr, unsigned int *pc)
250+{
251+ bool fp = false;
252+ bool sign, write;
253+ bool user = user_mode(regs);
254+ unsigned int res, size = 0;
255+ unsigned long value = 0;
256+ union loongarch_instruction insn;
257+
258+ perf_sw_event(PERF_COUNT_SW_EMULATION_FAULTS, 1, regs, 0);
259+
260+ __get_inst(&insn.word, pc, user);
261+
262+ switch (insn.reg2i12_format.opcode) {
263+ case ldh_op:
264+ size = 2;
265+ sign = true;
266+ write = false;
267+ break;
268+ case ldhu_op:
269+ size = 2;
270+ sign = false;
271+ write = false;
272+ break;
273+ case sth_op:
274+ size = 2;
275+ sign = true;
276+ write = true;
277+ break;
278+ case ldw_op:
279+ size = 4;
280+ sign = true;
281+ write = false;
282+ break;
283+ case ldwu_op:
284+ size = 4;
285+ sign = false;
286+ write = false;
287+ break;
288+ case stw_op:
289+ size = 4;
290+ sign = true;
291+ write = true;
292+ break;
293+ case ldd_op:
294+ size = 8;
295+ sign = true;
296+ write = false;
297+ break;
298+ case std_op:
299+ size = 8;
300+ sign = true;
301+ write = true;
302+ break;
303+ case flds_op:
304+ size = 4;
305+ fp = true;
306+ sign = true;
307+ write = false;
308+ break;
309+ case fsts_op:
310+ size = 4;
311+ fp = true;
312+ sign = true;
313+ write = true;
314+ break;
315+ case fldd_op:
316+ size = 8;
317+ fp = true;
318+ sign = true;
319+ write = false;
320+ break;
321+ case fstd_op:
322+ size = 8;
323+ fp = true;
324+ sign = true;
325+ write = true;
326+ break;
327+ }
328+
329+ switch (insn.reg2i14_format.opcode) {
330+ case ldptrw_op:
331+ size = 4;
332+ sign = true;
333+ write = false;
334+ break;
335+ case stptrw_op:
336+ size = 4;
337+ sign = true;
338+ write = true;
339+ break;
340+ case ldptrd_op:
341+ size = 8;
342+ sign = true;
343+ write = false;
344+ break;
345+ case stptrd_op:
346+ size = 8;
347+ sign = true;
348+ write = true;
349+ break;
350+ }
351+
352+ switch (insn.reg3_format.opcode) {
353+ case ldxh_op:
354+ size = 2;
355+ sign = true;
356+ write = false;
357+ break;
358+ case ldxhu_op:
359+ size = 2;
360+ sign = false;
361+ write = false;
362+ break;
363+ case stxh_op:
364+ size = 2;
365+ sign = true;
366+ write = true;
367+ break;
368+ case ldxw_op:
369+ size = 4;
370+ sign = true;
371+ write = false;
372+ break;
373+ case ldxwu_op:
374+ size = 4;
375+ sign = false;
376+ write = false;
377+ break;
378+ case stxw_op:
379+ size = 4;
380+ sign = true;
381+ write = true;
382+ break;
383+ case ldxd_op:
384+ size = 8;
385+ sign = true;
386+ write = false;
387+ break;
388+ case stxd_op:
389+ size = 8;
390+ sign = true;
391+ write = true;
392+ break;
393+ case fldxs_op:
394+ size = 4;
395+ fp = true;
396+ sign = true;
397+ write = false;
398+ break;
399+ case fstxs_op:
400+ size = 4;
401+ fp = true;
402+ sign = true;
403+ write = true;
404+ break;
405+ case fldxd_op:
406+ size = 8;
407+ fp = true;
408+ sign = true;
409+ write = false;
410+ break;
411+ case fstxd_op:
412+ size = 8;
413+ fp = true;
414+ sign = true;
415+ write = true;
416+ break;
417+ }
418+
419+ if (!size)
420+ goto sigbus;
421+ if (user && !access_ok(addr, size))
422+ goto sigbus;
423+
424+ if (!write) {
425+ res = unaligned_read(addr, &value, size, sign);
426+ if (res)
427+ goto fault;
428+
429+ /* Rd is the same field in any formats */
430+ if (!fp)
431+ regs->regs[insn.reg3_format.rd] = value;
432+ else {
433+ if (is_fpu_owner())
434+ write_fpr(insn.reg3_format.rd, value);
435+ else
436+ set_fpr64(&current->thread.fpu.fpr[insn.reg3_format.rd], 0, value);
437+ }
438+ } else {
439+ /* Rd is the same field in any formats */
440+ if (!fp)
441+ value = regs->regs[insn.reg3_format.rd];
442+ else {
443+ if (is_fpu_owner())
444+ value = read_fpr(insn.reg3_format.rd);
445+ else
446+ value = get_fpr64(&current->thread.fpu.fpr[insn.reg3_format.rd], 0);
447+ }
448+
449+ res = unaligned_write(addr, value, size);
450+ if (res)
451+ goto fault;
452+ }
453+
454+#ifdef CONFIG_DEBUG_FS
455+ if (user)
456+ unaligned_instructions_user++;
457+ else
458+ unaligned_instructions_kernel++;
459+#endif
460+
461+ compute_return_era(regs);
462+
463+ return;
464+
465+fault:
466+ /* Did we have an exception handler installed? */
467+ if (fixup_exception(regs))
468+ return;
469+
470+ die_if_kernel("Unhandled kernel unaligned access", regs);
471+ force_sig(SIGSEGV);
472+
473+ return;
474+
475+sigbus:
476+ die_if_kernel("Unhandled kernel unaligned access", regs);
477+ force_sig(SIGBUS);
478+
479+ return;
480+}
481+
482+#ifdef CONFIG_DEBUG_FS
483+static int __init debugfs_unaligned(void)
484+{
485+ struct dentry *d;
486+
487+ d = debugfs_create_dir("loongarch", NULL);
488+ if (!d)
489+ return -ENOMEM;
490+
491+ debugfs_create_u32("unaligned_instructions_user",
492+ S_IRUGO, d, &unaligned_instructions_user);
493+ debugfs_create_u32("unaligned_instructions_kernel",
494+ S_IRUGO, d, &unaligned_instructions_kernel);
495+
496+ return 0;
497+}
498+arch_initcall(debugfs_unaligned);
499+#endif
arch/loongarch/kernel/unwind_guess.c+3−1
@@ -3,6 +3,7 @@ · 2 unmodified lines
33 * Copyright (C) 2022 Loongson Technology Corporation Limited
44 */
55 #include <linux/kernel.h>
6+#include <linux/ftrace.h>
67
78 #include <asm/unwind.h>
89
@@ -53,7 +54,8 @@ bool unwind_next_frame(struct unwind_state *state) · 44 unmodified lines
5354 state->sp < info->end;
5455 state->sp += sizeof(unsigned long)) {
5556 addr = *(unsigned long *)(state->sp);
56−
57+ state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
58+ addr, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
5759 if (__kernel_text_address(addr))
5860 return true;
5961 }
arch/loongarch/kernel/unwind_prologue.c+42−8
@@ -2,12 +2,23 @@ · 1 unmodified line
22 /*
33 * Copyright (C) 2022 Loongson Technology Corporation Limited
44 */
5+#include <linux/ftrace.h>
56 #include <linux/kallsyms.h>
67
78 #include <asm/inst.h>
89 #include <asm/ptrace.h>
910 #include <asm/unwind.h>
1011
12+static inline void unwind_state_fixup(struct unwind_state *state)
13+{
14+#ifdef CONFIG_DYNAMIC_FTRACE
15+ static unsigned long ftrace = (unsigned long)ftrace_call + 4;
16+
17+ if (state->pc == ftrace)
18+ state->is_ftrace = true;
19+#endif
20+}
21+
1122 unsigned long unwind_get_return_address(struct unwind_state *state)
1223 {
1324
@@ -32,6 +43,8 @@ static bool unwind_by_guess(struct unwind_state *state) · 18 unmodified lines
3243 state->sp < info->end;
3344 state->sp += sizeof(unsigned long)) {
3445 addr = *(unsigned long *)(state->sp);
46+ state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
47+ addr, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
3548 if (__kernel_text_address(addr))
3649 return true;
3750 }
@@ -41,15 +54,30 @@ static bool unwind_by_guess(struct unwind_state *state) · 3 unmodified lines
4154
4255 static bool unwind_by_prologue(struct unwind_state *state)
4356 {
44− struct stack_info *info = &state->stack_info;
45− union loongarch_instruction *ip, *ip_end;
4657 long frame_ra = -1;
4758 unsigned long frame_size = 0;
4859 unsigned long size, offset, pc = state->pc;
60+ struct pt_regs *regs;
61+ struct stack_info *info = &state->stack_info;
62+ union loongarch_instruction *ip, *ip_end;
4963
5064 if (state->sp >= info->end || state->sp < info->begin)
5165 return false;
5266
67+ if (state->is_ftrace) {
68+ /*
69+ * As we meet ftrace_regs_entry, reset first flag like first doing
70+ * tracing. Prologue analysis will stop soon because PC is at entry.
71+ */
72+ regs = (struct pt_regs *)state->sp;
73+ state->first = true;
74+ state->is_ftrace = false;
75+ state->pc = regs->csr_era;
76+ state->ra = regs->regs[1];
77+ state->sp = regs->regs[3];
78+ return true;
79+ }
80+
5381 if (!kallsyms_lookup_size_offset(pc, &size, &offset))
5482 return false;
5583
@@ -95,7 +123,7 @@ static bool unwind_by_prologue(struct unwind_state *state) · 39 unmodified lines
95123
96124 state->pc = *(unsigned long *)(state->sp + frame_ra);
97125 state->sp = state->sp + frame_size;
98− return !!__kernel_text_address(state->pc);
126+ goto out;
99127
100128 first:
101129 state->first = false;
@@ -104,7 +132,9 @@ first: · 2 unmodified lines
104132
105133 state->pc = state->ra;
106134
107− return !!__kernel_text_address(state->ra);
135+out:
136+ unwind_state_fixup(state);
137+ return !!__kernel_text_address(state->pc);
108138 }
109139
110140 void unwind_start(struct unwind_state *state, struct task_struct *task,
@@ -147,8 +177,11 @@ bool unwind_next_frame(struct unwind_state *state) · 36 unmodified lines
147177 break;
148178
149179 case UNWINDER_PROLOGUE:
150− if (unwind_by_prologue(state))
180+ if (unwind_by_prologue(state)) {
181+ state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
182+ state->pc, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
151183 return true;
184+ }
152185
153186 if (info->type == STACK_TYPE_IRQ &&
154187 info->end == state->sp) {
@@ -158,10 +191,11 @@ bool unwind_next_frame(struct unwind_state *state) · 3 unmodified lines
158191 if (user_mode(regs) || !__kernel_text_address(pc))
159192 return false;
160193
161− state->pc = pc;
162− state->sp = regs->regs[3];
163− state->ra = regs->regs[1];
164194 state->first = true;
195+ state->ra = regs->regs[1];
196+ state->sp = regs->regs[3];
197+ state->pc = ftrace_graph_ret_addr(state->task, &state->graph_idx,
198+ pc, (unsigned long *)(state->sp - GRAPH_FAKE_OFFSET));
165199 get_stack_info(state->sp, state->task, info);
166200
167201 return true;
arch/loongarch/kernel/vmlinux.lds.S+12−1
@@ -4,6 +4,7 @@ · 3 unmodified lines
44 #include <asm/thread_info.h>
55
66 #define PAGE_SIZE _PAGE_SIZE
7+#define RO_EXCEPTION_TABLE_ALIGN 4
78
89 /*
910 * Put .bss..swapper_pg_dir as the first thing in .bss. This will
@@ -53,7 +54,17 @@ SECTIONS · 43 unmodified lines
5354 . = ALIGN(PECOFF_SEGMENT_ALIGN);
5455 _etext = .;
5556
56− EXCEPTION_TABLE(16)
57+ /*
58+ * struct alt_inst entries. From the header (alternative.h):
59+ * "Alternative instructions for different CPU types or capabilities"
60+ * Think locking instructions on spinlocks.
61+ */
62+ . = ALIGN(4);
63+ .altinstructions : AT(ADDR(.altinstructions) - LOAD_OFFSET) {
64+ __alt_instructions = .;
65+ *(.altinstructions)
66+ __alt_instructions_end = .;
67+ }
5768
5869 .got : ALIGN(16) { *(.got) }
5970 .plt : ALIGN(16) { *(.plt) }
arch/loongarch/lib/Makefile+2−1
@@ -3,4 +3,5 @@ · 2 unmodified lines
33 # Makefile for LoongArch-specific library files.
44 #
55
6−lib-y += delay.o clear_user.o copy_user.o dump_tlb.o
6+lib-y += delay.o memset.o memcpy.o memmove.o \
7+ clear_user.o copy_user.o dump_tlb.o unaligned.o
arch/loongarch/lib/clear_user.S+70−15
@@ -3,30 +3,37 @@ · 2 unmodified lines
33 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
44 */
55
6+#include <asm/alternative-asm.h>
67 #include <asm/asm.h>
78 #include <asm/asmmacro.h>
9+#include <asm/asm-extable.h>
10+#include <asm/cpu.h>
811 #include <asm/export.h>
912 #include <asm/regdef.h>
1013
11−.macro fixup_ex from, to, offset, fix
12−.if \fix
13− .section .fixup, "ax"
14−\to: addi.d a0, a1, \offset
14+.irp to, 0, 1, 2, 3, 4, 5, 6, 7
15+.L_fixup_handle_\to\():
16+ addi.d a0, a1, (\to) * (-8)
1517 jr ra
16− .previous
17−.endif
18− .section __ex_table, "a"
19− PTR \from\()b, \to\()b
20− .previous
21−.endm
18+.endr
19+
20+SYM_FUNC_START(__clear_user)
21+ /*
22+ * Some CPUs support hardware unaligned access
23+ */
24+ ALTERNATIVE "b __clear_user_generic", \
25+ "b __clear_user_fast", CPU_FEATURE_UAL
26+SYM_FUNC_END(__clear_user)
27+
28+EXPORT_SYMBOL(__clear_user)
2229
2330 /*
24− * unsigned long __clear_user(void *addr, size_t size)
31+ * unsigned long __clear_user_generic(void *addr, size_t size)
2532 *
2633 * a0: addr
2734 * a1: size
2835 */
29−SYM_FUNC_START(__clear_user)
36+SYM_FUNC_START(__clear_user_generic)
3037 beqz a1, 2f
3138
3239 1: st.b zero, a0, 0
@@ -37,7 +44,55 @@ SYM_FUNC_START(__clear_user) · 4 unmodified lines
3744 2: move a0, a1
3845 jr ra
3946
40− fixup_ex 1, 3, 0, 1
41−SYM_FUNC_END(__clear_user)
47+ _asm_extable 1b, .L_fixup_handle_0
48+SYM_FUNC_END(__clear_user_generic)
4249
43−EXPORT_SYMBOL(__clear_user)
50+/*
51+ * unsigned long __clear_user_fast(void *addr, unsigned long size)
52+ *
53+ * a0: addr
54+ * a1: size
55+ */
56+SYM_FUNC_START(__clear_user_fast)
57+ beqz a1, 10f
58+
59+ ori a2, zero, 64
60+ blt a1, a2, 9f
61+
62+ /* set 64 bytes at a time */
63+1: st.d zero, a0, 0
64+2: st.d zero, a0, 8
65+3: st.d zero, a0, 16
66+4: st.d zero, a0, 24
67+5: st.d zero, a0, 32
68+6: st.d zero, a0, 40
69+7: st.d zero, a0, 48
70+8: st.d zero, a0, 56
71+
72+ addi.d a0, a0, 64
73+ addi.d a1, a1, -64
74+ bge a1, a2, 1b
75+
76+ beqz a1, 10f
77+
78+ /* set the remaining bytes */
79+9: st.b zero, a0, 0
80+ addi.d a0, a0, 1
81+ addi.d a1, a1, -1
82+ bgt a1, zero, 9b
83+
84+ /* return */
85+10: move a0, a1
86+ jr ra
87+
88+ /* fixup and ex_table */
89+ _asm_extable 1b, .L_fixup_handle_0
90+ _asm_extable 2b, .L_fixup_handle_1
91+ _asm_extable 3b, .L_fixup_handle_2
92+ _asm_extable 4b, .L_fixup_handle_3
93+ _asm_extable 5b, .L_fixup_handle_4
94+ _asm_extable 6b, .L_fixup_handle_5
95+ _asm_extable 7b, .L_fixup_handle_6
96+ _asm_extable 8b, .L_fixup_handle_7
97+ _asm_extable 9b, .L_fixup_handle_0
98+SYM_FUNC_END(__clear_user_fast)
arch/loongarch/lib/copy_user.S+92−16
@@ -3,31 +3,38 @@ · 2 unmodified lines
33 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
44 */
55
6+#include <asm/alternative-asm.h>
67 #include <asm/asm.h>
78 #include <asm/asmmacro.h>
9+#include <asm/asm-extable.h>
10+#include <asm/cpu.h>
811 #include <asm/export.h>
912 #include <asm/regdef.h>
1013
11−.macro fixup_ex from, to, offset, fix
12−.if \fix
13− .section .fixup, "ax"
14−\to: addi.d a0, a2, \offset
14+.irp to, 0, 1, 2, 3, 4, 5, 6, 7
15+.L_fixup_handle_\to\():
16+ addi.d a0, a2, (\to) * (-8)
1517 jr ra
16− .previous
17−.endif
18− .section __ex_table, "a"
19− PTR \from\()b, \to\()b
20− .previous
21−.endm
18+.endr
19+
20+SYM_FUNC_START(__copy_user)
21+ /*
22+ * Some CPUs support hardware unaligned access
23+ */
24+ ALTERNATIVE "b __copy_user_generic", \
25+ "b __copy_user_fast", CPU_FEATURE_UAL
26+SYM_FUNC_END(__copy_user)
27+
28+EXPORT_SYMBOL(__copy_user)
2229
2330 /*
24− * unsigned long __copy_user(void *to, const void *from, size_t n)
31+ * unsigned long __copy_user_generic(void *to, const void *from, size_t n)
2532 *
2633 * a0: to
2734 * a1: from
2835 * a2: n
2936 */
30−SYM_FUNC_START(__copy_user)
37+SYM_FUNC_START(__copy_user_generic)
3138 beqz a2, 3f
3239
3340 1: ld.b t0, a1, 0
@@ -40,8 +47,77 @@ SYM_FUNC_START(__copy_user) · 6 unmodified lines
4047 3: move a0, a2
4148 jr ra
4249
43− fixup_ex 1, 4, 0, 1
44− fixup_ex 2, 4, 0, 0
45−SYM_FUNC_END(__copy_user)
50+ _asm_extable 1b, .L_fixup_handle_0
51+ _asm_extable 2b, .L_fixup_handle_0
52+SYM_FUNC_END(__copy_user_generic)
4653
47−EXPORT_SYMBOL(__copy_user)
54+/*
55+ * unsigned long __copy_user_fast(void *to, const void *from, unsigned long n)
56+ *
57+ * a0: to
58+ * a1: from
59+ * a2: n
60+ */
61+SYM_FUNC_START(__copy_user_fast)
62+ beqz a2, 19f
63+
64+ ori a3, zero, 64
65+ blt a2, a3, 17f
66+
67+ /* copy 64 bytes at a time */
68+1: ld.d t0, a1, 0
69+2: ld.d t1, a1, 8
70+3: ld.d t2, a1, 16
71+4: ld.d t3, a1, 24
72+5: ld.d t4, a1, 32
73+6: ld.d t5, a1, 40
74+7: ld.d t6, a1, 48
75+8: ld.d t7, a1, 56
76+9: st.d t0, a0, 0
77+10: st.d t1, a0, 8
78+11: st.d t2, a0, 16
79+12: st.d t3, a0, 24
80+13: st.d t4, a0, 32
81+14: st.d t5, a0, 40
82+15: st.d t6, a0, 48
83+16: st.d t7, a0, 56
84+
85+ addi.d a0, a0, 64
86+ addi.d a1, a1, 64
87+ addi.d a2, a2, -64
88+ bge a2, a3, 1b
89+
90+ beqz a2, 19f
91+
92+ /* copy the remaining bytes */
93+17: ld.b t0, a1, 0
94+18: st.b t0, a0, 0
95+ addi.d a0, a0, 1
96+ addi.d a1, a1, 1
97+ addi.d a2, a2, -1
98+ bgt a2, zero, 17b
99+
100+ /* return */
101+19: move a0, a2
102+ jr ra
103+
104+ /* fixup and ex_table */
105+ _asm_extable 1b, .L_fixup_handle_0
106+ _asm_extable 2b, .L_fixup_handle_1
107+ _asm_extable 3b, .L_fixup_handle_2
108+ _asm_extable 4b, .L_fixup_handle_3
109+ _asm_extable 5b, .L_fixup_handle_4
110+ _asm_extable 6b, .L_fixup_handle_5
111+ _asm_extable 7b, .L_fixup_handle_6
112+ _asm_extable 8b, .L_fixup_handle_7
113+ _asm_extable 9b, .L_fixup_handle_0
114+ _asm_extable 10b, .L_fixup_handle_1
115+ _asm_extable 11b, .L_fixup_handle_2
116+ _asm_extable 12b, .L_fixup_handle_3
117+ _asm_extable 13b, .L_fixup_handle_4
118+ _asm_extable 14b, .L_fixup_handle_5
119+ _asm_extable 15b, .L_fixup_handle_6
120+ _asm_extable 16b, .L_fixup_handle_7
121+ _asm_extable 17b, .L_fixup_handle_0
122+ _asm_extable 18b, .L_fixup_handle_0
123+SYM_FUNC_END(__copy_user_fast)
arch/loongarch/lib/memcpy.S+95−0
@@ -0,0 +1,95 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+/*
3+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
4+ */
5+
6+#include <asm/alternative-asm.h>
7+#include <asm/asm.h>
8+#include <asm/asmmacro.h>
9+#include <asm/cpu.h>
10+#include <asm/export.h>
11+#include <asm/regdef.h>
12+
13+SYM_FUNC_START(memcpy)
14+ /*
15+ * Some CPUs support hardware unaligned access
16+ */
17+ ALTERNATIVE "b __memcpy_generic", \
18+ "b __memcpy_fast", CPU_FEATURE_UAL
19+SYM_FUNC_END(memcpy)
20+
21+EXPORT_SYMBOL(memcpy)
22+
23+/*
24+ * void *__memcpy_generic(void *dst, const void *src, size_t n)
25+ *
26+ * a0: dst
27+ * a1: src
28+ * a2: n
29+ */
30+SYM_FUNC_START(__memcpy_generic)
31+ move a3, a0
32+ beqz a2, 2f
33+
34+1: ld.b t0, a1, 0
35+ st.b t0, a0, 0
36+ addi.d a0, a0, 1
37+ addi.d a1, a1, 1
38+ addi.d a2, a2, -1
39+ bgt a2, zero, 1b
40+
41+2: move a0, a3
42+ jr ra
43+SYM_FUNC_END(__memcpy_generic)
44+
45+/*
46+ * void *__memcpy_fast(void *dst, const void *src, size_t n)
47+ *
48+ * a0: dst
49+ * a1: src
50+ * a2: n
51+ */
52+SYM_FUNC_START(__memcpy_fast)
53+ move a3, a0
54+ beqz a2, 3f
55+
56+ ori a4, zero, 64
57+ blt a2, a4, 2f
58+
59+ /* copy 64 bytes at a time */
60+1: ld.d t0, a1, 0
61+ ld.d t1, a1, 8
62+ ld.d t2, a1, 16
63+ ld.d t3, a1, 24
64+ ld.d t4, a1, 32
65+ ld.d t5, a1, 40
66+ ld.d t6, a1, 48
67+ ld.d t7, a1, 56
68+ st.d t0, a0, 0
69+ st.d t1, a0, 8
70+ st.d t2, a0, 16
71+ st.d t3, a0, 24
72+ st.d t4, a0, 32
73+ st.d t5, a0, 40
74+ st.d t6, a0, 48
75+ st.d t7, a0, 56
76+
77+ addi.d a0, a0, 64
78+ addi.d a1, a1, 64
79+ addi.d a2, a2, -64
80+ bge a2, a4, 1b
81+
82+ beqz a2, 3f
83+
84+ /* copy the remaining bytes */
85+2: ld.b t0, a1, 0
86+ st.b t0, a0, 0
87+ addi.d a0, a0, 1
88+ addi.d a1, a1, 1
89+ addi.d a2, a2, -1
90+ bgt a2, zero, 2b
91+
92+ /* return */
93+3: move a0, a3
94+ jr ra
95+SYM_FUNC_END(__memcpy_fast)
arch/loongarch/lib/memmove.S+121−0
@@ -0,0 +1,121 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+/*
3+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
4+ */
5+
6+#include <asm/alternative-asm.h>
7+#include <asm/asm.h>
8+#include <asm/asmmacro.h>
9+#include <asm/cpu.h>
10+#include <asm/export.h>
11+#include <asm/regdef.h>
12+
13+SYM_FUNC_START(memmove)
14+ blt a0, a1, 1f /* dst < src, memcpy */
15+ blt a1, a0, 3f /* src < dst, rmemcpy */
16+ jr ra /* dst == src, return */
17+
18+ /* if (src - dst) < 64, copy 1 byte at a time */
19+1: ori a3, zero, 64
20+ sub.d t0, a1, a0
21+ blt t0, a3, 2f
22+ b memcpy
23+2: b __memcpy_generic
24+
25+ /* if (dst - src) < 64, copy 1 byte at a time */
26+3: ori a3, zero, 64
27+ sub.d t0, a0, a1
28+ blt t0, a3, 4f
29+ b rmemcpy
30+4: b __rmemcpy_generic
31+SYM_FUNC_END(memmove)
32+
33+EXPORT_SYMBOL(memmove)
34+
35+SYM_FUNC_START(rmemcpy)
36+ /*
37+ * Some CPUs support hardware unaligned access
38+ */
39+ ALTERNATIVE "b __rmemcpy_generic", \
40+ "b __rmemcpy_fast", CPU_FEATURE_UAL
41+SYM_FUNC_END(rmemcpy)
42+
43+/*
44+ * void *__rmemcpy_generic(void *dst, const void *src, size_t n)
45+ *
46+ * a0: dst
47+ * a1: src
48+ * a2: n
49+ */
50+SYM_FUNC_START(__rmemcpy_generic)
51+ move a3, a0
52+ beqz a2, 2f
53+
54+ add.d a0, a0, a2
55+ add.d a1, a1, a2
56+
57+1: ld.b t0, a1, -1
58+ st.b t0, a0, -1
59+ addi.d a0, a0, -1
60+ addi.d a1, a1, -1
61+ addi.d a2, a2, -1
62+ bgt a2, zero, 1b
63+
64+2: move a0, a3
65+ jr ra
66+SYM_FUNC_END(__rmemcpy_generic)
67+
68+/*
69+ * void *__rmemcpy_fast(void *dst, const void *src, size_t n)
70+ *
71+ * a0: dst
72+ * a1: src
73+ * a2: n
74+ */
75+SYM_FUNC_START(__rmemcpy_fast)
76+ move a3, a0
77+ beqz a2, 3f
78+
79+ add.d a0, a0, a2
80+ add.d a1, a1, a2
81+
82+ ori a4, zero, 64
83+ blt a2, a4, 2f
84+
85+ /* copy 64 bytes at a time */
86+1: ld.d t0, a1, -8
87+ ld.d t1, a1, -16
88+ ld.d t2, a1, -24
89+ ld.d t3, a1, -32
90+ ld.d t4, a1, -40
91+ ld.d t5, a1, -48
92+ ld.d t6, a1, -56
93+ ld.d t7, a1, -64
94+ st.d t0, a0, -8
95+ st.d t1, a0, -16
96+ st.d t2, a0, -24
97+ st.d t3, a0, -32
98+ st.d t4, a0, -40
99+ st.d t5, a0, -48
100+ st.d t6, a0, -56
101+ st.d t7, a0, -64
102+
103+ addi.d a0, a0, -64
104+ addi.d a1, a1, -64
105+ addi.d a2, a2, -64
106+ bge a2, a4, 1b
107+
108+ beqz a2, 3f
109+
110+ /* copy the remaining bytes */
111+2: ld.b t0, a1, -1
112+ st.b t0, a0, -1
113+ addi.d a0, a0, -1
114+ addi.d a1, a1, -1
115+ addi.d a2, a2, -1
116+ bgt a2, zero, 2b
117+
118+ /* return */
119+3: move a0, a3
120+ jr ra
121+SYM_FUNC_END(__rmemcpy_fast)
arch/loongarch/lib/memset.S+91−0
@@ -0,0 +1,91 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+/*
3+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
4+ */
5+
6+#include <asm/alternative-asm.h>
7+#include <asm/asm.h>
8+#include <asm/asmmacro.h>
9+#include <asm/cpu.h>
10+#include <asm/export.h>
11+#include <asm/regdef.h>
12+
13+.macro fill_to_64 r0
14+ bstrins.d \r0, \r0, 15, 8
15+ bstrins.d \r0, \r0, 31, 16
16+ bstrins.d \r0, \r0, 63, 32
17+.endm
18+
19+SYM_FUNC_START(memset)
20+ /*
21+ * Some CPUs support hardware unaligned access
22+ */
23+ ALTERNATIVE "b __memset_generic", \
24+ "b __memset_fast", CPU_FEATURE_UAL
25+SYM_FUNC_END(memset)
26+
27+EXPORT_SYMBOL(memset)
28+
29+/*
30+ * void *__memset_generic(void *s, int c, size_t n)
31+ *
32+ * a0: s
33+ * a1: c
34+ * a2: n
35+ */
36+SYM_FUNC_START(__memset_generic)
37+ move a3, a0
38+ beqz a2, 2f
39+
40+1: st.b a1, a0, 0
41+ addi.d a0, a0, 1
42+ addi.d a2, a2, -1
43+ bgt a2, zero, 1b
44+
45+2: move a0, a3
46+ jr ra
47+SYM_FUNC_END(__memset_generic)
48+
49+/*
50+ * void *__memset_fast(void *s, int c, size_t n)
51+ *
52+ * a0: s
53+ * a1: c
54+ * a2: n
55+ */
56+SYM_FUNC_START(__memset_fast)
57+ move a3, a0
58+ beqz a2, 3f
59+
60+ ori a4, zero, 64
61+ blt a2, a4, 2f
62+
63+ /* fill a1 to 64 bits */
64+ fill_to_64 a1
65+
66+ /* set 64 bytes at a time */
67+1: st.d a1, a0, 0
68+ st.d a1, a0, 8
69+ st.d a1, a0, 16
70+ st.d a1, a0, 24
71+ st.d a1, a0, 32
72+ st.d a1, a0, 40
73+ st.d a1, a0, 48
74+ st.d a1, a0, 56
75+
76+ addi.d a0, a0, 64
77+ addi.d a2, a2, -64
78+ bge a2, a4, 1b
79+
80+ beqz a2, 3f
81+
82+ /* set the remaining bytes */
83+2: st.b a1, a0, 0
84+ addi.d a0, a0, 1
85+ addi.d a2, a2, -1
86+ bgt a2, zero, 2b
87+
88+ /* return */
89+3: move a0, a3
90+ jr ra
91+SYM_FUNC_END(__memset_fast)
arch/loongarch/lib/unaligned.S+84−0
@@ -0,0 +1,84 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+/*
3+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
4+ */
5+
6+#include <linux/linkage.h>
7+
8+#include <asm/asm.h>
9+#include <asm/asmmacro.h>
10+#include <asm/asm-extable.h>
11+#include <asm/errno.h>
12+#include <asm/export.h>
13+#include <asm/regdef.h>
14+
15+.L_fixup_handle_unaligned:
16+ li.w a0, -EFAULT
17+ jr ra
18+
19+/*
20+ * unsigned long unaligned_read(void *addr, void *value, unsigned long n, bool sign)
21+ *
22+ * a0: addr
23+ * a1: value
24+ * a2: n
25+ * a3: sign
26+ */
27+SYM_FUNC_START(unaligned_read)
28+ beqz a2, 5f
29+
30+ li.w t2, 0
31+ addi.d t0, a2, -1
32+ slli.d t1, t0, 3
33+ add.d a0, a0, t0
34+
35+ beqz a3, 2f
36+1: ld.b t3, a0, 0
37+ b 3f
38+
39+2: ld.bu t3, a0, 0
40+3: sll.d t3, t3, t1
41+ or t2, t2, t3
42+ addi.d t1, t1, -8
43+ addi.d a0, a0, -1
44+ addi.d a2, a2, -1
45+ bgtz a2, 2b
46+4: st.d t2, a1, 0
47+
48+ move a0, a2
49+ jr ra
50+
51+5: li.w a0, -EFAULT
52+ jr ra
53+
54+ _asm_extable 1b, .L_fixup_handle_unaligned
55+ _asm_extable 2b, .L_fixup_handle_unaligned
56+ _asm_extable 4b, .L_fixup_handle_unaligned
57+SYM_FUNC_END(unaligned_read)
58+
59+/*
60+ * unsigned long unaligned_write(void *addr, unsigned long value, unsigned long n)
61+ *
62+ * a0: addr
63+ * a1: value
64+ * a2: n
65+ */
66+SYM_FUNC_START(unaligned_write)
67+ beqz a2, 3f
68+
69+ li.w t0, 0
70+1: srl.d t1, a1, t0
71+2: st.b t1, a0, 0
72+ addi.d t0, t0, 8
73+ addi.d a2, a2, -1
74+ addi.d a0, a0, 1
75+ bgtz a2, 1b
76+
77+ move a0, a2
78+ jr ra
79+
80+3: li.w a0, -EFAULT
81+ jr ra
82+
83+ _asm_extable 2b, .L_fixup_handle_unaligned
84+SYM_FUNC_END(unaligned_write)
arch/loongarch/mm/extable.c+50−9
@@ -2,21 +2,62 @@ · 1 unmodified line
22 /*
33 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
44 */
5+#include <linux/bitfield.h>
56 #include <linux/extable.h>
6−#include <linux/spinlock.h>
7−#include <asm/branch.h>
87 #include <linux/uaccess.h>
8+#include <asm/asm-extable.h>
9+#include <asm/branch.h>
10+
11+static inline unsigned long
12+get_ex_fixup(const struct exception_table_entry *ex)
13+{
14+ return ((unsigned long)&ex->fixup + ex->fixup);
15+}
16+
17+static inline void regs_set_gpr(struct pt_regs *regs,
18+ unsigned int offset, unsigned long val)
19+{
20+ if (offset && offset <= MAX_REG_OFFSET)
21+ *(unsigned long *)((unsigned long)regs + offset) = val;
22+}
23+
24+static bool ex_handler_fixup(const struct exception_table_entry *ex,
25+ struct pt_regs *regs)
26+{
27+ regs->csr_era = get_ex_fixup(ex);
28+
29+ return true;
30+}
31+
32+static bool ex_handler_uaccess_err_zero(const struct exception_table_entry *ex,
33+ struct pt_regs *regs)
34+{
35+ int reg_err = FIELD_GET(EX_DATA_REG_ERR, ex->data);
36+ int reg_zero = FIELD_GET(EX_DATA_REG_ZERO, ex->data);
37+
38+ regs_set_gpr(regs, reg_err * sizeof(unsigned long), -EFAULT);
39+ regs_set_gpr(regs, reg_zero * sizeof(unsigned long), 0);
40+ regs->csr_era = get_ex_fixup(ex);
41+
42+ return true;
43+}
944
10−int fixup_exception(struct pt_regs *regs)
45+bool fixup_exception(struct pt_regs *regs)
1146 {
12− const struct exception_table_entry *fixup;
47+ const struct exception_table_entry *ex;
1348
14− fixup = search_exception_tables(exception_era(regs));
15− if (fixup) {
16− regs->csr_era = fixup->fixup;
49+ ex = search_exception_tables(exception_era(regs));
50+ if (!ex)
51+ return false;
1752
18− return 1;
53+ switch (ex->type) {
54+ case EX_TYPE_FIXUP:
55+ return ex_handler_fixup(ex, regs);
56+ case EX_TYPE_UACCESS_ERR_ZERO:
57+ return ex_handler_uaccess_err_zero(ex, regs);
58+ case EX_TYPE_BPF:
59+ return ex_handler_bpf(ex, regs);
1960 }
2061
21− return 0;
62+ BUG();
2263 }
arch/loongarch/net/bpf_jit.c+81−5
@@ -387,6 +387,65 @@ static bool is_signed_bpf_cond(u8 cond) · 386 unmodified lines
387387 cond == BPF_JSGE || cond == BPF_JSLE;
388388 }
389389
390+#define BPF_FIXUP_REG_MASK GENMASK(31, 27)
391+#define BPF_FIXUP_OFFSET_MASK GENMASK(26, 0)
392+
393+bool ex_handler_bpf(const struct exception_table_entry *ex,
394+ struct pt_regs *regs)
395+{
396+ int dst_reg = FIELD_GET(BPF_FIXUP_REG_MASK, ex->fixup);
397+ off_t offset = FIELD_GET(BPF_FIXUP_OFFSET_MASK, ex->fixup);
398+
399+ regs->regs[dst_reg] = 0;
400+ regs->csr_era = (unsigned long)&ex->fixup - offset;
401+
402+ return true;
403+}
404+
405+/* For accesses to BTF pointers, add an entry to the exception table */
406+static int add_exception_handler(const struct bpf_insn *insn,
407+ struct jit_ctx *ctx,
408+ int dst_reg)
409+{
410+ unsigned long pc;
411+ off_t offset;
412+ struct exception_table_entry *ex;
413+
414+ if (!ctx->image || !ctx->prog->aux->extable || BPF_MODE(insn->code) != BPF_PROBE_MEM)
415+ return 0;
416+
417+ if (WARN_ON_ONCE(ctx->num_exentries >= ctx->prog->aux->num_exentries))
418+ return -EINVAL;
419+
420+ ex = &ctx->prog->aux->extable[ctx->num_exentries];
421+ pc = (unsigned long)&ctx->image[ctx->idx - 1];
422+
423+ offset = pc - (long)&ex->insn;
424+ if (WARN_ON_ONCE(offset >= 0 || offset < INT_MIN))
425+ return -ERANGE;
426+
427+ ex->insn = offset;
428+
429+ /*
430+ * Since the extable follows the program, the fixup offset is always
431+ * negative and limited to BPF_JIT_REGION_SIZE. Store a positive value
432+ * to keep things simple, and put the destination register in the upper
433+ * bits. We don't need to worry about buildtime or runtime sort
434+ * modifying the upper bits because the table is already sorted, and
435+ * isn't part of the main exception table.
436+ */
437+ offset = (long)&ex->fixup - (pc + LOONGARCH_INSN_SIZE);
438+ if (!FIELD_FIT(BPF_FIXUP_OFFSET_MASK, offset))
439+ return -ERANGE;
440+
441+ ex->type = EX_TYPE_BPF;
442+ ex->fixup = FIELD_PREP(BPF_FIXUP_OFFSET_MASK, offset) | FIELD_PREP(BPF_FIXUP_REG_MASK, dst_reg);
443+
444+ ctx->num_exentries++;
445+
446+ return 0;
447+}
448+
390449 static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool extra_pass)
391450 {
392451 u8 tm = -1;
@@ -816,6 +875,10 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext · 423 unmodified lines
816875 case BPF_LDX | BPF_MEM | BPF_H:
817876 case BPF_LDX | BPF_MEM | BPF_W:
818877 case BPF_LDX | BPF_MEM | BPF_DW:
878+ case BPF_LDX | BPF_PROBE_MEM | BPF_DW:
879+ case BPF_LDX | BPF_PROBE_MEM | BPF_W:
880+ case BPF_LDX | BPF_PROBE_MEM | BPF_H:
881+ case BPF_LDX | BPF_PROBE_MEM | BPF_B:
819882 switch (BPF_SIZE(code)) {
820883 case BPF_B:
821884 if (is_signed_imm12(off)) {
@@ -854,6 +917,10 @@ static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool ext · 32 unmodified lines
854917 }
855918 break;
856919 }
920+
921+ ret = add_exception_handler(insn, ctx, dst);
922+ if (ret)
923+ return ret;
857924 break;
858925
859926 /* *(size *)(dst + off) = imm */
@@ -1018,6 +1085,9 @@ static int validate_code(struct jit_ctx *ctx) · 158 unmodified lines
10181085 return -1;
10191086 }
10201087
1088+ if (WARN_ON_ONCE(ctx->num_exentries != ctx->prog->aux->num_exentries))
1089+ return -1;
1090+
10211091 return 0;
10221092 }
10231093
@@ -1025,7 +1095,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) · 1 unmodified line
10251095 {
10261096 bool tmp_blinded = false, extra_pass = false;
10271097 u8 *image_ptr;
1028− int image_size;
1098+ int image_size, prog_size, extable_size;
10291099 struct jit_ctx ctx;
10301100 struct jit_data *jit_data;
10311101 struct bpf_binary_header *header;
@@ -1066,7 +1136,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) · 34 unmodified lines
10661136 image_ptr = jit_data->image;
10671137 header = jit_data->header;
10681138 extra_pass = true;
1069− image_size = sizeof(u32) * ctx.idx;
1139+ prog_size = sizeof(u32) * ctx.idx;
10701140 goto skip_init_ctx;
10711141 }
10721142
@@ -1088,12 +1158,15 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) · 15 unmodified lines
10881158 ctx.epilogue_offset = ctx.idx;
10891159 build_epilogue(&ctx);
10901160
1161+ extable_size = prog->aux->num_exentries * sizeof(struct exception_table_entry);
1162+
10911163 /* Now we know the actual image size.
10921164 * As each LoongArch instruction is of length 32bit,
10931165 * we are translating number of JITed intructions into
10941166 * the size required to store these JITed code.
10951167 */
1096− image_size = sizeof(u32) * ctx.idx;
1168+ prog_size = sizeof(u32) * ctx.idx;
1169+ image_size = prog_size + extable_size;
10971170 /* Now we know the size of the structure to make */
10981171 header = bpf_jit_binary_alloc(image_size, &image_ptr,
10991172 sizeof(u32), jit_fill_hole);
@@ -1104,9 +1177,12 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) · 4 unmodified lines
11041177
11051178 /* 2. Now, the actual pass to generate final JIT code */
11061179 ctx.image = (union loongarch_instruction *)image_ptr;
1180+ if (extable_size)
1181+ prog->aux->extable = (void *)image_ptr + prog_size;
11071182
11081183 skip_init_ctx:
11091184 ctx.idx = 0;
1185+ ctx.num_exentries = 0;
11101186
11111187 build_prologue(&ctx);
11121188 if (build_body(&ctx, extra_pass)) {
@@ -1125,7 +1201,7 @@ skip_init_ctx: · 12 unmodified lines
11251201
11261202 /* And we're done */
11271203 if (bpf_jit_enable > 1)
1128− bpf_jit_dump(prog->len, image_size, 2, ctx.image);
1204+ bpf_jit_dump(prog->len, prog_size, 2, ctx.image);
11291205
11301206 /* Update the icache */
11311207 flush_icache_range((unsigned long)header, (unsigned long)(ctx.image + ctx.idx));
@@ -1147,7 +1223,7 @@ skip_init_ctx: · 15 unmodified lines
11471223 jit_data->header = header;
11481224 }
11491225 prog->jited = 1;
1150− prog->jited_len = image_size;
1226+ prog->jited_len = prog_size;
11511227 prog->bpf_func = (void *)ctx.image;
11521228
11531229 if (!prog->is_func || extra_pass) {
arch/loongarch/net/bpf_jit.h+2−0
@@ -4,6 +4,7 @@ · 3 unmodified lines
44 *
55 * Copyright (C) 2022 Loongson Technology Corporation Limited
66 */
7+#include <linux/bitfield.h>
78 #include <linux/bpf.h>
89 #include <linux/filter.h>
910 #include <asm/cacheflush.h>
@@ -15,6 +16,7 @@ struct jit_ctx { · 5 unmodified lines
1516 unsigned int flags;
1617 unsigned int epilogue_offset;
1718 u32 *offset;
19+ int num_exentries;
1820 union loongarch_instruction *image;
1921 u32 stack_size;
2022 };
arch/loongarch/pci/acpi.c+5−2
@@ -26,9 +26,12 @@ void pcibios_add_bus(struct pci_bus *bus) · 25 unmodified lines
2626
2727 int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge)
2828 {
29− struct pci_config_window *cfg = bridge->bus->sysdata;
30− struct acpi_device *adev = to_acpi_device(cfg->parent);
29+ struct acpi_device *adev = NULL;
3130 struct device *bus_dev = &bridge->bus->dev;
31+ struct pci_config_window *cfg = bridge->bus->sysdata;
32+
33+ if (!acpi_disabled)
34+ adev = to_acpi_device(cfg->parent);
3235
3336 ACPI_COMPANION_SET(&bridge->dev, adev);
3437 set_dev_node(bus_dev, pa_to_nid(cfg->res.start));
arch/loongarch/power/Makefile+4−0
@@ -0,0 +1,4 @@
1+obj-y += platform.o
2+
3+obj-$(CONFIG_SUSPEND) += suspend.o suspend_asm.o
4+obj-$(CONFIG_HIBERNATION) += hibernate.o hibernate_asm.o
arch/loongarch/power/hibernate.c+62−0
@@ -0,0 +1,62 @@
1+// SPDX-License-Identifier: GPL-2.0
2+#include <asm/fpu.h>
3+#include <asm/loongson.h>
4+#include <asm/sections.h>
5+#include <asm/tlbflush.h>
6+#include <linux/suspend.h>
7+
8+static u32 saved_crmd;
9+static u32 saved_prmd;
10+static u32 saved_euen;
11+static u32 saved_ecfg;
12+static u64 saved_pcpu_base;
13+struct pt_regs saved_regs;
14+
15+void save_processor_state(void)
16+{
17+ saved_crmd = csr_read32(LOONGARCH_CSR_CRMD);
18+ saved_prmd = csr_read32(LOONGARCH_CSR_PRMD);
19+ saved_euen = csr_read32(LOONGARCH_CSR_EUEN);
20+ saved_ecfg = csr_read32(LOONGARCH_CSR_ECFG);
21+ saved_pcpu_base = csr_read64(PERCPU_BASE_KS);
22+
23+ if (is_fpu_owner())
24+ save_fp(current);
25+}
26+
27+void restore_processor_state(void)
28+{
29+ csr_write32(saved_crmd, LOONGARCH_CSR_CRMD);
30+ csr_write32(saved_prmd, LOONGARCH_CSR_PRMD);
31+ csr_write32(saved_euen, LOONGARCH_CSR_EUEN);
32+ csr_write32(saved_ecfg, LOONGARCH_CSR_ECFG);
33+ csr_write64(saved_pcpu_base, PERCPU_BASE_KS);
34+
35+ if (is_fpu_owner())
36+ restore_fp(current);
37+}
38+
39+int pfn_is_nosave(unsigned long pfn)
40+{
41+ unsigned long nosave_begin_pfn = PFN_DOWN(__pa(&__nosave_begin));
42+ unsigned long nosave_end_pfn = PFN_UP(__pa(&__nosave_end));
43+
44+ return (pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn);
45+}
46+
47+extern int swsusp_asm_suspend(void);
48+
49+int swsusp_arch_suspend(void)
50+{
51+ enable_pci_wakeup();
52+ return swsusp_asm_suspend();
53+}
54+
55+extern int swsusp_asm_resume(void);
56+
57+int swsusp_arch_resume(void)
58+{
59+ /* Avoid TLB mismatch during and after kernel resume */
60+ local_flush_tlb_all();
61+ return swsusp_asm_resume();
62+}
arch/loongarch/power/hibernate_asm.S+66−0
@@ -0,0 +1,66 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+/*
3+ * Hibernation support specific for LoongArch
4+ *
5+ * Author: Huacai Chen <chenhuacai@loongson.cn>
6+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
7+ */
8+#include <linux/linkage.h>
9+#include <asm/asm.h>
10+#include <asm/asm-offsets.h>
11+#include <asm/regdef.h>
12+
13+.text
14+SYM_FUNC_START(swsusp_asm_suspend)
15+ la.pcrel t0, saved_regs
16+ PTR_S ra, t0, PT_R1
17+ PTR_S tp, t0, PT_R2
18+ PTR_S sp, t0, PT_R3
19+ PTR_S u0, t0, PT_R21
20+ PTR_S fp, t0, PT_R22
21+ PTR_S s0, t0, PT_R23
22+ PTR_S s1, t0, PT_R24
23+ PTR_S s2, t0, PT_R25
24+ PTR_S s3, t0, PT_R26
25+ PTR_S s4, t0, PT_R27
26+ PTR_S s5, t0, PT_R28
27+ PTR_S s6, t0, PT_R29
28+ PTR_S s7, t0, PT_R30
29+ PTR_S s8, t0, PT_R31
30+ b swsusp_save
31+SYM_FUNC_END(swsusp_asm_suspend)
32+
33+SYM_FUNC_START(swsusp_asm_resume)
34+ la.pcrel t0, restore_pblist
35+ PTR_L t0, t0, 0
36+0:
37+ PTR_L t1, t0, PBE_ADDRESS /* source */
38+ PTR_L t2, t0, PBE_ORIG_ADDRESS /* destination */
39+ PTR_LI t3, _PAGE_SIZE
40+ PTR_ADD t3, t3, t1
41+1:
42+ REG_L t8, t1, 0
43+ REG_S t8, t2, 0
44+ PTR_ADDI t1, t1, SZREG
45+ PTR_ADDI t2, t2, SZREG
46+ bne t1, t3, 1b
47+ PTR_L t0, t0, PBE_NEXT
48+ bnez t0, 0b
49+ la.pcrel t0, saved_regs
50+ PTR_L ra, t0, PT_R1
51+ PTR_L tp, t0, PT_R2
52+ PTR_L sp, t0, PT_R3
53+ PTR_L u0, t0, PT_R21
54+ PTR_L fp, t0, PT_R22
55+ PTR_L s0, t0, PT_R23
56+ PTR_L s1, t0, PT_R24
57+ PTR_L s2, t0, PT_R25
58+ PTR_L s3, t0, PT_R26
59+ PTR_L s4, t0, PT_R27
60+ PTR_L s5, t0, PT_R28
61+ PTR_L s6, t0, PT_R29
62+ PTR_L s7, t0, PT_R30
63+ PTR_L s8, t0, PT_R31
64+ PTR_LI a0, 0x0
65+ jirl zero, ra, 0
66+SYM_FUNC_END(swsusp_asm_resume)
arch/loongarch/power/platform.c+57−0
@@ -0,0 +1,57 @@
1+// SPDX-License-Identifier: GPL-2.0
2+/*
3+ * Author: Huacai Chen <chenhuacai@loongson.cn>
4+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
5+ */
6+#include <linux/acpi.h>
7+#include <linux/platform_device.h>
8+
9+#include <asm/bootinfo.h>
10+#include <asm/loongson.h>
11+
12+void enable_gpe_wakeup(void)
13+{
14+ if (acpi_disabled)
15+ return;
16+
17+ if (acpi_gbl_reduced_hardware)
18+ return;
19+
20+ acpi_enable_all_wakeup_gpes();
21+}
22+
23+void enable_pci_wakeup(void)
24+{
25+ if (acpi_disabled)
26+ return;
27+
28+ if (acpi_gbl_reduced_hardware)
29+ return;
30+
31+ acpi_write_bit_register(ACPI_BITREG_PCIEXP_WAKE_STATUS, 1);
32+
33+ if (acpi_gbl_FADT.flags & ACPI_FADT_PCI_EXPRESS_WAKE)
34+ acpi_write_bit_register(ACPI_BITREG_PCIEXP_WAKE_DISABLE, 0);
35+}
36+
37+static int __init loongson3_acpi_suspend_init(void)
38+{
39+#ifdef CONFIG_ACPI
40+ acpi_status status;
41+ uint64_t suspend_addr = 0;
42+
43+ if (acpi_disabled || acpi_gbl_reduced_hardware)
44+ return 0;
45+
46+ acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1);
47+ status = acpi_evaluate_integer(NULL, "\\SADR", NULL, &suspend_addr);
48+ if (ACPI_FAILURE(status) || !suspend_addr) {
49+ pr_err("ACPI S3 is not support!\n");
50+ return -1;
51+ }
52+ loongson_sysconf.suspend_addr = (u64)phys_to_virt(PHYSADDR(suspend_addr));
53+#endif
54+ return 0;
55+}
56+
57+device_initcall(loongson3_acpi_suspend_init);
arch/loongarch/power/suspend.c+73−0
@@ -0,0 +1,73 @@
1+// SPDX-License-Identifier: GPL-2.0
2+/*
3+ * loongson-specific suspend support
4+ *
5+ * Author: Huacai Chen <chenhuacai@loongson.cn>
6+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
7+ */
8+#include <linux/acpi.h>
9+#include <linux/pm.h>
10+#include <linux/suspend.h>
11+
12+#include <asm/loongarch.h>
13+#include <asm/loongson.h>
14+#include <asm/setup.h>
15+#include <asm/time.h>
16+#include <asm/tlbflush.h>
17+
18+u64 loongarch_suspend_addr;
19+
20+struct saved_registers {
21+ u32 ecfg;
22+ u32 euen;
23+ u64 pgd;
24+ u64 kpgd;
25+ u32 pwctl0;
26+ u32 pwctl1;
27+};
28+static struct saved_registers saved_regs;
29+
30+static void arch_common_suspend(void)
31+{
32+ save_counter();
33+ saved_regs.pgd = csr_read64(LOONGARCH_CSR_PGDL);
34+ saved_regs.kpgd = csr_read64(LOONGARCH_CSR_PGDH);
35+ saved_regs.pwctl0 = csr_read32(LOONGARCH_CSR_PWCTL0);
36+ saved_regs.pwctl1 = csr_read32(LOONGARCH_CSR_PWCTL1);
37+ saved_regs.ecfg = csr_read32(LOONGARCH_CSR_ECFG);
38+ saved_regs.euen = csr_read32(LOONGARCH_CSR_EUEN);
39+
40+ loongarch_suspend_addr = loongson_sysconf.suspend_addr;
41+}
42+
43+static void arch_common_resume(void)
44+{
45+ sync_counter();
46+ local_flush_tlb_all();
47+ csr_write64(per_cpu_offset(0), PERCPU_BASE_KS);
48+ csr_write64(eentry, LOONGARCH_CSR_EENTRY);
49+ csr_write64(eentry, LOONGARCH_CSR_MERRENTRY);
50+ csr_write64(tlbrentry, LOONGARCH_CSR_TLBRENTRY);
51+
52+ csr_write64(saved_regs.pgd, LOONGARCH_CSR_PGDL);
53+ csr_write64(saved_regs.kpgd, LOONGARCH_CSR_PGDH);
54+ csr_write32(saved_regs.pwctl0, LOONGARCH_CSR_PWCTL0);
55+ csr_write32(saved_regs.pwctl1, LOONGARCH_CSR_PWCTL1);
56+ csr_write32(saved_regs.ecfg, LOONGARCH_CSR_ECFG);
57+ csr_write32(saved_regs.euen, LOONGARCH_CSR_EUEN);
58+}
59+
60+int loongarch_acpi_suspend(void)
61+{
62+ enable_gpe_wakeup();
63+ enable_pci_wakeup();
64+
65+ arch_common_suspend();
66+
67+ /* processor specific suspend */
68+ loongarch_suspend_enter();
69+
70+ arch_common_resume();
71+
72+ return 0;
73+}
arch/loongarch/power/suspend_asm.S+89−0
@@ -0,0 +1,89 @@
1+/* SPDX-License-Identifier: GPL-2.0 */
2+/*
3+ * Sleep helper for Loongson-3 sleep mode.
4+ *
5+ * Author: Huacai Chen <chenhuacai@loongson.cn>
6+ * Copyright (C) 2020-2022 Loongson Technology Corporation Limited
7+ */
8+
9+#include <asm/asm.h>
10+#include <asm/asmmacro.h>
11+#include <asm/addrspace.h>
12+#include <asm/loongarch.h>
13+#include <asm/stackframe.h>
14+
15+/* preparatory stuff */
16+.macro SETUP_SLEEP
17+ addi.d sp, sp, -PT_SIZE
18+ st.d $r1, sp, PT_R1
19+ st.d $r2, sp, PT_R2
20+ st.d $r3, sp, PT_R3
21+ st.d $r4, sp, PT_R4
22+ st.d $r21, sp, PT_R21
23+ st.d $r22, sp, PT_R22
24+ st.d $r23, sp, PT_R23
25+ st.d $r24, sp, PT_R24
26+ st.d $r25, sp, PT_R25
27+ st.d $r26, sp, PT_R26
28+ st.d $r27, sp, PT_R27
29+ st.d $r28, sp, PT_R28
30+ st.d $r29, sp, PT_R29
31+ st.d $r30, sp, PT_R30
32+ st.d $r31, sp, PT_R31
33+
34+ la.pcrel t0, acpi_saved_sp
35+ st.d sp, t0, 0
36+.endm
37+
38+.macro SETUP_WAKEUP
39+ ld.d $r1, sp, PT_R1
40+ ld.d $r2, sp, PT_R2
41+ ld.d $r3, sp, PT_R3
42+ ld.d $r4, sp, PT_R4
43+ ld.d $r21, sp, PT_R21
44+ ld.d $r22, sp, PT_R22
45+ ld.d $r23, sp, PT_R23
46+ ld.d $r24, sp, PT_R24
47+ ld.d $r25, sp, PT_R25
48+ ld.d $r26, sp, PT_R26
49+ ld.d $r27, sp, PT_R27
50+ ld.d $r28, sp, PT_R28
51+ ld.d $r29, sp, PT_R29
52+ ld.d $r30, sp, PT_R30
53+ ld.d $r31, sp, PT_R31
54+.endm
55+
56+ .text
57+ .align 12
58+
59+/* Sleep/wakeup code for Loongson-3 */
60+SYM_FUNC_START(loongarch_suspend_enter)
61+ SETUP_SLEEP
62+ bl __flush_cache_all
63+
64+ /* Pass RA and SP to BIOS */
65+ addi.d a1, sp, 0
66+ la.pcrel a0, loongarch_wakeup_start
67+ la.pcrel t0, loongarch_suspend_addr
68+ ld.d t0, t0, 0
69+ jirl a0, t0, 0 /* Call BIOS's STR sleep routine */
70+
71+ /*
72+ * This is where we return upon wakeup.
73+ * Reload all of the registers and return.
74+ */
75+SYM_INNER_LABEL(loongarch_wakeup_start, SYM_L_GLOBAL)
76+ li.d t0, CSR_DMW0_INIT # UC, PLV0
77+ csrwr t0, LOONGARCH_CSR_DMWIN0
78+ li.d t0, CSR_DMW1_INIT # CA, PLV0
79+ csrwr t0, LOONGARCH_CSR_DMWIN1
80+
81+ la.abs t0, 0f
82+ jr t0
83+0:
84+ la.pcrel t0, acpi_saved_sp
85+ ld.d sp, t0, 0
86+ SETUP_WAKEUP
87+ addi.d sp, sp, PT_SIZE
88+ jr ra
89+SYM_FUNC_END(loongarch_suspend_enter)
scripts/mod/modpost.c+13−0
@@ -1523,6 +1523,14 @@ static int addend_mips_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r) · 1522 unmodified lines
15231523 #define R_RISCV_SUB32 39
15241524 #endif
15251525
1526+#ifndef EM_LOONGARCH
1527+#define EM_LOONGARCH 258
1528+#endif
1529+
1530+#ifndef R_LARCH_SUB32
1531+#define R_LARCH_SUB32 55
1532+#endif
1533+
15261534 static void section_rela(const char *modname, struct elf_info *elf,
15271535 Elf_Shdr *sechdr)
15281536 {
@@ -1564,6 +1572,11 @@ static void section_rela(const char *modname, struct elf_info *elf, · 35 unmodified lines
15641572 ELF_R_TYPE(r.r_info) == R_RISCV_SUB32)
15651573 continue;
15661574 break;
1575+ case EM_LOONGARCH:
1576+ if (!strcmp("__ex_table", fromsec) &&
1577+ ELF_R_TYPE(r.r_info) == R_LARCH_SUB32)
1578+ continue;
1579+ break;
15671580 }
15681581 sym = elf->symtab_start + r_sym;
15691582 /* Skip special sections */
scripts/recordmcount.c+39−0
@@ -38,6 +38,14 @@ · 37 unmodified lines
3838 #define R_AARCH64_ABS64 257
3939 #endif
4040
41+#ifndef EM_LOONGARCH
42+#define EM_LOONGARCH 258
43+#define R_LARCH_32 1
44+#define R_LARCH_64 2
45+#define R_LARCH_MARK_LA 20
46+#define R_LARCH_SOP_PUSH_PLT_PCREL 29
47+#endif
48+
4149 #define R_ARM_PC24 1
4250 #define R_ARM_THM_CALL 10
4351 #define R_ARM_CALL 28
@@ -441,6 +449,28 @@ static int arm64_is_fake_mcount(Elf64_Rel const *rp) · 397 unmodified lines
441449 return ELF64_R_TYPE(w8(rp->r_info)) != R_AARCH64_CALL26;
442450 }
443451
452+static int LARCH32_is_fake_mcount(Elf32_Rel const *rp)
453+{
454+ switch (ELF64_R_TYPE(w(rp->r_info))) {
455+ case R_LARCH_MARK_LA:
456+ case R_LARCH_SOP_PUSH_PLT_PCREL:
457+ return 0;
458+ }
459+
460+ return 1;
461+}
462+
463+static int LARCH64_is_fake_mcount(Elf64_Rel const *rp)
464+{
465+ switch (ELF64_R_TYPE(w(rp->r_info))) {
466+ case R_LARCH_MARK_LA:
467+ case R_LARCH_SOP_PUSH_PLT_PCREL:
468+ return 0;
469+ }
470+
471+ return 1;
472+}
473+
444474 /* 64-bit EM_MIPS has weird ELF64_Rela.r_info.
445475 * http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf
446476 * We interpret Table 29 Relocation Operation (Elf64_Rel, Elf64_Rela) [p.40]
@@ -558,6 +588,7 @@ static int do_file(char const *const fname) · 111 unmodified lines
558588 break;
559589 case EM_IA_64: reltype = R_IA64_IMM64; break;
560590 case EM_MIPS: /* reltype: e_class */ break;
591+ case EM_LOONGARCH: /* reltype: e_class */ break;
561592 case EM_PPC: reltype = R_PPC_ADDR32; break;
562593 case EM_PPC64: reltype = R_PPC64_ADDR64; break;
563594 case EM_S390: /* reltype: e_class */ break;
@@ -589,6 +620,10 @@ static int do_file(char const *const fname) · 25 unmodified lines
589620 reltype = R_MIPS_32;
590621 is_fake_mcount32 = MIPS32_is_fake_mcount;
591622 }
623+ if (w2(ehdr->e_machine) == EM_LOONGARCH) {
624+ reltype = R_LARCH_32;
625+ is_fake_mcount32 = LARCH32_is_fake_mcount;
626+ }
592627 if (do32(ehdr, fname, reltype) < 0)
593628 goto out;
594629 break;
@@ -610,6 +645,10 @@ static int do_file(char const *const fname) · 15 unmodified lines
610645 Elf64_r_info = MIPS64_r_info;
611646 is_fake_mcount64 = MIPS64_is_fake_mcount;
612647 }
648+ if (w2(ghdr->e_machine) == EM_LOONGARCH) {
649+ reltype = R_LARCH_64;
650+ is_fake_mcount64 = LARCH64_is_fake_mcount;
651+ }
613652 if (do64(ghdr, fname, reltype) < 0)
614653 goto out;
615654 break;
scripts/sorttable.c+1−1
@@ -304,6 +304,7 @@ static int do_file(char const *const fname, void *addr) · 303 unmodified lines
304304 switch (r2(&ehdr->e_machine)) {
305305 case EM_386:
306306 case EM_AARCH64:
307+ case EM_LOONGARCH:
307308 case EM_RISCV:
308309 case EM_S390:
309310 case EM_X86_64:
@@ -317,7 +318,6 @@ static int do_file(char const *const fname, void *addr) · 7 unmodified lines
317318 case EM_ARCOMPACT:
318319 case EM_ARCV2:
319320 case EM_ARM:
320− case EM_LOONGARCH:
321321 case EM_MICROBLAZE:
322322 case EM_MIPS:
323323 case EM_XTENSA: