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