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 ...
Unified · Split
| @@ -436,8 +436,8 @@ ignore-unaligned-usertrap · 435 unmodified lines | ||
| 436 | 436 | |
| 437 | 437 | On architectures where unaligned accesses cause traps, and where this |
| 438 | 438 | 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. | |
| 441 | 441 | |
| 442 | 442 | = ============================================================= |
| 443 | 443 | 0 Log all unaligned accesses. |
| @@ -1492,8 +1492,8 @@ unaligned-trap · 1048 unmodified lines | ||
| 1492 | 1492 | |
| 1493 | 1493 | On architectures where unaligned accesses cause traps, and where this |
| 1494 | 1494 | 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). | |
| 1497 | 1497 | |
| 1498 | 1498 | = ======================================================== |
| 1499 | 1499 | 0 Do not emulate unaligned accesses. |
| @@ -58,6 +58,7 @@ config LOONGARCH · 57 unmodified lines | ||
| 58 | 58 | select ARCH_WANTS_NO_INSTR |
| 59 | 59 | select BUILDTIME_TABLE_SORT |
| 60 | 60 | select COMMON_CLK |
| 61 | + select CPU_PM | |
| 61 | 62 | select EFI |
| 62 | 63 | select GENERIC_CLOCKEVENTS |
| 63 | 64 | select GENERIC_CMOS_UPDATE |
| @@ -86,11 +87,18 @@ config LOONGARCH · 22 unmodified lines | ||
| 86 | 87 | select HAVE_ARCH_TRANSPARENT_HUGEPAGE |
| 87 | 88 | select HAVE_ASM_MODVERSIONS |
| 88 | 89 | select HAVE_CONTEXT_TRACKING_USER |
| 90 | + select HAVE_C_RECORDMCOUNT | |
| 89 | 91 | select HAVE_DEBUG_STACKOVERFLOW |
| 90 | 92 | select HAVE_DMA_CONTIGUOUS |
| 93 | + select HAVE_DYNAMIC_FTRACE | |
| 94 | + select HAVE_DYNAMIC_FTRACE_WITH_ARGS | |
| 95 | + select HAVE_DYNAMIC_FTRACE_WITH_REGS | |
| 91 | 96 | select HAVE_EBPF_JIT |
| 92 | 97 | select HAVE_EXIT_THREAD |
| 93 | 98 | select HAVE_FAST_GUP |
| 99 | + select HAVE_FTRACE_MCOUNT_RECORD | |
| 100 | + select HAVE_FUNCTION_GRAPH_TRACER | |
| 101 | + select HAVE_FUNCTION_TRACER | |
| 94 | 102 | select HAVE_GENERIC_VDSO |
| 95 | 103 | select HAVE_IOREMAP_PROT |
| 96 | 104 | select HAVE_IRQ_EXIT_ON_IRQ_STACK |
| @@ -104,6 +112,7 @@ config LOONGARCH · 7 unmodified lines | ||
| 104 | 112 | select HAVE_REGS_AND_STACK_ACCESS_API |
| 105 | 113 | select HAVE_RSEQ |
| 106 | 114 | select HAVE_SETUP_PER_CPU_AREA if NUMA |
| 115 | + select HAVE_STACKPROTECTOR | |
| 107 | 116 | select HAVE_SYSCALL_TRACEPOINTS |
| 108 | 117 | select HAVE_TIF_NOHZ |
| 109 | 118 | select HAVE_VIRT_CPU_ACCOUNTING_GEN if !SMP |
| @@ -113,6 +122,8 @@ config LOONGARCH · 3 unmodified lines | ||
| 113 | 122 | select MODULES_USE_ELF_RELA if MODULES |
| 114 | 123 | select NEED_PER_CPU_EMBED_FIRST_CHUNK |
| 115 | 124 | select NEED_PER_CPU_PAGE_FIRST_CHUNK |
| 125 | + select OF | |
| 126 | + select OF_EARLY_FLATTREE | |
| 116 | 127 | select PCI |
| 117 | 128 | select PCI_DOMAINS_GENERIC |
| 118 | 129 | select PCI_ECAM if ACPI |
| @@ -123,6 +134,8 @@ config LOONGARCH · 4 unmodified lines | ||
| 123 | 134 | select RTC_LIB |
| 124 | 135 | select SMP |
| 125 | 136 | select SPARSE_IRQ |
| 137 | + select SYSCTL_ARCH_UNALIGN_ALLOW | |
| 138 | + select SYSCTL_ARCH_UNALIGN_NO_WARN | |
| 126 | 139 | select SYSCTL_EXCEPTION_TRACE |
| 127 | 140 | select SWIOTLB |
| 128 | 141 | select TRACE_IRQFLAGS_SUPPORT |
| @@ -516,6 +529,13 @@ config ARCH_MMAP_RND_BITS_MAX · 387 unmodified lines | ||
| 516 | 529 | |
| 517 | 530 | menu "Power management options" |
| 518 | 531 | |
| 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" | |
| 519 | 539 | source "drivers/acpi/Kconfig" |
| 520 | 540 | |
| 521 | 541 | endmenu |
| @@ -25,6 +25,11 @@ endif · 24 unmodified lines | ||
| 25 | 25 | 32bit-emul = elf32loongarch |
| 26 | 26 | 64bit-emul = elf64loongarch |
| 27 | 27 | |
| 28 | +ifdef CONFIG_DYNAMIC_FTRACE | |
| 29 | +KBUILD_CPPFLAGS += -DCC_USING_PATCHABLE_FUNCTION_ENTRY | |
| 30 | +CC_FLAGS_FTRACE := -fpatchable-function-entry=2 | |
| 31 | +endif | |
| 32 | + | |
| 28 | 33 | ifdef CONFIG_64BIT |
| 29 | 34 | tool-archpref = $(64bit-tool-archpref) |
| 30 | 35 | UTS_MACHINE := loongarch64 |
| @@ -104,6 +109,9 @@ endif · 73 unmodified lines | ||
| 104 | 109 | libs-y += arch/loongarch/lib/ |
| 105 | 110 | libs-$(CONFIG_EFI_STUB) += $(objtree)/drivers/firmware/efi/libstub/lib.a |
| 106 | 111 | |
| 112 | +# suspend and hibernation support | |
| 113 | +drivers-$(CONFIG_PM) += arch/loongarch/power/ | |
| 114 | + | |
| 107 | 115 | ifeq ($(KBUILD_EXTMOD),) |
| 108 | 116 | prepare: vdso_prepare |
| 109 | 117 | vdso_prepare: prepare0 |
| @@ -34,12 +34,13 @@ CONFIG_SYSFS_DEPRECATED=y · 33 unmodified lines | ||
| 34 | 34 | CONFIG_RELAY=y |
| 35 | 35 | CONFIG_BLK_DEV_INITRD=y |
| 36 | 36 | CONFIG_EXPERT=y |
| 37 | −CONFIG_USERFAULTFD=y | |
| 37 | +CONFIG_KALLSYMS_ALL=y | |
| 38 | 38 | CONFIG_PERF_EVENTS=y |
| 39 | −# CONFIG_COMPAT_BRK is not set | |
| 40 | 39 | CONFIG_LOONGARCH=y |
| 41 | 40 | CONFIG_64BIT=y |
| 42 | 41 | CONFIG_MACH_LOONGSON64=y |
| 42 | +CONFIG_PAGE_SIZE_16KB=y | |
| 43 | +CONFIG_HZ_250=y | |
| 43 | 44 | CONFIG_DMI=y |
| 44 | 45 | CONFIG_EFI=y |
| 45 | 46 | CONFIG_SMP=y |
| @@ -47,14 +48,14 @@ CONFIG_HOTPLUG_CPU=y · 1 unmodified line | ||
| 47 | 48 | CONFIG_NR_CPUS=64 |
| 48 | 49 | CONFIG_NUMA=y |
| 49 | 50 | CONFIG_KEXEC=y |
| 50 | −CONFIG_PAGE_SIZE_16KB=y | |
| 51 | −CONFIG_HZ_250=y | |
| 51 | +CONFIG_SUSPEND=y | |
| 52 | +CONFIG_HIBERNATION=y | |
| 52 | 53 | CONFIG_ACPI=y |
| 53 | 54 | CONFIG_ACPI_SPCR_TABLE=y |
| 54 | −CONFIG_ACPI_HOTPLUG_CPU=y | |
| 55 | 55 | CONFIG_ACPI_TAD=y |
| 56 | 56 | CONFIG_ACPI_DOCK=y |
| 57 | 57 | CONFIG_ACPI_IPMI=m |
| 58 | +CONFIG_ACPI_HOTPLUG_CPU=y | |
| 58 | 59 | CONFIG_ACPI_PCI_SLOT=y |
| 59 | 60 | CONFIG_ACPI_HOTPLUG_MEMORY=y |
| 60 | 61 | CONFIG_EFI_ZBOOT=y |
| @@ -73,17 +74,19 @@ CONFIG_UNIXWARE_DISKLABEL=y · 12 unmodified lines | ||
| 73 | 74 | CONFIG_IOSCHED_BFQ=y |
| 74 | 75 | CONFIG_BFQ_GROUP_IOSCHED=y |
| 75 | 76 | 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 | |
| 81 | 78 | CONFIG_ZSWAP=y |
| 82 | 79 | CONFIG_ZSWAP_COMPRESSOR_DEFAULT_ZSTD=y |
| 83 | −CONFIG_ZPOOL=y | |
| 84 | 80 | CONFIG_ZBUD=y |
| 85 | 81 | CONFIG_Z3FOLD=y |
| 86 | 82 | 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 | |
| 87 | 90 | CONFIG_NET=y |
| 88 | 91 | CONFIG_PACKET=y |
| 89 | 92 | CONFIG_UNIX=y |
| @@ -118,7 +121,6 @@ CONFIG_NETFILTER=y · 28 unmodified lines | ||
| 118 | 121 | CONFIG_BRIDGE_NETFILTER=m |
| 119 | 122 | CONFIG_NETFILTER_NETLINK_LOG=m |
| 120 | 123 | CONFIG_NF_CONNTRACK=m |
| 121 | −CONFIG_NF_LOG_NETDEV=m | |
| 122 | 124 | CONFIG_NF_CONNTRACK_AMANDA=m |
| 123 | 125 | CONFIG_NF_CONNTRACK_FTP=m |
| 124 | 126 | CONFIG_NF_CONNTRACK_NETBIOS_NS=m |
| @@ -416,6 +418,7 @@ CONFIG_SCSI_VIRTIO=m · 291 unmodified lines | ||
| 416 | 418 | CONFIG_ATA=y |
| 417 | 419 | CONFIG_SATA_AHCI=y |
| 418 | 420 | CONFIG_SATA_AHCI_PLATFORM=y |
| 421 | +CONFIG_AHCI_DWC=y | |
| 419 | 422 | CONFIG_PATA_ATIIXP=y |
| 420 | 423 | CONFIG_PATA_PCMCIA=m |
| 421 | 424 | CONFIG_MD=y |
| @@ -469,13 +472,11 @@ CONFIG_VIRTIO_NET=m · 47 unmodified lines | ||
| 469 | 472 | # CONFIG_NET_VENDOR_ARC is not set |
| 470 | 473 | # CONFIG_NET_VENDOR_ATHEROS is not set |
| 471 | 474 | CONFIG_BNX2=y |
| 472 | −# CONFIG_NET_VENDOR_BROCADE is not set | |
| 473 | 475 | # CONFIG_NET_VENDOR_CAVIUM is not set |
| 474 | 476 | CONFIG_CHELSIO_T1=m |
| 475 | 477 | CONFIG_CHELSIO_T1_1G=y |
| 476 | 478 | CONFIG_CHELSIO_T3=m |
| 477 | 479 | CONFIG_CHELSIO_T4=m |
| 478 | −# CONFIG_NET_VENDOR_CIRRUS is not set | |
| 479 | 480 | # CONFIG_NET_VENDOR_CISCO is not set |
| 480 | 481 | # CONFIG_NET_VENDOR_DEC is not set |
| 481 | 482 | # CONFIG_NET_VENDOR_DLINK is not set |
| @@ -496,6 +497,7 @@ CONFIG_IXGBE=y · 14 unmodified lines | ||
| 496 | 497 | # CONFIG_NET_VENDOR_NVIDIA is not set |
| 497 | 498 | # CONFIG_NET_VENDOR_OKI is not set |
| 498 | 499 | # CONFIG_NET_VENDOR_QLOGIC is not set |
| 500 | +# CONFIG_NET_VENDOR_BROCADE is not set | |
| 499 | 501 | # CONFIG_NET_VENDOR_QUALCOMM is not set |
| 500 | 502 | # CONFIG_NET_VENDOR_RDC is not set |
| 501 | 503 | CONFIG_8139CP=m |
| @@ -505,9 +507,9 @@ CONFIG_R8169=y · 3 unmodified lines | ||
| 505 | 507 | # CONFIG_NET_VENDOR_ROCKER is not set |
| 506 | 508 | # CONFIG_NET_VENDOR_SAMSUNG is not set |
| 507 | 509 | # CONFIG_NET_VENDOR_SEEQ is not set |
| 508 | −# CONFIG_NET_VENDOR_SOLARFLARE is not set | |
| 509 | 510 | # CONFIG_NET_VENDOR_SILAN is not set |
| 510 | 511 | # CONFIG_NET_VENDOR_SIS is not set |
| 512 | +# CONFIG_NET_VENDOR_SOLARFLARE is not set | |
| 511 | 513 | # CONFIG_NET_VENDOR_SMSC is not set |
| 512 | 514 | CONFIG_STMMAC_ETH=y |
| 513 | 515 | # CONFIG_NET_VENDOR_SUN is not set |
| @@ -588,6 +590,7 @@ CONFIG_SERIAL_8250_EXTENDED=y · 74 unmodified lines | ||
| 588 | 590 | CONFIG_SERIAL_8250_MANY_PORTS=y |
| 589 | 591 | CONFIG_SERIAL_8250_SHARE_IRQ=y |
| 590 | 592 | CONFIG_SERIAL_8250_RSA=y |
| 593 | +CONFIG_SERIAL_OF_PLATFORM=y | |
| 591 | 594 | CONFIG_SERIAL_NONSTANDARD=y |
| 592 | 595 | CONFIG_PRINTER=m |
| 593 | 596 | CONFIG_VIRTIO_CONSOLE=y |
| @@ -602,6 +605,11 @@ CONFIG_I2C_GPIO=y · 8 unmodified lines | ||
| 602 | 605 | CONFIG_SPI=y |
| 603 | 606 | CONFIG_GPIO_SYSFS=y |
| 604 | 607 | 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 | |
| 605 | 613 | CONFIG_SENSORS_LM75=m |
| 606 | 614 | CONFIG_SENSORS_LM93=m |
| 607 | 615 | CONFIG_SENSORS_W83795=m |
| @@ -609,16 +617,16 @@ CONFIG_SENSORS_W83627HF=m · 1 unmodified line | ||
| 609 | 617 | CONFIG_RC_CORE=m |
| 610 | 618 | CONFIG_LIRC=y |
| 611 | 619 | CONFIG_RC_DECODERS=y |
| 620 | +CONFIG_IR_IMON_DECODER=m | |
| 621 | +CONFIG_IR_JVC_DECODER=m | |
| 622 | +CONFIG_IR_MCE_KBD_DECODER=m | |
| 612 | 623 | CONFIG_IR_NEC_DECODER=m |
| 613 | 624 | CONFIG_IR_RC5_DECODER=m |
| 614 | 625 | CONFIG_IR_RC6_DECODER=m |
| 615 | −CONFIG_IR_JVC_DECODER=m | |
| 616 | −CONFIG_IR_SONY_DECODER=m | |
| 617 | 626 | CONFIG_IR_SANYO_DECODER=m |
| 618 | 627 | CONFIG_IR_SHARP_DECODER=m |
| 619 | −CONFIG_IR_MCE_KBD_DECODER=m | |
| 628 | +CONFIG_IR_SONY_DECODER=m | |
| 620 | 629 | CONFIG_IR_XMP_DECODER=m |
| 621 | −CONFIG_IR_IMON_DECODER=m | |
| 622 | 630 | CONFIG_MEDIA_SUPPORT=m |
| 623 | 631 | CONFIG_MEDIA_USB_SUPPORT=y |
| 624 | 632 | CONFIG_USB_VIDEO_CLASS=m |
| @@ -638,6 +646,7 @@ CONFIG_DRM_VIRTIO_GPU=m · 13 unmodified lines | ||
| 638 | 646 | CONFIG_FB=y |
| 639 | 647 | CONFIG_FB_EFI=y |
| 640 | 648 | CONFIG_FB_RADEON=y |
| 649 | +CONFIG_LCD_CLASS_DEVICE=y | |
| 641 | 650 | CONFIG_LCD_PLATFORM=m |
| 642 | 651 | # CONFIG_VGA_CONSOLE is not set |
| 643 | 652 | CONFIG_FRAMEBUFFER_CONSOLE=y |
| @@ -647,7 +656,6 @@ CONFIG_SOUND=y · 3 unmodified lines | ||
| 647 | 656 | CONFIG_SND=y |
| 648 | 657 | CONFIG_SND_SEQUENCER=m |
| 649 | 658 | CONFIG_SND_SEQ_DUMMY=m |
| 650 | −# CONFIG_SND_ISA is not set | |
| 651 | 659 | CONFIG_SND_BT87X=m |
| 652 | 660 | CONFIG_SND_BT87X_OVERCLOCK=y |
| 653 | 661 | CONFIG_SND_HDA_INTEL=y |
| @@ -818,10 +826,6 @@ CONFIG_CRYPTO_USER=m · 164 unmodified lines | ||
| 818 | 826 | # CONFIG_CRYPTO_MANAGER_DISABLE_TESTS is not set |
| 819 | 827 | CONFIG_CRYPTO_PCRYPT=m |
| 820 | 828 | CONFIG_CRYPTO_CRYPTD=m |
| 821 | −CONFIG_CRYPTO_CHACHA20POLY1305=m | |
| 822 | −CONFIG_CRYPTO_HMAC=y | |
| 823 | −CONFIG_CRYPTO_VMAC=m | |
| 824 | −CONFIG_CRYPTO_WP512=m | |
| 825 | 829 | CONFIG_CRYPTO_ANUBIS=m |
| 826 | 830 | CONFIG_CRYPTO_BLOWFISH=m |
| 827 | 831 | CONFIG_CRYPTO_CAST5=m |
| @@ -831,6 +835,9 @@ CONFIG_CRYPTO_SEED=m · 3 unmodified lines | ||
| 831 | 835 | CONFIG_CRYPTO_SERPENT=m |
| 832 | 836 | CONFIG_CRYPTO_TEA=m |
| 833 | 837 | CONFIG_CRYPTO_TWOFISH=m |
| 838 | +CONFIG_CRYPTO_CHACHA20POLY1305=m | |
| 839 | +CONFIG_CRYPTO_VMAC=m | |
| 840 | +CONFIG_CRYPTO_WP512=m | |
| 834 | 841 | CONFIG_CRYPTO_DEFLATE=m |
| 835 | 842 | CONFIG_CRYPTO_LZO=m |
| 836 | 843 | CONFIG_CRYPTO_842=m |
| @@ -844,6 +851,7 @@ CONFIG_CRYPTO_DEV_VIRTIO=m · 7 unmodified lines | ||
| 844 | 851 | CONFIG_PRINTK_TIME=y |
| 845 | 852 | CONFIG_STRIP_ASM_SYMS=y |
| 846 | 853 | CONFIG_MAGIC_SYSRQ=y |
| 854 | +CONFIG_DEBUG_FS=y | |
| 847 | 855 | # CONFIG_SCHED_DEBUG is not set |
| 848 | 856 | CONFIG_SCHEDSTATS=y |
| 849 | 857 | # CONFIG_DEBUG_PREEMPT is not set |
| @@ -35,4 +35,14 @@ extern struct list_head acpi_wakeup_device_list; · 34 unmodified lines | ||
| 35 | 35 | |
| 36 | 36 | #define ACPI_TABLE_UPGRADE_MAX_PHYS ARCH_LOW_ADDRESS_LIMIT |
| 37 | 37 | |
| 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 | + | |
| 38 | 48 | #endif /* _ASM_LOONGARCH_ACPI_H */ |
| @@ -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 */ | |
| @@ -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 */ | |
| @@ -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 */ | |
| @@ -32,6 +32,7 @@ struct loongson_system_configuration { · 31 unmodified lines | ||
| 32 | 32 | int cores_per_node; |
| 33 | 33 | int cores_per_package; |
| 34 | 34 | unsigned long cores_io_master; |
| 35 | + unsigned long suspend_addr; | |
| 35 | 36 | const char *cpuname; |
| 36 | 37 | }; |
| 37 | 38 | |
| @@ -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 */ | |
| @@ -9,6 +9,7 @@ · 8 unmodified lines | ||
| 9 | 9 | |
| 10 | 10 | void __init efi_init(void); |
| 11 | 11 | void __init efi_runtime_init(void); |
| 12 | +void __init *efi_fdt_pointer(void); | |
| 12 | 13 | void efifb_setup_from_dmi(struct screen_info *si, const char *opt); |
| 13 | 14 | |
| 14 | 15 | #define ARCH_EFI_IRQ_FLAGS_MASK 0x00000004 /* Bit 2: CSR.CRMD.IE */ |
| @@ -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 | |
| @@ -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 */ | |
| @@ -7,6 +7,7 @@ · 6 unmodified lines | ||
| 7 | 7 | |
| 8 | 8 | #include <linux/futex.h> |
| 9 | 9 | #include <linux/uaccess.h> |
| 10 | +#include <asm/asm-extable.h> | |
| 10 | 11 | #include <asm/barrier.h> |
| 11 | 12 | #include <asm/errno.h> |
| 12 | 13 | |
| @@ -18,18 +19,11 @@ · 5 unmodified lines | ||
| 18 | 19 | "2: sc.w $t0, %2 \n" \ |
| 19 | 20 | " beqz $t0, 1b \n" \ |
| 20 | 21 | "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) \ | |
| 29 | 24 | : "=r" (ret), "=&r" (oldval), \ |
| 30 | 25 | "=ZC" (*uaddr) \ |
| 31 | − : "0" (0), "ZC" (*uaddr), "Jr" (oparg), \ | |
| 32 | − "i" (-EFAULT) \ | |
| 26 | + : "0" (0), "ZC" (*uaddr), "Jr" (oparg) \ | |
| 33 | 27 | : "memory", "t0"); \ |
| 34 | 28 | } |
| 35 | 29 | |
| @@ -86,17 +80,10 @@ futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32 newv · 50 unmodified lines | ||
| 86 | 80 | " beqz $t0, 1b \n" |
| 87 | 81 | "3: \n" |
| 88 | 82 | __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) | |
| 97 | 85 | : "+r" (ret), "=&r" (val), "=ZC" (*uaddr) |
| 98 | − : "ZC" (*uaddr), "Jr" (oldval), "Jr" (newval), | |
| 99 | − "i" (-EFAULT) | |
| 86 | + : "ZC" (*uaddr), "Jr" (oldval), "Jr" (newval) | |
| 100 | 87 | : "memory", "t0"); |
| 101 | 88 | |
| 102 | 89 | *uval = val; |
| @@ -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 */ | |
| @@ -8,14 +8,17 @@ · 7 unmodified lines | ||
| 8 | 8 | #include <linux/types.h> |
| 9 | 9 | #include <asm/asm.h> |
| 10 | 10 | |
| 11 | +#define INSN_NOP 0x03400000 | |
| 11 | 12 | #define INSN_BREAK 0x002a0000 |
| 12 | 13 | |
| 13 | 14 | #define ADDR_IMMMASK_LU52ID 0xFFF0000000000000 |
| 14 | 15 | #define ADDR_IMMMASK_LU32ID 0x000FFFFF00000000 |
| 16 | +#define ADDR_IMMMASK_LU12IW 0x00000000FFFFF000 | |
| 15 | 17 | #define ADDR_IMMMASK_ADDU16ID 0x00000000FFFF0000 |
| 16 | 18 | |
| 17 | 19 | #define ADDR_IMMSHIFT_LU52ID 52 |
| 18 | 20 | #define ADDR_IMMSHIFT_LU32ID 32 |
| 21 | +#define ADDR_IMMSHIFT_LU12IW 12 | |
| 19 | 22 | #define ADDR_IMMSHIFT_ADDU16ID 16 |
| 20 | 23 | |
| 21 | 24 | #define ADDR_IMM(addr, INSN) ((addr & ADDR_IMMMASK_##INSN) >> ADDR_IMMSHIFT_##INSN) |
| @@ -28,6 +31,7 @@ enum reg0i26_op { · 6 unmodified lines | ||
| 28 | 31 | enum reg1i20_op { |
| 29 | 32 | lu12iw_op = 0x0a, |
| 30 | 33 | lu32id_op = 0x0b, |
| 34 | + pcaddi_op = 0x0c, | |
| 31 | 35 | pcaddu12i_op = 0x0e, |
| 32 | 36 | pcaddu18i_op = 0x0f, |
| 33 | 37 | }; |
| @@ -35,6 +39,8 @@ enum reg1i20_op { · 1 unmodified line | ||
| 35 | 39 | enum reg1i21_op { |
| 36 | 40 | beqz_op = 0x10, |
| 37 | 41 | bnez_op = 0x11, |
| 42 | + bceqz_op = 0x12, /* bits[9:8] = 0x00 */ | |
| 43 | + bcnez_op = 0x12, /* bits[9:8] = 0x01 */ | |
| 38 | 44 | }; |
| 39 | 45 | |
| 40 | 46 | enum reg2_op { |
| @@ -76,6 +82,10 @@ enum reg2i12_op { · 35 unmodified lines | ||
| 76 | 82 | ldbu_op = 0xa8, |
| 77 | 83 | ldhu_op = 0xa9, |
| 78 | 84 | ldwu_op = 0xaa, |
| 85 | + flds_op = 0xac, | |
| 86 | + fsts_op = 0xad, | |
| 87 | + fldd_op = 0xae, | |
| 88 | + fstd_op = 0xaf, | |
| 79 | 89 | }; |
| 80 | 90 | |
| 81 | 91 | enum reg2i14_op { |
| @@ -146,6 +156,10 @@ enum reg3_op { · 64 unmodified lines | ||
| 146 | 156 | ldxbu_op = 0x7040, |
| 147 | 157 | ldxhu_op = 0x7048, |
| 148 | 158 | ldxwu_op = 0x7050, |
| 159 | + fldxs_op = 0x7060, | |
| 160 | + fldxd_op = 0x7068, | |
| 161 | + fstxs_op = 0x7070, | |
| 162 | + fstxd_op = 0x7078, | |
| 149 | 163 | amswapw_op = 0x70c0, |
| 150 | 164 | amswapd_op = 0x70c1, |
| 151 | 165 | amaddw_op = 0x70c2, |
| @@ -307,6 +321,12 @@ static inline bool is_imm_negative(unsigned long val, unsigned int bit) · 155 unmodified lines | ||
| 307 | 321 | return val & (1UL << (bit - 1)); |
| 308 | 322 | } |
| 309 | 323 | |
| 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 | + | |
| 310 | 330 | static inline bool is_branch_ins(union loongarch_instruction *ip) |
| 311 | 331 | { |
| 312 | 332 | 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 | ||
| 331 | 351 | is_imm12_negative(ip->reg2i12_format.immediate); |
| 332 | 352 | } |
| 333 | 353 | |
| 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); | |
| 334 | 366 | u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm); |
| 335 | 367 | u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm); |
| 336 | 368 | 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 | ||
| 345 | 377 | return val < (1UL << bit); |
| 346 | 378 | } |
| 347 | 379 | |
| 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 | + | |
| 348 | 388 | #define DEF_EMIT_REG0I26_FORMAT(NAME, OP) \ |
| 349 | 389 | static inline void emit_##NAME(union loongarch_instruction *insn, \ |
| 350 | 390 | int offset) \ |
| @@ -566,4 +606,10 @@ static inline void emit_##NAME(union loongarch_instruction *insn, \ · 215 unmodified lines | ||
| 566 | 606 | |
| 567 | 607 | DEF_EMIT_REG3SA2_FORMAT(alsld, alsld_op) |
| 568 | 608 | |
| 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 | + | |
| 569 | 615 | #endif /* _ASM_INST_H */ |
| @@ -136,4 +136,7 @@ typedef enum { · 135 unmodified lines | ||
| 136 | 136 | #define ls7a_writel(val, addr) *(volatile unsigned int *)TO_UNCACHE(addr) = (val) |
| 137 | 137 | #define ls7a_writeq(val, addr) *(volatile unsigned long *)TO_UNCACHE(addr) = (val) |
| 138 | 138 | |
| 139 | +void enable_gpe_wakeup(void); | |
| 140 | +void enable_pci_wakeup(void); | |
| 141 | + | |
| 139 | 142 | #endif /* __ASM_LOONGSON_H */ |
| @@ -11,7 +11,7 @@ · 10 unmodified lines | ||
| 11 | 11 | #define RELA_STACK_DEPTH 16 |
| 12 | 12 | |
| 13 | 13 | struct mod_section { |
| 14 | − Elf_Shdr *shdr; | |
| 14 | + int shndx; | |
| 15 | 15 | int num_entries; |
| 16 | 16 | int max_entries; |
| 17 | 17 | }; |
| @@ -20,6 +20,9 @@ struct mod_arch_specific { · 2 unmodified lines | ||
| 20 | 20 | struct mod_section got; |
| 21 | 21 | struct mod_section plt; |
| 22 | 22 | struct mod_section plt_idx; |
| 23 | + | |
| 24 | + /* For CONFIG_DYNAMIC_FTRACE */ | |
| 25 | + struct plt_entry *ftrace_trampolines; | |
| 23 | 26 | }; |
| 24 | 27 | |
| 25 | 28 | struct got_entry { |
| @@ -37,8 +40,8 @@ struct plt_idx_entry { · 11 unmodified lines | ||
| 37 | 40 | Elf_Addr symbol_addr; |
| 38 | 41 | }; |
| 39 | 42 | |
| 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); | |
| 42 | 45 | |
| 43 | 46 | static inline struct got_entry emit_got_entry(Elf_Addr val) |
| 44 | 47 | { |
| @@ -49,7 +52,7 @@ static inline struct plt_entry emit_plt_entry(unsigned long val) · 4 unmodified lines | ||
| 49 | 52 | { |
| 50 | 53 | u32 lu12iw, lu32id, lu52id, jirl; |
| 51 | 54 | |
| 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)); | |
| 53 | 56 | lu32id = larch_insn_gen_lu32id(LOONGARCH_GPR_T1, ADDR_IMM(val, LU32ID)); |
| 54 | 57 | lu52id = larch_insn_gen_lu52id(LOONGARCH_GPR_T1, LOONGARCH_GPR_T1, ADDR_IMM(val, LU52ID)); |
| 55 | 58 | 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 | ||
| 62 | 65 | return (struct plt_idx_entry) { val }; |
| 63 | 66 | } |
| 64 | 67 | |
| 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) | |
| 66 | 69 | { |
| 67 | 70 | 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; | |
| 69 | 72 | |
| 70 | 73 | for (i = 0; i < sec->num_entries; i++) { |
| 71 | 74 | 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 | ||
| 76 | 79 | } |
| 77 | 80 | |
| 78 | 81 | 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) | |
| 81 | 85 | { |
| 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; | |
| 84 | 88 | |
| 85 | 89 | if (plt_idx < 0) |
| 86 | 90 | return NULL; |
| @@ -89,10 +93,11 @@ static inline struct plt_entry *get_plt_entry(unsigned long val, · 2 unmodified lines | ||
| 89 | 93 | } |
| 90 | 94 | |
| 91 | 95 | static inline struct got_entry *get_got_entry(Elf_Addr val, |
| 96 | + Elf_Shdr *sechdrs, | |
| 92 | 97 | const struct mod_section *sec) |
| 93 | 98 | { |
| 94 | − struct got_entry *got = (struct got_entry *)sec->shdr->sh_addr; | |
| 95 | 99 | int i; |
| 100 | + struct got_entry *got = (struct got_entry *)sechdrs[sec->shndx].sh_addr; | |
| 96 | 101 | |
| 97 | 102 | for (i = 0; i < sec->num_entries; i++) |
| 98 | 103 | if (got[i].symbol_addr == val) |
| @@ -5,4 +5,5 @@ SECTIONS { · 4 unmodified lines | ||
| 5 | 5 | .got : { BYTE(0) } |
| 6 | 6 | .plt : { BYTE(0) } |
| 7 | 7 | .plt.idx : { BYTE(0) } |
| 8 | + .ftrace_trampoline : { BYTE(0) } | |
| 8 | 9 | } |
| @@ -13,6 +13,7 @@ · 12 unmodified lines | ||
| 13 | 13 | |
| 14 | 14 | extern unsigned long eentry; |
| 15 | 15 | extern unsigned long tlbrentry; |
| 16 | +extern char init_command_line[COMMAND_LINE_SIZE]; | |
| 16 | 17 | extern void tlb_init(int cpu); |
| 17 | 18 | extern void cpu_cache_init(void); |
| 18 | 19 | extern void cache_error_setup(void); |
| @@ -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 */ | |
| @@ -5,8 +5,13 @@ · 4 unmodified lines | ||
| 5 | 5 | #ifndef _ASM_STRING_H |
| 6 | 6 | #define _ASM_STRING_H |
| 7 | 7 | |
| 8 | +#define __HAVE_ARCH_MEMSET | |
| 8 | 9 | extern void *memset(void *__s, int __c, size_t __count); |
| 10 | + | |
| 11 | +#define __HAVE_ARCH_MEMCPY | |
| 9 | 12 | extern void *memcpy(void *__to, __const__ void *__from, size_t __n); |
| 13 | + | |
| 14 | +#define __HAVE_ARCH_MEMMOVE | |
| 10 | 15 | extern void *memmove(void *__dest, __const__ void *__src, size_t __n); |
| 11 | 16 | |
| 12 | 17 | #endif /* _ASM_STRING_H */ |
| @@ -38,7 +38,7 @@ struct thread_info { · 37 unmodified lines | ||
| 38 | 38 | #define INIT_THREAD_INFO(tsk) \ |
| 39 | 39 | { \ |
| 40 | 40 | .task = &tsk, \ |
| 41 | − .flags = 0, \ | |
| 41 | + .flags = _TIF_FIXADE, \ | |
| 42 | 42 | .cpu = 0, \ |
| 43 | 43 | .preempt_count = INIT_PREEMPT_COUNT, \ |
| 44 | 44 | } |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | ||
| 12 | 12 | extern u64 cpu_clock_freq; |
| 13 | 13 | extern u64 const_clock_freq; |
| 14 | 14 | |
| 15 | +extern void save_counter(void); | |
| 15 | 16 | extern void sync_counter(void); |
| 16 | 17 | |
| 17 | 18 | static inline unsigned int calc_const_freq(void) |
| @@ -15,7 +15,8 @@ · 14 unmodified lines | ||
| 15 | 15 | #include <linux/string.h> |
| 16 | 16 | #include <linux/extable.h> |
| 17 | 17 | #include <asm/pgtable.h> |
| 18 | −#include <asm-generic/extable.h> | |
| 18 | +#include <asm/extable.h> | |
| 19 | +#include <asm/asm-extable.h> | |
| 19 | 20 | #include <asm-generic/access_ok.h> |
| 20 | 21 | |
| 21 | 22 | extern u64 __ua_limit; |
| @@ -160,16 +161,9 @@ do { \ · 138 unmodified lines | ||
| 160 | 161 | __asm__ __volatile__( \ |
| 161 | 162 | "1: " insn " %1, %2 \n" \ |
| 162 | 163 | "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) \ | |
| 171 | 165 | : "+r" (__gu_err), "=r" (__gu_tmp) \ |
| 172 | − : "m" (__m(ptr)), "i" (-EFAULT)); \ | |
| 166 | + : "m" (__m(ptr))); \ | |
| 173 | 167 | \ |
| 174 | 168 | (val) = (__typeof__(*(ptr))) __gu_tmp; \ |
| 175 | 169 | } |
| @@ -192,15 +186,9 @@ do { \ · 16 unmodified lines | ||
| 192 | 186 | __asm__ __volatile__( \ |
| 193 | 187 | "1: " insn " %z2, %1 # __put_user_asm\n" \ |
| 194 | 188 | "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) \ | |
| 202 | 190 | : "+r" (__pu_err), "=m" (__m(ptr)) \ |
| 203 | − : "Jr" (__pu_val), "i" (-EFAULT)); \ | |
| 191 | + : "Jr" (__pu_val)); \ | |
| 204 | 192 | } |
| 205 | 193 | |
| 206 | 194 | #define __get_kernel_nofault(dst, src, type, err_label) \ |
| @@ -20,7 +20,8 @@ struct unwind_state { · 19 unmodified lines | ||
| 20 | 20 | char type; /* UNWINDER_XXX */ |
| 21 | 21 | struct stack_info stack_info; |
| 22 | 22 | struct task_struct *task; |
| 23 | − bool first, error; | |
| 23 | + bool first, error, is_ftrace; | |
| 24 | + int graph_idx; | |
| 24 | 25 | unsigned long sp, pc, ra; |
| 25 | 26 | }; |
| 26 | 27 | |
| @@ -7,13 +7,27 @@ extra-y := vmlinux.lds · 6 unmodified lines | ||
| 7 | 7 | |
| 8 | 8 | obj-y += head.o cpu-probe.o cacheinfo.o env.o setup.o entry.o genex.o \ |
| 9 | 9 | 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 | |
| 11 | 12 | |
| 12 | 13 | obj-$(CONFIG_ACPI) += acpi.o |
| 13 | 14 | obj-$(CONFIG_EFI) += efi.o |
| 14 | 15 | |
| 15 | 16 | obj-$(CONFIG_CPU_HAS_FPU) += fpu.o |
| 16 | 17 | |
| 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 | + | |
| 17 | 31 | obj-$(CONFIG_MODULES) += module.o module-sections.o |
| 18 | 32 | obj-$(CONFIG_STACKTRACE) += stacktrace.o |
| 19 | 33 | |
| @@ -12,6 +12,7 @@ · 11 unmodified lines | ||
| 12 | 12 | #include <linux/irq.h> |
| 13 | 13 | #include <linux/irqdomain.h> |
| 14 | 14 | #include <linux/memblock.h> |
| 15 | +#include <linux/of_fdt.h> | |
| 15 | 16 | #include <linux/serial_core.h> |
| 16 | 17 | #include <asm/io.h> |
| 17 | 18 | #include <asm/numa.h> |
| @@ -139,20 +140,26 @@ static void __init acpi_process_madt(void) · 121 unmodified lines | ||
| 139 | 140 | loongson_sysconf.nr_cpus = num_processors; |
| 140 | 141 | } |
| 141 | 142 | |
| 143 | +#ifndef CONFIG_SUSPEND | |
| 144 | +int (*acpi_suspend_lowlevel)(void); | |
| 145 | +#else | |
| 146 | +int (*acpi_suspend_lowlevel)(void) = loongarch_acpi_suspend; | |
| 147 | +#endif | |
| 148 | + | |
| 142 | 149 | void __init acpi_boot_table_init(void) |
| 143 | 150 | { |
| 144 | 151 | /* |
| 145 | 152 | * If acpi_disabled, bail out |
| 146 | 153 | */ |
| 147 | 154 | if (acpi_disabled) |
| 148 | − return; | |
| 155 | + goto fdt_earlycon; | |
| 149 | 156 | |
| 150 | 157 | /* |
| 151 | 158 | * Initialize the ACPI boot-time table parser. |
| 152 | 159 | */ |
| 153 | 160 | if (acpi_table_init()) { |
| 154 | 161 | disable_acpi(); |
| 155 | − return; | |
| 162 | + goto fdt_earlycon; | |
| 156 | 163 | } |
| 157 | 164 | |
| 158 | 165 | loongson_sysconf.boot_cpu_id = read_csr_cpuid(); |
| @@ -164,6 +171,12 @@ void __init acpi_boot_table_init(void) · 5 unmodified lines | ||
| 164 | 171 | |
| 165 | 172 | /* Do not enable ACPI SPCR console by default */ |
| 166 | 173 | 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(); | |
| 167 | 180 | } |
| 168 | 181 | |
| 169 | 182 | #ifdef CONFIG_ACPI_NUMA |
| @@ -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 | +} | |
| @@ -68,6 +68,9 @@ void output_task_defines(void) · 67 unmodified lines | ||
| 68 | 68 | OFFSET(TASK_FLAGS, task_struct, flags); |
| 69 | 69 | OFFSET(TASK_MM, task_struct, mm); |
| 70 | 70 | OFFSET(TASK_PID, task_struct, pid); |
| 71 | +#if defined(CONFIG_STACKPROTECTOR) | |
| 72 | + OFFSET(TASK_STACK_CANARY, task_struct, stack_canary); | |
| 73 | +#endif | |
| 71 | 74 | DEFINE(TASK_STRUCT_SIZE, sizeof(struct task_struct)); |
| 72 | 75 | BLANK(); |
| 73 | 76 | } |
| @@ -257,3 +260,15 @@ void output_smpboot_defines(void) · 183 unmodified lines | ||
| 257 | 260 | BLANK(); |
| 258 | 261 | } |
| 259 | 262 | #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 | |
| @@ -28,16 +28,29 @@ static unsigned long efi_nr_tables; · 27 unmodified lines | ||
| 28 | 28 | static unsigned long efi_config_table; |
| 29 | 29 | |
| 30 | 30 | static unsigned long __initdata boot_memmap = EFI_INVALID_TABLE_ADDR; |
| 31 | +static unsigned long __initdata fdt_pointer = EFI_INVALID_TABLE_ADDR; | |
| 31 | 32 | |
| 32 | 33 | static efi_system_table_t *efi_systab; |
| 33 | 34 | static efi_config_table_type_t arch_tables[] __initdata = { |
| 34 | 35 | {LINUX_EFI_BOOT_MEMMAP_GUID, &boot_memmap, "MEMMAP" }, |
| 36 | + {DEVICE_TREE_GUID, &fdt_pointer, "FDTPTR" }, | |
| 35 | 37 | {}, |
| 36 | 38 | }; |
| 37 | 39 | |
| 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 | + | |
| 38 | 51 | void __init efi_runtime_init(void) |
| 39 | 52 | { |
| 40 | − if (!efi_enabled(EFI_BOOT)) | |
| 53 | + if (!efi_enabled(EFI_BOOT) || !efi_systab->runtime) | |
| 41 | 54 | return; |
| 42 | 55 | |
| 43 | 56 | if (efi_runtime_disabled()) { |
| @@ -11,6 +11,7 @@ · 10 unmodified lines | ||
| 11 | 11 | #include <asm/early_ioremap.h> |
| 12 | 12 | #include <asm/bootinfo.h> |
| 13 | 13 | #include <asm/loongson.h> |
| 14 | +#include <asm/setup.h> | |
| 14 | 15 | |
| 15 | 16 | u64 efi_system_table; |
| 16 | 17 | struct loongson_system_configuration loongson_sysconf; |
| @@ -27,6 +28,7 @@ void __init init_environ(void) · 10 unmodified lines | ||
| 27 | 28 | clear_bit(EFI_BOOT, &efi.flags); |
| 28 | 29 | |
| 29 | 30 | strscpy(boot_command_line, cmdline, COMMAND_LINE_SIZE); |
| 31 | + strscpy(init_command_line, cmdline, COMMAND_LINE_SIZE); | |
| 30 | 32 | early_memunmap(cmdline, COMMAND_LINE_SIZE); |
| 31 | 33 | |
| 32 | 34 | efi_system_table = fw_arg2; |
| @@ -8,6 +8,7 @@ · 7 unmodified lines | ||
| 8 | 8 | */ |
| 9 | 9 | #include <asm/asm.h> |
| 10 | 10 | #include <asm/asmmacro.h> |
| 11 | +#include <asm/asm-extable.h> | |
| 11 | 12 | #include <asm/asm-offsets.h> |
| 12 | 13 | #include <asm/errno.h> |
| 13 | 14 | #include <asm/export.h> |
| @@ -21,9 +22,7 @@ · 7 unmodified lines | ||
| 21 | 22 | |
| 22 | 23 | .macro EX insn, reg, src, offs |
| 23 | 24 | .ex\@: \insn \reg, \src, \offs |
| 24 | − .section __ex_table,"a" | |
| 25 | − PTR .ex\@, fault | |
| 26 | − .previous | |
| 25 | + _asm_extable .ex\@, fault | |
| 27 | 26 | .endm |
| 28 | 27 | |
| 29 | 28 | .macro sc_save_fp base |
| @@ -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(¤t->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 */ | |
| @@ -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(¤t->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 *)®s->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 */ | |
| @@ -2,8 +2,135 @@ · 1 unmodified line | ||
| 2 | 2 | /* |
| 3 | 3 | * Copyright (C) 2020-2022 Loongson Technology Corporation Limited |
| 4 | 4 | */ |
| 5 | +#include <linux/sizes.h> | |
| 6 | +#include <linux/uaccess.h> | |
| 7 | + | |
| 8 | +#include <asm/cacheflush.h> | |
| 5 | 9 | #include <asm/inst.h> |
| 6 | 10 | |
| 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 | + | |
| 7 | 134 | u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm) |
| 8 | 135 | { |
| 9 | 136 | union loongarch_instruction insn; |
| @@ -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 */ | |
| @@ -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 | |
| @@ -6,18 +6,19 @@ · 5 unmodified lines | ||
| 6 | 6 | #include <linux/elf.h> |
| 7 | 7 | #include <linux/kernel.h> |
| 8 | 8 | #include <linux/module.h> |
| 9 | +#include <linux/ftrace.h> | |
| 9 | 10 | |
| 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) | |
| 11 | 12 | { |
| 12 | 13 | struct mod_section *got_sec = &mod->arch.got; |
| 13 | 14 | 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); | |
| 15 | 16 | |
| 16 | 17 | if (got) |
| 17 | 18 | return (Elf_Addr)got; |
| 18 | 19 | |
| 19 | 20 | /* 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; | |
| 21 | 22 | got[i] = emit_got_entry(val); |
| 22 | 23 | |
| 23 | 24 | got_sec->num_entries++; |
| @@ -33,12 +34,12 @@ Elf_Addr module_emit_got_entry(struct module *mod, Elf_Addr val) · 9 unmodified lines | ||
| 33 | 34 | return (Elf_Addr)&got[i]; |
| 34 | 35 | } |
| 35 | 36 | |
| 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) | |
| 37 | 38 | { |
| 38 | 39 | int nr; |
| 39 | 40 | struct mod_section *plt_sec = &mod->arch.plt; |
| 40 | 41 | 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); | |
| 42 | 43 | struct plt_idx_entry *plt_idx; |
| 43 | 44 | |
| 44 | 45 | if (plt) |
| @@ -47,9 +48,9 @@ Elf_Addr module_emit_plt_entry(struct module *mod, Elf_Addr val) · 2 unmodified lines | ||
| 47 | 48 | nr = plt_sec->num_entries; |
| 48 | 49 | |
| 49 | 50 | /* 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; | |
| 51 | 52 | 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; | |
| 53 | 54 | plt_idx[nr] = emit_plt_idx_entry(val); |
| 54 | 55 | |
| 55 | 56 | plt_sec->num_entries++; |
| @@ -103,28 +104,31 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, · 47 unmodified lines | ||
| 103 | 104 | char *secstrings, struct module *mod) |
| 104 | 105 | { |
| 105 | 106 | unsigned int i, num_plts = 0, num_gots = 0; |
| 107 | + Elf_Shdr *got_sec, *plt_sec, *plt_idx_sec, *tramp = NULL; | |
| 106 | 108 | |
| 107 | 109 | /* |
| 108 | 110 | * Find the empty .plt sections. |
| 109 | 111 | */ |
| 110 | 112 | for (i = 0; i < ehdr->e_shnum; i++) { |
| 111 | 113 | if (!strcmp(secstrings + sechdrs[i].sh_name, ".got")) |
| 112 | − mod->arch.got.shdr = sechdrs + i; | |
| 114 | + mod->arch.got.shndx = i; | |
| 113 | 115 | else if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt")) |
| 114 | − mod->arch.plt.shdr = sechdrs + i; | |
| 116 | + mod->arch.plt.shndx = i; | |
| 115 | 117 | 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; | |
| 117 | 121 | } |
| 118 | 122 | |
| 119 | − if (!mod->arch.got.shdr) { | |
| 123 | + if (!mod->arch.got.shndx) { | |
| 120 | 124 | pr_err("%s: module GOT section(s) missing\n", mod->name); |
| 121 | 125 | return -ENOEXEC; |
| 122 | 126 | } |
| 123 | − if (!mod->arch.plt.shdr) { | |
| 127 | + if (!mod->arch.plt.shndx) { | |
| 124 | 128 | pr_err("%s: module PLT section(s) missing\n", mod->name); |
| 125 | 129 | return -ENOEXEC; |
| 126 | 130 | } |
| 127 | − if (!mod->arch.plt_idx.shdr) { | |
| 131 | + if (!mod->arch.plt_idx.shndx) { | |
| 128 | 132 | pr_err("%s: module PLT.IDX section(s) missing\n", mod->name); |
| 129 | 133 | return -ENOEXEC; |
| 130 | 134 | } |
| @@ -145,26 +149,36 @@ int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, · 14 unmodified lines | ||
| 145 | 149 | count_max_entries(relas, num_rela, &num_plts, &num_gots); |
| 146 | 150 | } |
| 147 | 151 | |
| 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); | |
| 152 | 157 | mod->arch.got.num_entries = 0; |
| 153 | 158 | mod->arch.got.max_entries = num_gots; |
| 154 | 159 | |
| 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); | |
| 159 | 165 | mod->arch.plt.num_entries = 0; |
| 160 | 166 | mod->arch.plt.max_entries = num_plts; |
| 161 | 167 | |
| 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); | |
| 166 | 173 | mod->arch.plt_idx.num_entries = 0; |
| 167 | 174 | mod->arch.plt_idx.max_entries = num_plts; |
| 168 | 175 | |
| 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 | + | |
| 169 | 183 | return 0; |
| 170 | 184 | } |
| @@ -15,8 +15,11 @@ · 14 unmodified lines | ||
| 15 | 15 | #include <linux/vmalloc.h> |
| 16 | 16 | #include <linux/slab.h> |
| 17 | 17 | #include <linux/fs.h> |
| 18 | +#include <linux/ftrace.h> | |
| 18 | 19 | #include <linux/string.h> |
| 19 | 20 | #include <linux/kernel.h> |
| 21 | +#include <asm/alternative.h> | |
| 22 | +#include <asm/inst.h> | |
| 20 | 23 | |
| 21 | 24 | static int rela_stack_push(s64 stack_value, s64 *rela_stack, size_t *rela_stack_top) |
| 22 | 25 | { |
| @@ -98,16 +101,17 @@ static int apply_r_larch_sop_push_dup(struct module *mod, u32 *location, Elf_Add · 75 unmodified lines | ||
| 98 | 101 | return 0; |
| 99 | 102 | } |
| 100 | 103 | |
| 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, | |
| 102 | 106 | s64 *rela_stack, size_t *rela_stack_top, unsigned int type) |
| 103 | 107 | { |
| 104 | 108 | ptrdiff_t offset = (void *)v - (void *)location; |
| 105 | 109 | |
| 106 | 110 | if (offset >= SZ_128M) |
| 107 | − v = module_emit_plt_entry(mod, v); | |
| 111 | + v = module_emit_plt_entry(mod, sechdrs, v); | |
| 108 | 112 | |
| 109 | 113 | if (offset < -SZ_128M) |
| 110 | − v = module_emit_plt_entry(mod, v); | |
| 114 | + v = module_emit_plt_entry(mod, sechdrs, v); | |
| 111 | 115 | |
| 112 | 116 | return apply_r_larch_sop_push_pcrel(mod, location, v, rela_stack, rela_stack_top, type); |
| 113 | 117 | } |
| @@ -271,17 +275,18 @@ static int apply_r_larch_add_sub(struct module *mod, u32 *location, Elf_Addr v, · 157 unmodified lines | ||
| 271 | 275 | } |
| 272 | 276 | } |
| 273 | 277 | |
| 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, | |
| 275 | 280 | s64 *rela_stack, size_t *rela_stack_top, unsigned int type) |
| 276 | 281 | { |
| 277 | 282 | ptrdiff_t offset = (void *)v - (void *)location; |
| 278 | 283 | union loongarch_instruction *insn = (union loongarch_instruction *)location; |
| 279 | 284 | |
| 280 | 285 | if (offset >= SZ_128M) |
| 281 | − v = module_emit_plt_entry(mod, v); | |
| 286 | + v = module_emit_plt_entry(mod, sechdrs, v); | |
| 282 | 287 | |
| 283 | 288 | if (offset < -SZ_128M) |
| 284 | − v = module_emit_plt_entry(mod, v); | |
| 289 | + v = module_emit_plt_entry(mod, sechdrs, v); | |
| 285 | 290 | |
| 286 | 291 | offset = (void *)v - (void *)location; |
| 287 | 292 | |
| @@ -338,10 +343,11 @@ static int apply_r_larch_pcala(struct module *mod, u32 *location, Elf_Addr v, · 50 unmodified lines | ||
| 338 | 343 | return 0; |
| 339 | 344 | } |
| 340 | 345 | |
| 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, | |
| 342 | 348 | s64 *rela_stack, size_t *rela_stack_top, unsigned int type) |
| 343 | 349 | { |
| 344 | − Elf_Addr got = module_emit_got_entry(mod, v); | |
| 350 | + Elf_Addr got = module_emit_got_entry(mod, sechdrs, v); | |
| 345 | 351 | |
| 346 | 352 | if (!got) |
| 347 | 353 | return -EINVAL; |
| @@ -386,13 +392,10 @@ static reloc_rela_handler reloc_rela_handlers[] = { · 38 unmodified lines | ||
| 386 | 392 | [R_LARCH_SOP_PUSH_PCREL] = apply_r_larch_sop_push_pcrel, |
| 387 | 393 | [R_LARCH_SOP_PUSH_ABSOLUTE] = apply_r_larch_sop_push_absolute, |
| 388 | 394 | [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, | |
| 390 | 395 | [R_LARCH_SOP_SUB ... R_LARCH_SOP_IF_ELSE] = apply_r_larch_sop, |
| 391 | 396 | [R_LARCH_SOP_POP_32_S_10_5 ... R_LARCH_SOP_POP_32_U] = apply_r_larch_sop_imm_field, |
| 392 | 397 | [R_LARCH_ADD32 ... R_LARCH_SUB64] = apply_r_larch_add_sub, |
| 393 | − [R_LARCH_B26] = apply_r_larch_b26, | |
| 394 | 398 | [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, | |
| 396 | 399 | }; |
| 397 | 400 | |
| 398 | 401 | 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 | ||
| 443 | 446 | sym->st_value, rel[i].r_addend, (u64)location); |
| 444 | 447 | |
| 445 | 448 | 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 | + } | |
| 447 | 465 | if (err) |
| 448 | 466 | return err; |
| 449 | 467 | } |
| @@ -456,3 +474,36 @@ void *module_alloc(unsigned long size) · 6 unmodified lines | ||
| 456 | 474 | return __vmalloc_node_range(size, 1, MODULES_VADDR, MODULES_END, |
| 457 | 475 | GFP_KERNEL, PAGE_KERNEL, 0, NUMA_NO_NODE, __builtin_return_address(0)); |
| 458 | 476 | } |
| 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 | +} | |
| @@ -388,6 +388,21 @@ static void __init numa_default_distance(void) · 387 unmodified lines | ||
| 388 | 388 | } |
| 389 | 389 | } |
| 390 | 390 | |
| 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 | + | |
| 391 | 406 | int __init init_numa_memory(void) |
| 392 | 407 | { |
| 393 | 408 | int i; |
| @@ -404,7 +419,7 @@ int __init init_numa_memory(void) · 10 unmodified lines | ||
| 404 | 419 | memset(&numa_meminfo, 0, sizeof(numa_meminfo)); |
| 405 | 420 | |
| 406 | 421 | /* Parse SRAT and SLIT if provided by firmware. */ |
| 407 | − ret = acpi_numa_init(); | |
| 422 | + ret = acpi_disabled ? fake_numa_init() : acpi_numa_init(); | |
| 408 | 423 | if (ret < 0) |
| 409 | 424 | return ret; |
| 410 | 425 | |
| @@ -47,6 +47,12 @@ · 46 unmodified lines | ||
| 47 | 47 | #include <asm/unwind.h> |
| 48 | 48 | #include <asm/vdso.h> |
| 49 | 49 | |
| 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 | + | |
| 50 | 56 | /* |
| 51 | 57 | * Idle related variables and functions |
| 52 | 58 | */ |
| @@ -15,6 +15,7 @@ · 14 unmodified lines | ||
| 15 | 15 | #include <acpi/reboot.h> |
| 16 | 16 | #include <asm/idle.h> |
| 17 | 17 | #include <asm/loongarch.h> |
| 18 | +#include <asm/loongson.h> | |
| 18 | 19 | |
| 19 | 20 | void (*pm_power_off)(void); |
| 20 | 21 | EXPORT_SYMBOL(pm_power_off); |
| @@ -41,6 +42,10 @@ void machine_power_off(void) · 20 unmodified lines | ||
| 41 | 42 | #ifdef CONFIG_SMP |
| 42 | 43 | preempt_disable(); |
| 43 | 44 | smp_send_stop(); |
| 45 | +#endif | |
| 46 | +#ifdef CONFIG_PM | |
| 47 | + if (!acpi_disabled) | |
| 48 | + enable_pci_wakeup(); | |
| 44 | 49 | #endif |
| 45 | 50 | do_kernel_power_off(); |
| 46 | 51 | #ifdef CONFIG_EFI |
| @@ -28,10 +28,16 @@ · 27 unmodified lines | ||
| 28 | 28 | #include <linux/sizes.h> |
| 29 | 29 | #include <linux/device.h> |
| 30 | 30 | #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> | |
| 31 | 35 | #include <linux/swiotlb.h> |
| 32 | 36 | |
| 33 | 37 | #include <asm/addrspace.h> |
| 38 | +#include <asm/alternative.h> | |
| 34 | 39 | #include <asm/bootinfo.h> |
| 40 | +#include <asm/bugs.h> | |
| 35 | 41 | #include <asm/cache.h> |
| 36 | 42 | #include <asm/cpu.h> |
| 37 | 43 | #include <asm/dma.h> |
| @@ -67,6 +73,7 @@ static const char dmi_empty_string[] = " "; · 29 unmodified lines | ||
| 67 | 73 | * |
| 68 | 74 | * These are initialized so they are in the .data section |
| 69 | 75 | */ |
| 76 | +char init_command_line[COMMAND_LINE_SIZE] __initdata; | |
| 70 | 77 | |
| 71 | 78 | static int num_standard_resources; |
| 72 | 79 | static struct resource *standard_resources; |
| @@ -80,6 +87,11 @@ const char *get_system_type(void) · 7 unmodified lines | ||
| 80 | 87 | return "generic-loongson-machine"; |
| 81 | 88 | } |
| 82 | 89 | |
| 90 | +void __init check_bugs(void) | |
| 91 | +{ | |
| 92 | + alternative_instructions(); | |
| 93 | +} | |
| 94 | + | |
| 83 | 95 | static const char *dmi_string_parse(const struct dmi_header *dm, u8 s) |
| 84 | 96 | { |
| 85 | 97 | const u8 *bp = ((u8 *) dm) + dm->length; |
| @@ -246,6 +258,58 @@ static void __init arch_parse_crashkernel(void) · 160 unmodified lines | ||
| 246 | 258 | #endif |
| 247 | 259 | } |
| 248 | 260 | |
| 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 | + | |
| 249 | 313 | void __init platform_init(void) |
| 250 | 314 | { |
| 251 | 315 | arch_reserve_vmcore(); |
| @@ -258,6 +322,7 @@ void __init platform_init(void) · 6 unmodified lines | ||
| 258 | 322 | acpi_gbl_use_default_register_widths = false; |
| 259 | 323 | acpi_boot_table_init(); |
| 260 | 324 | #endif |
| 325 | + unflatten_and_copy_device_tree(); | |
| 261 | 326 | |
| 262 | 327 | #ifdef CONFIG_NUMA |
| 263 | 328 | init_numa_memory(); |
| @@ -290,6 +355,8 @@ static void __init arch_mem_init(char **cmdline_p) · 26 unmodified lines | ||
| 290 | 355 | |
| 291 | 356 | check_kernel_sections_mem(); |
| 292 | 357 | |
| 358 | + early_init_fdt_scan_reserved_mem(); | |
| 359 | + | |
| 293 | 360 | /* |
| 294 | 361 | * In order to reduce the possibility of kernel panic when failed to |
| 295 | 362 | * 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 | ||
| 304 | 371 | |
| 305 | 372 | dma_contiguous_reserve(PFN_PHYS(max_low_pfn)); |
| 306 | 373 | |
| 374 | + /* Reserve for hibernation. */ | |
| 375 | + register_nosave_region(PFN_DOWN(__pa_symbol(&__nosave_begin)), | |
| 376 | + PFN_UP(__pa_symbol(&__nosave_end))); | |
| 377 | + | |
| 307 | 378 | memblock_dump_all(); |
| 308 | 379 | |
| 309 | 380 | 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 | ||
| 363 | 434 | #endif |
| 364 | 435 | } |
| 365 | 436 | |
| 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 | + | |
| 366 | 512 | static int __init reserve_memblock_reserved_regions(void) |
| 367 | 513 | { |
| 368 | 514 | u64 i, j; |
| @@ -415,12 +561,13 @@ static void __init prefill_possible_map(void) · 46 unmodified lines | ||
| 415 | 561 | void __init setup_arch(char **cmdline_p) |
| 416 | 562 | { |
| 417 | 563 | cpu_probe(); |
| 418 | − *cmdline_p = boot_command_line; | |
| 419 | 564 | |
| 420 | 565 | init_environ(); |
| 421 | 566 | efi_init(); |
| 567 | + fdt_setup(); | |
| 422 | 568 | memblock_init(); |
| 423 | 569 | pagetable_init(); |
| 570 | + bootcmdline_init(cmdline_p); | |
| 424 | 571 | parse_early_param(); |
| 425 | 572 | reserve_initrd_mem(); |
| 426 | 573 | |
| @@ -16,6 +16,7 @@ · 15 unmodified lines | ||
| 16 | 16 | #include <linux/smp.h> |
| 17 | 17 | #include <linux/threads.h> |
| 18 | 18 | #include <linux/export.h> |
| 19 | +#include <linux/syscore_ops.h> | |
| 19 | 20 | #include <linux/time.h> |
| 20 | 21 | #include <linux/tracepoint.h> |
| 21 | 22 | #include <linux/sched/hotplug.h> |
| @@ -180,8 +181,42 @@ irqreturn_t loongson_ipi_interrupt(int irq, void *dev) · 158 unmodified lines | ||
| 180 | 181 | return IRQ_HANDLED; |
| 181 | 182 | } |
| 182 | 183 | |
| 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 | + | |
| 183 | 216 | void __init loongson_smp_setup(void) |
| 184 | 217 | { |
| 218 | + fdt_smp_setup(); | |
| 219 | + | |
| 185 | 220 | cpu_data[0].core = cpu_logical_map(0) % loongson_sysconf.cores_per_package; |
| 186 | 221 | cpu_data[0].package = cpu_logical_map(0) / loongson_sysconf.cores_per_package; |
| 187 | 222 | |
| @@ -23,6 +23,11 @@ SYM_FUNC_START(__switch_to) · 22 unmodified lines | ||
| 23 | 23 | stptr.d ra, a0, THREAD_REG01 |
| 24 | 24 | stptr.d a3, a0, THREAD_SCHED_RA |
| 25 | 25 | 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 | |
| 26 | 31 | move tp, a2 |
| 27 | 32 | cpu_restore_nonscratch a1 |
| 28 | 33 | |
| @@ -115,12 +115,17 @@ static unsigned long __init get_loops_per_jiffy(void) · 114 unmodified lines | ||
| 115 | 115 | return lpj; |
| 116 | 116 | } |
| 117 | 117 | |
| 118 | −static long init_timeval; | |
| 118 | +static long init_offset __nosavedata; | |
| 119 | + | |
| 120 | +void save_counter(void) | |
| 121 | +{ | |
| 122 | + init_offset = drdtime(); | |
| 123 | +} | |
| 119 | 124 | |
| 120 | 125 | void sync_counter(void) |
| 121 | 126 | { |
| 122 | 127 | /* Ensure counter begin at 0 */ |
| 123 | − csr_write64(-init_timeval, LOONGARCH_CSR_CNTC); | |
| 128 | + csr_write64(init_offset, LOONGARCH_CSR_CNTC); | |
| 124 | 129 | } |
| 125 | 130 | |
| 126 | 131 | static int get_timer_irq(void) |
| @@ -219,7 +224,7 @@ void __init time_init(void) · 92 unmodified lines | ||
| 219 | 224 | else |
| 220 | 225 | const_clock_freq = calc_const_freq(); |
| 221 | 226 | |
| 222 | − init_timeval = drdtime() - csr_read64(LOONGARCH_CSR_CNTC); | |
| 227 | + init_offset = -(drdtime() - csr_read64(LOONGARCH_CSR_CNTC)); | |
| 223 | 228 | |
| 224 | 229 | constant_clockevent_init(); |
| 225 | 230 | constant_clocksource_init(); |
| @@ -368,13 +368,40 @@ asmlinkage void noinstr do_ade(struct pt_regs *regs) · 367 unmodified lines | ||
| 368 | 368 | irqentry_exit(regs, state); |
| 369 | 369 | } |
| 370 | 370 | |
| 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 | + | |
| 371 | 375 | asmlinkage void noinstr do_ale(struct pt_regs *regs) |
| 372 | 376 | { |
| 377 | + unsigned int *pc; | |
| 373 | 378 | irqentry_state_t state = irqentry_enter(regs); |
| 374 | 379 | |
| 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: | |
| 375 | 401 | die_if_kernel("Kernel ale access", regs); |
| 376 | 402 | force_sig_fault(SIGBUS, BUS_ADRALN, (void __user *)regs->csr_badvaddr); |
| 377 | 403 | |
| 404 | +out: | |
| 378 | 405 | irqentry_exit(regs, state); |
| 379 | 406 | } |
| 380 | 407 | |
| @@ -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(¤t->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(¤t->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 | |
| @@ -3,6 +3,7 @@ · 2 unmodified lines | ||
| 3 | 3 | * Copyright (C) 2022 Loongson Technology Corporation Limited |
| 4 | 4 | */ |
| 5 | 5 | #include <linux/kernel.h> |
| 6 | +#include <linux/ftrace.h> | |
| 6 | 7 | |
| 7 | 8 | #include <asm/unwind.h> |
| 8 | 9 | |
| @@ -53,7 +54,8 @@ bool unwind_next_frame(struct unwind_state *state) · 44 unmodified lines | ||
| 53 | 54 | state->sp < info->end; |
| 54 | 55 | state->sp += sizeof(unsigned long)) { |
| 55 | 56 | 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)); | |
| 57 | 59 | if (__kernel_text_address(addr)) |
| 58 | 60 | return true; |
| 59 | 61 | } |
| @@ -2,12 +2,23 @@ · 1 unmodified line | ||
| 2 | 2 | /* |
| 3 | 3 | * Copyright (C) 2022 Loongson Technology Corporation Limited |
| 4 | 4 | */ |
| 5 | +#include <linux/ftrace.h> | |
| 5 | 6 | #include <linux/kallsyms.h> |
| 6 | 7 | |
| 7 | 8 | #include <asm/inst.h> |
| 8 | 9 | #include <asm/ptrace.h> |
| 9 | 10 | #include <asm/unwind.h> |
| 10 | 11 | |
| 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 | + | |
| 11 | 22 | unsigned long unwind_get_return_address(struct unwind_state *state) |
| 12 | 23 | { |
| 13 | 24 | |
| @@ -32,6 +43,8 @@ static bool unwind_by_guess(struct unwind_state *state) · 18 unmodified lines | ||
| 32 | 43 | state->sp < info->end; |
| 33 | 44 | state->sp += sizeof(unsigned long)) { |
| 34 | 45 | 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)); | |
| 35 | 48 | if (__kernel_text_address(addr)) |
| 36 | 49 | return true; |
| 37 | 50 | } |
| @@ -41,15 +54,30 @@ static bool unwind_by_guess(struct unwind_state *state) · 3 unmodified lines | ||
| 41 | 54 | |
| 42 | 55 | static bool unwind_by_prologue(struct unwind_state *state) |
| 43 | 56 | { |
| 44 | − struct stack_info *info = &state->stack_info; | |
| 45 | − union loongarch_instruction *ip, *ip_end; | |
| 46 | 57 | long frame_ra = -1; |
| 47 | 58 | unsigned long frame_size = 0; |
| 48 | 59 | 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; | |
| 49 | 63 | |
| 50 | 64 | if (state->sp >= info->end || state->sp < info->begin) |
| 51 | 65 | return false; |
| 52 | 66 | |
| 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 | + | |
| 53 | 81 | if (!kallsyms_lookup_size_offset(pc, &size, &offset)) |
| 54 | 82 | return false; |
| 55 | 83 | |
| @@ -95,7 +123,7 @@ static bool unwind_by_prologue(struct unwind_state *state) · 39 unmodified lines | ||
| 95 | 123 | |
| 96 | 124 | state->pc = *(unsigned long *)(state->sp + frame_ra); |
| 97 | 125 | state->sp = state->sp + frame_size; |
| 98 | − return !!__kernel_text_address(state->pc); | |
| 126 | + goto out; | |
| 99 | 127 | |
| 100 | 128 | first: |
| 101 | 129 | state->first = false; |
| @@ -104,7 +132,9 @@ first: · 2 unmodified lines | ||
| 104 | 132 | |
| 105 | 133 | state->pc = state->ra; |
| 106 | 134 | |
| 107 | − return !!__kernel_text_address(state->ra); | |
| 135 | +out: | |
| 136 | + unwind_state_fixup(state); | |
| 137 | + return !!__kernel_text_address(state->pc); | |
| 108 | 138 | } |
| 109 | 139 | |
| 110 | 140 | 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 | ||
| 147 | 177 | break; |
| 148 | 178 | |
| 149 | 179 | 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)); | |
| 151 | 183 | return true; |
| 184 | + } | |
| 152 | 185 | |
| 153 | 186 | if (info->type == STACK_TYPE_IRQ && |
| 154 | 187 | info->end == state->sp) { |
| @@ -158,10 +191,11 @@ bool unwind_next_frame(struct unwind_state *state) · 3 unmodified lines | ||
| 158 | 191 | if (user_mode(regs) || !__kernel_text_address(pc)) |
| 159 | 192 | return false; |
| 160 | 193 | |
| 161 | − state->pc = pc; | |
| 162 | − state->sp = regs->regs[3]; | |
| 163 | − state->ra = regs->regs[1]; | |
| 164 | 194 | 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)); | |
| 165 | 199 | get_stack_info(state->sp, state->task, info); |
| 166 | 200 | |
| 167 | 201 | return true; |
| @@ -4,6 +4,7 @@ · 3 unmodified lines | ||
| 4 | 4 | #include <asm/thread_info.h> |
| 5 | 5 | |
| 6 | 6 | #define PAGE_SIZE _PAGE_SIZE |
| 7 | +#define RO_EXCEPTION_TABLE_ALIGN 4 | |
| 7 | 8 | |
| 8 | 9 | /* |
| 9 | 10 | * Put .bss..swapper_pg_dir as the first thing in .bss. This will |
| @@ -53,7 +54,17 @@ SECTIONS · 43 unmodified lines | ||
| 53 | 54 | . = ALIGN(PECOFF_SEGMENT_ALIGN); |
| 54 | 55 | _etext = .; |
| 55 | 56 | |
| 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 | + } | |
| 57 | 68 | |
| 58 | 69 | .got : ALIGN(16) { *(.got) } |
| 59 | 70 | .plt : ALIGN(16) { *(.plt) } |
| @@ -3,4 +3,5 @@ · 2 unmodified lines | ||
| 3 | 3 | # Makefile for LoongArch-specific library files. |
| 4 | 4 | # |
| 5 | 5 | |
| 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 | |
| @@ -3,30 +3,37 @@ · 2 unmodified lines | ||
| 3 | 3 | * Copyright (C) 2020-2022 Loongson Technology Corporation Limited |
| 4 | 4 | */ |
| 5 | 5 | |
| 6 | +#include <asm/alternative-asm.h> | |
| 6 | 7 | #include <asm/asm.h> |
| 7 | 8 | #include <asm/asmmacro.h> |
| 9 | +#include <asm/asm-extable.h> | |
| 10 | +#include <asm/cpu.h> | |
| 8 | 11 | #include <asm/export.h> |
| 9 | 12 | #include <asm/regdef.h> |
| 10 | 13 | |
| 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) | |
| 15 | 17 | 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) | |
| 22 | 29 | |
| 23 | 30 | /* |
| 24 | − * unsigned long __clear_user(void *addr, size_t size) | |
| 31 | + * unsigned long __clear_user_generic(void *addr, size_t size) | |
| 25 | 32 | * |
| 26 | 33 | * a0: addr |
| 27 | 34 | * a1: size |
| 28 | 35 | */ |
| 29 | −SYM_FUNC_START(__clear_user) | |
| 36 | +SYM_FUNC_START(__clear_user_generic) | |
| 30 | 37 | beqz a1, 2f |
| 31 | 38 | |
| 32 | 39 | 1: st.b zero, a0, 0 |
| @@ -37,7 +44,55 @@ SYM_FUNC_START(__clear_user) · 4 unmodified lines | ||
| 37 | 44 | 2: move a0, a1 |
| 38 | 45 | jr ra |
| 39 | 46 | |
| 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) | |
| 42 | 49 | |
| 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) | |
| @@ -3,31 +3,38 @@ · 2 unmodified lines | ||
| 3 | 3 | * Copyright (C) 2020-2022 Loongson Technology Corporation Limited |
| 4 | 4 | */ |
| 5 | 5 | |
| 6 | +#include <asm/alternative-asm.h> | |
| 6 | 7 | #include <asm/asm.h> |
| 7 | 8 | #include <asm/asmmacro.h> |
| 9 | +#include <asm/asm-extable.h> | |
| 10 | +#include <asm/cpu.h> | |
| 8 | 11 | #include <asm/export.h> |
| 9 | 12 | #include <asm/regdef.h> |
| 10 | 13 | |
| 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) | |
| 15 | 17 | 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) | |
| 22 | 29 | |
| 23 | 30 | /* |
| 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) | |
| 25 | 32 | * |
| 26 | 33 | * a0: to |
| 27 | 34 | * a1: from |
| 28 | 35 | * a2: n |
| 29 | 36 | */ |
| 30 | −SYM_FUNC_START(__copy_user) | |
| 37 | +SYM_FUNC_START(__copy_user_generic) | |
| 31 | 38 | beqz a2, 3f |
| 32 | 39 | |
| 33 | 40 | 1: ld.b t0, a1, 0 |
| @@ -40,8 +47,77 @@ SYM_FUNC_START(__copy_user) · 6 unmodified lines | ||
| 40 | 47 | 3: move a0, a2 |
| 41 | 48 | jr ra |
| 42 | 49 | |
| 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) | |
| 46 | 53 | |
| 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) | |
| @@ -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) | |
| @@ -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) | |
| @@ -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) | |
| @@ -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) | |
| @@ -2,21 +2,62 @@ · 1 unmodified line | ||
| 2 | 2 | /* |
| 3 | 3 | * Copyright (C) 2020-2022 Loongson Technology Corporation Limited |
| 4 | 4 | */ |
| 5 | +#include <linux/bitfield.h> | |
| 5 | 6 | #include <linux/extable.h> |
| 6 | −#include <linux/spinlock.h> | |
| 7 | −#include <asm/branch.h> | |
| 8 | 7 | #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 | +} | |
| 9 | 44 | |
| 10 | −int fixup_exception(struct pt_regs *regs) | |
| 45 | +bool fixup_exception(struct pt_regs *regs) | |
| 11 | 46 | { |
| 12 | − const struct exception_table_entry *fixup; | |
| 47 | + const struct exception_table_entry *ex; | |
| 13 | 48 | |
| 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; | |
| 17 | 52 | |
| 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); | |
| 19 | 60 | } |
| 20 | 61 | |
| 21 | − return 0; | |
| 62 | + BUG(); | |
| 22 | 63 | } |
| @@ -387,6 +387,65 @@ static bool is_signed_bpf_cond(u8 cond) · 386 unmodified lines | ||
| 387 | 387 | cond == BPF_JSGE || cond == BPF_JSLE; |
| 388 | 388 | } |
| 389 | 389 | |
| 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 | + | |
| 390 | 449 | static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool extra_pass) |
| 391 | 450 | { |
| 392 | 451 | 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 | ||
| 816 | 875 | case BPF_LDX | BPF_MEM | BPF_H: |
| 817 | 876 | case BPF_LDX | BPF_MEM | BPF_W: |
| 818 | 877 | 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: | |
| 819 | 882 | switch (BPF_SIZE(code)) { |
| 820 | 883 | case BPF_B: |
| 821 | 884 | 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 | ||
| 854 | 917 | } |
| 855 | 918 | break; |
| 856 | 919 | } |
| 920 | + | |
| 921 | + ret = add_exception_handler(insn, ctx, dst); | |
| 922 | + if (ret) | |
| 923 | + return ret; | |
| 857 | 924 | break; |
| 858 | 925 | |
| 859 | 926 | /* *(size *)(dst + off) = imm */ |
| @@ -1018,6 +1085,9 @@ static int validate_code(struct jit_ctx *ctx) · 158 unmodified lines | ||
| 1018 | 1085 | return -1; |
| 1019 | 1086 | } |
| 1020 | 1087 | |
| 1088 | + if (WARN_ON_ONCE(ctx->num_exentries != ctx->prog->aux->num_exentries)) | |
| 1089 | + return -1; | |
| 1090 | + | |
| 1021 | 1091 | return 0; |
| 1022 | 1092 | } |
| 1023 | 1093 | |
| @@ -1025,7 +1095,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) · 1 unmodified line | ||
| 1025 | 1095 | { |
| 1026 | 1096 | bool tmp_blinded = false, extra_pass = false; |
| 1027 | 1097 | u8 *image_ptr; |
| 1028 | − int image_size; | |
| 1098 | + int image_size, prog_size, extable_size; | |
| 1029 | 1099 | struct jit_ctx ctx; |
| 1030 | 1100 | struct jit_data *jit_data; |
| 1031 | 1101 | struct bpf_binary_header *header; |
| @@ -1066,7 +1136,7 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) · 34 unmodified lines | ||
| 1066 | 1136 | image_ptr = jit_data->image; |
| 1067 | 1137 | header = jit_data->header; |
| 1068 | 1138 | extra_pass = true; |
| 1069 | − image_size = sizeof(u32) * ctx.idx; | |
| 1139 | + prog_size = sizeof(u32) * ctx.idx; | |
| 1070 | 1140 | goto skip_init_ctx; |
| 1071 | 1141 | } |
| 1072 | 1142 | |
| @@ -1088,12 +1158,15 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) · 15 unmodified lines | ||
| 1088 | 1158 | ctx.epilogue_offset = ctx.idx; |
| 1089 | 1159 | build_epilogue(&ctx); |
| 1090 | 1160 | |
| 1161 | + extable_size = prog->aux->num_exentries * sizeof(struct exception_table_entry); | |
| 1162 | + | |
| 1091 | 1163 | /* Now we know the actual image size. |
| 1092 | 1164 | * As each LoongArch instruction is of length 32bit, |
| 1093 | 1165 | * we are translating number of JITed intructions into |
| 1094 | 1166 | * the size required to store these JITed code. |
| 1095 | 1167 | */ |
| 1096 | − image_size = sizeof(u32) * ctx.idx; | |
| 1168 | + prog_size = sizeof(u32) * ctx.idx; | |
| 1169 | + image_size = prog_size + extable_size; | |
| 1097 | 1170 | /* Now we know the size of the structure to make */ |
| 1098 | 1171 | header = bpf_jit_binary_alloc(image_size, &image_ptr, |
| 1099 | 1172 | sizeof(u32), jit_fill_hole); |
| @@ -1104,9 +1177,12 @@ struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) · 4 unmodified lines | ||
| 1104 | 1177 | |
| 1105 | 1178 | /* 2. Now, the actual pass to generate final JIT code */ |
| 1106 | 1179 | ctx.image = (union loongarch_instruction *)image_ptr; |
| 1180 | + if (extable_size) | |
| 1181 | + prog->aux->extable = (void *)image_ptr + prog_size; | |
| 1107 | 1182 | |
| 1108 | 1183 | skip_init_ctx: |
| 1109 | 1184 | ctx.idx = 0; |
| 1185 | + ctx.num_exentries = 0; | |
| 1110 | 1186 | |
| 1111 | 1187 | build_prologue(&ctx); |
| 1112 | 1188 | if (build_body(&ctx, extra_pass)) { |
| @@ -1125,7 +1201,7 @@ skip_init_ctx: · 12 unmodified lines | ||
| 1125 | 1201 | |
| 1126 | 1202 | /* And we're done */ |
| 1127 | 1203 | 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); | |
| 1129 | 1205 | |
| 1130 | 1206 | /* Update the icache */ |
| 1131 | 1207 | flush_icache_range((unsigned long)header, (unsigned long)(ctx.image + ctx.idx)); |
| @@ -1147,7 +1223,7 @@ skip_init_ctx: · 15 unmodified lines | ||
| 1147 | 1223 | jit_data->header = header; |
| 1148 | 1224 | } |
| 1149 | 1225 | prog->jited = 1; |
| 1150 | − prog->jited_len = image_size; | |
| 1226 | + prog->jited_len = prog_size; | |
| 1151 | 1227 | prog->bpf_func = (void *)ctx.image; |
| 1152 | 1228 | |
| 1153 | 1229 | if (!prog->is_func || extra_pass) { |
| @@ -4,6 +4,7 @@ · 3 unmodified lines | ||
| 4 | 4 | * |
| 5 | 5 | * Copyright (C) 2022 Loongson Technology Corporation Limited |
| 6 | 6 | */ |
| 7 | +#include <linux/bitfield.h> | |
| 7 | 8 | #include <linux/bpf.h> |
| 8 | 9 | #include <linux/filter.h> |
| 9 | 10 | #include <asm/cacheflush.h> |
| @@ -15,6 +16,7 @@ struct jit_ctx { · 5 unmodified lines | ||
| 15 | 16 | unsigned int flags; |
| 16 | 17 | unsigned int epilogue_offset; |
| 17 | 18 | u32 *offset; |
| 19 | + int num_exentries; | |
| 18 | 20 | union loongarch_instruction *image; |
| 19 | 21 | u32 stack_size; |
| 20 | 22 | }; |
| @@ -26,9 +26,12 @@ void pcibios_add_bus(struct pci_bus *bus) · 25 unmodified lines | ||
| 26 | 26 | |
| 27 | 27 | int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) |
| 28 | 28 | { |
| 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; | |
| 31 | 30 | 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); | |
| 32 | 35 | |
| 33 | 36 | ACPI_COMPANION_SET(&bridge->dev, adev); |
| 34 | 37 | set_dev_node(bus_dev, pa_to_nid(cfg->res.start)); |
| @@ -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 | |
| @@ -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 | +} | |
| @@ -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) | |
| @@ -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); | |
| @@ -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 | +} | |
| @@ -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) | |
| @@ -1523,6 +1523,14 @@ static int addend_mips_rel(struct elf_info *elf, Elf_Shdr *sechdr, Elf_Rela *r) · 1522 unmodified lines | ||
| 1523 | 1523 | #define R_RISCV_SUB32 39 |
| 1524 | 1524 | #endif |
| 1525 | 1525 | |
| 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 | + | |
| 1526 | 1534 | static void section_rela(const char *modname, struct elf_info *elf, |
| 1527 | 1535 | Elf_Shdr *sechdr) |
| 1528 | 1536 | { |
| @@ -1564,6 +1572,11 @@ static void section_rela(const char *modname, struct elf_info *elf, · 35 unmodified lines | ||
| 1564 | 1572 | ELF_R_TYPE(r.r_info) == R_RISCV_SUB32) |
| 1565 | 1573 | continue; |
| 1566 | 1574 | 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; | |
| 1567 | 1580 | } |
| 1568 | 1581 | sym = elf->symtab_start + r_sym; |
| 1569 | 1582 | /* Skip special sections */ |
| @@ -38,6 +38,14 @@ · 37 unmodified lines | ||
| 38 | 38 | #define R_AARCH64_ABS64 257 |
| 39 | 39 | #endif |
| 40 | 40 | |
| 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 | + | |
| 41 | 49 | #define R_ARM_PC24 1 |
| 42 | 50 | #define R_ARM_THM_CALL 10 |
| 43 | 51 | #define R_ARM_CALL 28 |
| @@ -441,6 +449,28 @@ static int arm64_is_fake_mcount(Elf64_Rel const *rp) · 397 unmodified lines | ||
| 441 | 449 | return ELF64_R_TYPE(w8(rp->r_info)) != R_AARCH64_CALL26; |
| 442 | 450 | } |
| 443 | 451 | |
| 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 | + | |
| 444 | 474 | /* 64-bit EM_MIPS has weird ELF64_Rela.r_info. |
| 445 | 475 | * http://techpubs.sgi.com/library/manuals/4000/007-4658-001/pdf/007-4658-001.pdf |
| 446 | 476 | * 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 | ||
| 558 | 588 | break; |
| 559 | 589 | case EM_IA_64: reltype = R_IA64_IMM64; break; |
| 560 | 590 | case EM_MIPS: /* reltype: e_class */ break; |
| 591 | + case EM_LOONGARCH: /* reltype: e_class */ break; | |
| 561 | 592 | case EM_PPC: reltype = R_PPC_ADDR32; break; |
| 562 | 593 | case EM_PPC64: reltype = R_PPC64_ADDR64; break; |
| 563 | 594 | case EM_S390: /* reltype: e_class */ break; |
| @@ -589,6 +620,10 @@ static int do_file(char const *const fname) · 25 unmodified lines | ||
| 589 | 620 | reltype = R_MIPS_32; |
| 590 | 621 | is_fake_mcount32 = MIPS32_is_fake_mcount; |
| 591 | 622 | } |
| 623 | + if (w2(ehdr->e_machine) == EM_LOONGARCH) { | |
| 624 | + reltype = R_LARCH_32; | |
| 625 | + is_fake_mcount32 = LARCH32_is_fake_mcount; | |
| 626 | + } | |
| 592 | 627 | if (do32(ehdr, fname, reltype) < 0) |
| 593 | 628 | goto out; |
| 594 | 629 | break; |
| @@ -610,6 +645,10 @@ static int do_file(char const *const fname) · 15 unmodified lines | ||
| 610 | 645 | Elf64_r_info = MIPS64_r_info; |
| 611 | 646 | is_fake_mcount64 = MIPS64_is_fake_mcount; |
| 612 | 647 | } |
| 648 | + if (w2(ghdr->e_machine) == EM_LOONGARCH) { | |
| 649 | + reltype = R_LARCH_64; | |
| 650 | + is_fake_mcount64 = LARCH64_is_fake_mcount; | |
| 651 | + } | |
| 613 | 652 | if (do64(ghdr, fname, reltype) < 0) |
| 614 | 653 | goto out; |
| 615 | 654 | break; |
| @@ -304,6 +304,7 @@ static int do_file(char const *const fname, void *addr) · 303 unmodified lines | ||
| 304 | 304 | switch (r2(&ehdr->e_machine)) { |
| 305 | 305 | case EM_386: |
| 306 | 306 | case EM_AARCH64: |
| 307 | + case EM_LOONGARCH: | |
| 307 | 308 | case EM_RISCV: |
| 308 | 309 | case EM_S390: |
| 309 | 310 | case EM_X86_64: |
| @@ -317,7 +318,6 @@ static int do_file(char const *const fname, void *addr) · 7 unmodified lines | ||
| 317 | 318 | case EM_ARCOMPACT: |
| 318 | 319 | case EM_ARCV2: |
| 319 | 320 | case EM_ARM: |
| 320 | − case EM_LOONGARCH: | |
| 321 | 321 | case EM_MICROBLAZE: |
| 322 | 322 | case EM_MIPS: |
| 323 | 323 | case EM_XTENSA: |