Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

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

Pull LoongArch updates from Huacai Chen:

- Use EXPLICIT_RELOCS (ABIv2.0)

- Use generic BUG() handler

- Refactor TLB/Cache operations

- Add qspinlock support

- Add perf events support

- Add kexec/kdump support

- Add BPF JIT support

- Add ACPI-based laptop driver

- Update the default config file

* tag 'loongarch-6.1' of git://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson: (25 commits)
LoongArch: Update Loongson-3 default config file
LoongArch: Add ACPI-based generic laptop driver
LoongArch: Add BPF JIT support
LoongArch: Add some instruction opcodes and formats
LoongArch: Move {signed,unsigned}_imm_check() to inst.h
LoongArch: Add kdump support
LoongArch: Add kexec support
LoongArch: Use generic BUG() handler
LoongArch: Add SysRq-x (TLB Dump) support
LoongArch: Add perf events support
LoongArch: Add qspinlock support
LoongArch: Use TLB for ioremap()
LoongArch: Support access filter to /dev/mem interface
LoongArch: Refactor cache probe and flush methods
LoongArch: mm: Refactor TLB exception handlers
LoongArch: Support R_LARCH_GOT_PC_{LO12,HI20} in modules
LoongArch: Support PC-relative relocations in modules
LoongArch: Define ELF relocation types added in ABIv2.0
LoongArch: Adjust symbol addressing for AS_HAS_EXPLICIT_RELOCS
LoongArch: Add Kconfig option AS_HAS_EXPLICIT_RELOCS
...

+5359 -689
+1
arch/loongarch/Kbuild
··· 1 1 obj-y += kernel/ 2 2 obj-y += mm/ 3 + obj-y += net/ 3 4 obj-y += vdso/ 4 5 5 6 # for cleaning
+61 -1
arch/loongarch/Kconfig
··· 50 50 select ARCH_USE_BUILTIN_BSWAP 51 51 select ARCH_USE_CMPXCHG_LOCKREF 52 52 select ARCH_USE_QUEUED_RWLOCKS 53 + select ARCH_USE_QUEUED_SPINLOCKS 53 54 select ARCH_WANT_DEFAULT_TOPDOWN_MMAP_LAYOUT 54 55 select ARCH_WANT_LD_ORPHAN_WARN 55 56 select ARCH_WANTS_NO_INSTR ··· 62 61 select GENERIC_CPU_AUTOPROBE 63 62 select GENERIC_ENTRY 64 63 select GENERIC_GETTIMEOFDAY 64 + select GENERIC_IOREMAP if !ARCH_IOREMAP 65 65 select GENERIC_IRQ_MULTI_HANDLER 66 66 select GENERIC_IRQ_PROBE 67 67 select GENERIC_IRQ_SHOW ··· 71 69 select GENERIC_LIB_CMPDI2 72 70 select GENERIC_LIB_LSHRDI3 73 71 select GENERIC_LIB_UCMPDI2 72 + select GENERIC_LIB_DEVMEM_IS_ALLOWED 74 73 select GENERIC_PCI_IOMAP 75 74 select GENERIC_SCHED_CLOCK 76 75 select GENERIC_SMP_IDLE_THREAD ··· 86 83 select HAVE_CONTEXT_TRACKING_USER 87 84 select HAVE_DEBUG_STACKOVERFLOW 88 85 select HAVE_DMA_CONTIGUOUS 86 + select HAVE_EBPF_JIT 89 87 select HAVE_EXIT_THREAD 90 88 select HAVE_FAST_GUP 91 89 select HAVE_GENERIC_VDSO ··· 97 93 select HAVE_NMI 98 94 select HAVE_PCI 99 95 select HAVE_PERF_EVENTS 96 + select HAVE_PERF_REGS 97 + select HAVE_PERF_USER_STACK_DUMP 100 98 select HAVE_REGS_AND_STACK_ACCESS_API 101 99 select HAVE_RSEQ 102 100 select HAVE_SETUP_PER_CPU_AREA if NUMA ··· 142 136 bool 143 137 default y 144 138 139 + config GENERIC_BUG 140 + def_bool y 141 + depends on BUG 142 + 143 + config GENERIC_BUG_RELATIVE_POINTERS 144 + def_bool y 145 + depends on GENERIC_BUG 146 + 145 147 config GENERIC_CALIBRATE_DELAY 146 148 def_bool y 147 149 ··· 171 157 bool 172 158 default y 173 159 174 - # MACH_LOONGSON32 and MACH_LOONGSON64 are delibrately carried over from the 160 + # MACH_LOONGSON32 and MACH_LOONGSON64 are deliberately carried over from the 175 161 # MIPS Loongson code, to preserve Loongson-specific code paths in drivers that 176 162 # are shared between architectures, and specifically expecting the symbols. 177 163 config MACH_LOONGSON32 ··· 179 165 180 166 config MACH_LOONGSON64 181 167 def_bool 64BIT 168 + 169 + config FIX_EARLYCON_MEM 170 + def_bool y 182 171 183 172 config PAGE_SIZE_4KB 184 173 bool ··· 210 193 config SCHED_OMIT_FRAME_POINTER 211 194 bool 212 195 default y 196 + 197 + config AS_HAS_EXPLICIT_RELOCS 198 + def_bool $(as-instr,x:pcalau12i \$t0$(comma)%pc_hi20(x)) 213 199 214 200 menu "Kernel type and options" 215 201 ··· 418 398 419 399 The page size is not necessarily 4KB. Keep this in mind 420 400 when choosing a value for this option. 401 + 402 + config ARCH_IOREMAP 403 + bool "Enable LoongArch DMW-based ioremap()" 404 + help 405 + We use generic TLB-based ioremap() by default since it has page 406 + protection support. However, you can enable LoongArch DMW-based 407 + ioremap() for better performance. 408 + 409 + config KEXEC 410 + bool "Kexec system call" 411 + select KEXEC_CORE 412 + help 413 + kexec is a system call that implements the ability to shutdown your 414 + current kernel, and to start another kernel. It is like a reboot 415 + but it is independent of the system firmware. And like a reboot 416 + you can start any kernel with it, not just Linux. 417 + 418 + The name comes from the similarity to the exec system call. 419 + 420 + config CRASH_DUMP 421 + bool "Build kdump crash kernel" 422 + help 423 + Generate crash dump after being started by kexec. This should 424 + be normally only set in special crash dump kernels which are 425 + loaded in the main kernel with kexec-tools into a specially 426 + reserved region and then later executed after a crash by 427 + kdump/kexec. 428 + 429 + For more details see Documentation/admin-guide/kdump/kdump.rst 430 + 431 + config PHYSICAL_START 432 + hex "Physical address where the kernel is loaded" 433 + default "0x90000000a0000000" 434 + depends on CRASH_DUMP 435 + help 436 + This gives the XKPRANGE address where the kernel is loaded. 437 + If you plan to use kernel for capturing the crash dump change 438 + this value to start of the reserved region (the "X" value as 439 + specified in the "crashkernel=YM@XM" command line boot parameter 440 + passed to the panic-ed kernel). 421 441 422 442 config SECCOMP 423 443 bool "Enable seccomp to safely compute untrusted bytecode"
+22
arch/loongarch/Makefile
··· 43 43 44 44 cflags-y += -G0 -pipe -msoft-float 45 45 LDFLAGS_vmlinux += -G0 -static -n -nostdlib 46 + 47 + # When the assembler supports explicit relocation hint, we must use it. 48 + # GCC may have -mexplicit-relocs off by default if it was built with an old 49 + # assembler, so we force it via an option. 50 + # 51 + # When the assembler does not supports explicit relocation hint, we can't use 52 + # it. Disable it if the compiler supports it. 53 + # 54 + # If you've seen "unknown reloc hint" message building the kernel and you are 55 + # now wondering why "-mexplicit-relocs" is not wrapped with cc-option: the 56 + # combination of a "new" assembler and "old" compiler is not supported. Either 57 + # upgrade the compiler or downgrade the assembler. 58 + ifdef CONFIG_AS_HAS_EXPLICIT_RELOCS 59 + cflags-y += -mexplicit-relocs 60 + KBUILD_CFLAGS_KERNEL += -mdirect-extern-access 61 + else 62 + cflags-y += $(call cc-option,-mno-explicit-relocs) 46 63 KBUILD_AFLAGS_KERNEL += -Wa,-mla-global-with-pcrel 47 64 KBUILD_CFLAGS_KERNEL += -Wa,-mla-global-with-pcrel 48 65 KBUILD_AFLAGS_MODULE += -Wa,-mla-global-with-abs 49 66 KBUILD_CFLAGS_MODULE += -fplt -Wa,-mla-global-with-abs,-mla-local-with-abs 67 + endif 50 68 51 69 cflags-y += -ffreestanding 52 70 cflags-y += $(call cc-option, -mno-check-zero-division) 53 71 72 + ifndef CONFIG_PHYSICAL_START 54 73 load-y = 0x9000000000200000 74 + else 75 + load-y = $(CONFIG_PHYSICAL_START) 76 + endif 55 77 bootvars-y = VMLINUX_LOAD_ADDRESS=$(load-y) 56 78 57 79 drivers-$(CONFIG_PCI) += arch/loongarch/pci/
+55 -8
arch/loongarch/configs/loongson3_defconfig
··· 4 4 CONFIG_NO_HZ=y 5 5 CONFIG_HIGH_RES_TIMERS=y 6 6 CONFIG_BPF_SYSCALL=y 7 + CONFIG_BPF_JIT=y 7 8 CONFIG_PREEMPT=y 8 9 CONFIG_BSD_PROCESS_ACCT=y 9 10 CONFIG_BSD_PROCESS_ACCT_V3=y ··· 46 45 CONFIG_HOTPLUG_CPU=y 47 46 CONFIG_NR_CPUS=64 48 47 CONFIG_NUMA=y 48 + CONFIG_KEXEC=y 49 49 CONFIG_PAGE_SIZE_16KB=y 50 50 CONFIG_HZ_250=y 51 51 CONFIG_ACPI=y ··· 57 55 CONFIG_ACPI_IPMI=m 58 56 CONFIG_ACPI_PCI_SLOT=y 59 57 CONFIG_ACPI_HOTPLUG_MEMORY=y 58 + CONFIG_EFI_ZBOOT=y 60 59 CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=y 61 60 CONFIG_EFI_CAPSULE_LOADER=m 62 61 CONFIG_EFI_TEST=m ··· 68 65 CONFIG_MODVERSIONS=y 69 66 CONFIG_BLK_DEV_THROTTLING=y 70 67 CONFIG_PARTITION_ADVANCED=y 68 + CONFIG_BSD_DISKLABEL=y 69 + CONFIG_UNIXWARE_DISKLABEL=y 71 70 CONFIG_IOSCHED_BFQ=y 72 71 CONFIG_BFQ_GROUP_IOSCHED=y 73 72 CONFIG_BINFMT_MISC=m ··· 87 82 CONFIG_NET=y 88 83 CONFIG_PACKET=y 89 84 CONFIG_UNIX=y 85 + CONFIG_TLS=m 86 + CONFIG_TLS_DEVICE=y 90 87 CONFIG_XFRM_USER=y 91 88 CONFIG_NET_KEY=y 89 + CONFIG_XDP_SOCKETS=y 92 90 CONFIG_INET=y 93 91 CONFIG_IP_MULTICAST=y 94 92 CONFIG_IP_ADVANCED_ROUTER=y ··· 103 95 CONFIG_IP_PNP_BOOTP=y 104 96 CONFIG_IP_PNP_RARP=y 105 97 CONFIG_NET_IPIP=m 98 + CONFIG_NET_IPGRE_DEMUX=m 106 99 CONFIG_IP_MROUTE=y 107 100 CONFIG_INET_ESP=m 108 101 CONFIG_INET_UDP_DIAG=y ··· 111 102 CONFIG_TCP_CONG_BBR=m 112 103 CONFIG_IPV6_ROUTER_PREF=y 113 104 CONFIG_IPV6_ROUTE_INFO=y 105 + CONFIG_INET6_ESP=m 114 106 CONFIG_IPV6_MROUTE=y 115 107 CONFIG_NETWORK_PHY_TIMESTAMPING=y 116 108 CONFIG_NETFILTER=y ··· 122 112 CONFIG_NF_CONNTRACK_AMANDA=m 123 113 CONFIG_NF_CONNTRACK_FTP=m 124 114 CONFIG_NF_CONNTRACK_NETBIOS_NS=m 115 + CONFIG_NF_CONNTRACK_SNMP=m 116 + CONFIG_NF_CONNTRACK_PPTP=m 125 117 CONFIG_NF_CONNTRACK_TFTP=m 126 118 CONFIG_NF_CT_NETLINK=m 127 119 CONFIG_NF_TABLES=m 128 - CONFIG_NFT_COUNTER=m 129 120 CONFIG_NFT_CONNLIMIT=m 130 121 CONFIG_NFT_LOG=m 131 122 CONFIG_NFT_LIMIT=m ··· 211 200 CONFIG_NFT_DUP_IPV4=m 212 201 CONFIG_NFT_FIB_IPV4=m 213 202 CONFIG_NF_TABLES_ARP=y 214 - CONFIG_NF_LOG_ARP=m 215 203 CONFIG_IP_NF_IPTABLES=m 216 204 CONFIG_IP_NF_MATCH_AH=m 217 205 CONFIG_IP_NF_MATCH_ECN=m ··· 264 254 CONFIG_IP_SCTP=m 265 255 CONFIG_RDS=y 266 256 CONFIG_L2TP=m 257 + CONFIG_L2TP_V3=y 258 + CONFIG_L2TP_IP=m 259 + CONFIG_L2TP_ETH=m 267 260 CONFIG_BRIDGE=m 268 261 CONFIG_VLAN_8021Q=m 269 262 CONFIG_VLAN_8021Q_GVRP=y 270 263 CONFIG_VLAN_8021Q_MVRP=y 264 + CONFIG_LLC2=m 271 265 CONFIG_NET_SCHED=y 272 266 CONFIG_NET_SCH_HTB=m 273 267 CONFIG_NET_SCH_PRIO=m ··· 296 282 CONFIG_VIRTIO_VSOCKETS=m 297 283 CONFIG_NETLINK_DIAG=y 298 284 CONFIG_CGROUP_NET_PRIO=y 285 + CONFIG_BPF_STREAM_PARSER=y 299 286 CONFIG_BT=m 287 + CONFIG_BT_RFCOMM=m 288 + CONFIG_BT_RFCOMM_TTY=y 289 + CONFIG_BT_BNEP=m 290 + CONFIG_BT_BNEP_MC_FILTER=y 291 + CONFIG_BT_BNEP_PROTO_FILTER=y 292 + CONFIG_BT_HIDP=m 293 + CONFIG_BT_HS=y 300 294 CONFIG_BT_HCIBTUSB=m 301 - # CONFIG_BT_HCIBTUSB_BCM is not set 295 + CONFIG_BT_HCIBTUSB_AUTOSUSPEND=y 296 + CONFIG_BT_HCIBTUSB_MTK=y 297 + CONFIG_BT_HCIUART=m 298 + CONFIG_BT_HCIUART_BCSP=y 299 + CONFIG_BT_HCIUART_ATH3K=y 300 + CONFIG_BT_HCIUART_INTEL=y 301 + CONFIG_BT_HCIUART_AG6XX=y 302 + CONFIG_BT_HCIBCM203X=m 303 + CONFIG_BT_HCIBPA10X=m 304 + CONFIG_BT_HCIBFUSB=m 305 + CONFIG_BT_HCIDTL1=m 306 + CONFIG_BT_HCIBT3C=m 307 + CONFIG_BT_HCIBLUECARD=m 308 + CONFIG_BT_HCIVHCI=m 309 + CONFIG_BT_MRVL=m 310 + CONFIG_BT_ATH3K=m 311 + CONFIG_BT_VIRTIO=m 302 312 CONFIG_CFG80211=m 303 313 CONFIG_CFG80211_WEXT=y 304 314 CONFIG_MAC80211=m ··· 367 329 CONFIG_ZRAM=m 368 330 CONFIG_ZRAM_DEF_COMP_ZSTD=y 369 331 CONFIG_BLK_DEV_LOOP=y 370 - CONFIG_BLK_DEV_CRYPTOLOOP=y 371 332 CONFIG_BLK_DEV_NBD=m 372 333 CONFIG_BLK_DEV_RAM=y 373 334 CONFIG_BLK_DEV_RAM_SIZE=8192 ··· 523 486 CONFIG_PPP_MPPE=m 524 487 CONFIG_PPP_MULTILINK=y 525 488 CONFIG_PPPOE=m 489 + CONFIG_PPTP=m 526 490 CONFIG_PPPOL2TP=m 527 491 CONFIG_PPP_ASYNC=m 528 492 CONFIG_PPP_SYNC_TTY=m ··· 543 505 CONFIG_IWLWIFI=m 544 506 CONFIG_IWLDVM=m 545 507 CONFIG_IWLMVM=m 546 - CONFIG_IWLWIFI_BCAST_FILTERING=y 547 508 CONFIG_HOSTAP=m 548 509 CONFIG_MT7601U=m 549 510 CONFIG_RT2X00=m ··· 558 521 CONFIG_RTL8192CU=m 559 522 # CONFIG_RTLWIFI_DEBUG is not set 560 523 CONFIG_RTL8XXXU=m 524 + CONFIG_RTW88=m 525 + CONFIG_RTW88_8822BE=m 526 + CONFIG_RTW88_8822CE=m 527 + CONFIG_RTW88_8723DE=m 528 + CONFIG_RTW88_8821CE=m 529 + CONFIG_RTW89=m 530 + CONFIG_RTW89_8852AE=m 531 + CONFIG_RTW89_8852CE=m 561 532 CONFIG_ZD1211RW=m 562 533 CONFIG_USB_NET_RNDIS_WLAN=m 563 534 CONFIG_INPUT_MOUSEDEV=y ··· 696 651 CONFIG_USB_SERIAL_PL2303=m 697 652 CONFIG_USB_SERIAL_OPTION=m 698 653 CONFIG_USB_GADGET=y 654 + CONFIG_TYPEC=m 655 + CONFIG_TYPEC_TCPM=m 656 + CONFIG_TYPEC_TCPCI=m 657 + CONFIG_TYPEC_UCSI=m 658 + CONFIG_UCSI_ACPI=m 699 659 CONFIG_INFINIBAND=m 700 660 CONFIG_RTC_CLASS=y 701 661 CONFIG_RTC_DRV_EFI=y ··· 738 688 CONFIG_COMEDI_NI_PCIMIO=m 739 689 CONFIG_STAGING=y 740 690 CONFIG_R8188EU=m 741 - # CONFIG_88EU_AP_MODE is not set 742 691 CONFIG_PM_DEVFREQ=y 743 692 CONFIG_DEVFREQ_GOV_SIMPLE_ONDEMAND=y 744 693 CONFIG_DEVFREQ_GOV_PERFORMANCE=y ··· 821 772 CONFIG_CRYPTO_CHACHA20POLY1305=m 822 773 CONFIG_CRYPTO_HMAC=y 823 774 CONFIG_CRYPTO_VMAC=m 824 - CONFIG_CRYPTO_TGR192=m 825 775 CONFIG_CRYPTO_WP512=m 826 776 CONFIG_CRYPTO_ANUBIS=m 827 777 CONFIG_CRYPTO_BLOWFISH=m 828 778 CONFIG_CRYPTO_CAST5=m 829 779 CONFIG_CRYPTO_CAST6=m 830 780 CONFIG_CRYPTO_KHAZAD=m 831 - CONFIG_CRYPTO_SALSA20=m 832 781 CONFIG_CRYPTO_SEED=m 833 782 CONFIG_CRYPTO_SERPENT=m 834 783 CONFIG_CRYPTO_TEA=m
+2 -3
arch/loongarch/include/asm/Kbuild
··· 1 1 # SPDX-License-Identifier: GPL-2.0 2 2 generic-y += dma-contiguous.h 3 3 generic-y += export.h 4 + generic-y += mcs_spinlock.h 4 5 generic-y += parport.h 5 6 generic-y += early_ioremap.h 6 7 generic-y += qrwlock.h 7 - generic-y += qrwlock_types.h 8 - generic-y += spinlock.h 9 - generic-y += spinlock_types.h 8 + generic-y += qspinlock.h 10 9 generic-y += rwsem.h 11 10 generic-y += segment.h 12 11 generic-y += user.h
+5
arch/loongarch/include/asm/bootinfo.h
··· 40 40 extern struct loongson_board_info b_info; 41 41 extern struct loongson_system_configuration loongson_sysconf; 42 42 43 + static inline bool io_master(int cpu) 44 + { 45 + return test_bit(cpu, &loongson_sysconf.cores_io_master); 46 + } 47 + 43 48 #endif /* _ASM_BOOTINFO_H */
+49 -11
arch/loongarch/include/asm/bug.h
··· 2 2 #ifndef __ASM_BUG_H 3 3 #define __ASM_BUG_H 4 4 5 - #include <linux/compiler.h> 6 - 7 - #ifdef CONFIG_BUG 8 - 9 5 #include <asm/break.h> 6 + #include <linux/stringify.h> 10 7 11 - static inline void __noreturn BUG(void) 12 - { 13 - __asm__ __volatile__("break %0" : : "i" (BRK_BUG)); 14 - unreachable(); 15 - } 8 + #ifndef CONFIG_DEBUG_BUGVERBOSE 9 + #define _BUGVERBOSE_LOCATION(file, line) 10 + #else 11 + #define __BUGVERBOSE_LOCATION(file, line) \ 12 + .pushsection .rodata.str, "aMS", @progbits, 1; \ 13 + 10002: .string file; \ 14 + .popsection; \ 15 + \ 16 + .long 10002b - .; \ 17 + .short line; 18 + #define _BUGVERBOSE_LOCATION(file, line) __BUGVERBOSE_LOCATION(file, line) 19 + #endif 20 + 21 + #ifndef CONFIG_GENERIC_BUG 22 + #define __BUG_ENTRY(flags) 23 + #else 24 + #define __BUG_ENTRY(flags) \ 25 + .pushsection __bug_table, "aw"; \ 26 + .align 2; \ 27 + 10000: .long 10001f - .; \ 28 + _BUGVERBOSE_LOCATION(__FILE__, __LINE__) \ 29 + .short flags; \ 30 + .popsection; \ 31 + 10001: 32 + #endif 33 + 34 + #define ASM_BUG_FLAGS(flags) \ 35 + __BUG_ENTRY(flags) \ 36 + break BRK_BUG 37 + 38 + #define ASM_BUG() ASM_BUG_FLAGS(0) 39 + 40 + #define __BUG_FLAGS(flags) \ 41 + asm_inline volatile (__stringify(ASM_BUG_FLAGS(flags))); 42 + 43 + #define __WARN_FLAGS(flags) \ 44 + do { \ 45 + instrumentation_begin(); \ 46 + __BUG_FLAGS(BUGFLAG_WARNING|(flags)); \ 47 + instrumentation_end(); \ 48 + } while (0) 49 + 50 + #define BUG() \ 51 + do { \ 52 + instrumentation_begin(); \ 53 + __BUG_FLAGS(0); \ 54 + unreachable(); \ 55 + } while (0) 16 56 17 57 #define HAVE_ARCH_BUG 18 - 19 - #endif 20 58 21 59 #include <asm-generic/bug.h> 22 60
+48 -39
arch/loongarch/include/asm/cacheflush.h
··· 6 6 #define _ASM_CACHEFLUSH_H 7 7 8 8 #include <linux/mm.h> 9 - #include <asm/cpu-features.h> 9 + #include <asm/cpu-info.h> 10 10 #include <asm/cacheops.h> 11 11 12 - extern void local_flush_icache_range(unsigned long start, unsigned long end); 12 + static inline bool cache_present(struct cache_desc *cdesc) 13 + { 14 + return cdesc->flags & CACHE_PRESENT; 15 + } 16 + 17 + static inline bool cache_private(struct cache_desc *cdesc) 18 + { 19 + return cdesc->flags & CACHE_PRIVATE; 20 + } 21 + 22 + static inline bool cache_inclusive(struct cache_desc *cdesc) 23 + { 24 + return cdesc->flags & CACHE_INCLUSIVE; 25 + } 26 + 27 + static inline unsigned int cpu_last_level_cache_line_size(void) 28 + { 29 + int cache_present = boot_cpu_data.cache_leaves_present; 30 + 31 + return boot_cpu_data.cache_leaves[cache_present - 1].linesz; 32 + } 33 + 34 + asmlinkage void __flush_cache_all(void); 35 + void local_flush_icache_range(unsigned long start, unsigned long end); 13 36 14 37 #define flush_icache_range local_flush_icache_range 15 38 #define flush_icache_user_range local_flush_icache_range ··· 58 35 : \ 59 36 : "i" (op), "ZC" (*(unsigned char *)(addr))) 60 37 61 - static inline void flush_icache_line_indexed(unsigned long addr) 38 + static inline void flush_cache_line(int leaf, unsigned long addr) 62 39 { 63 - cache_op(Index_Invalidate_I, addr); 64 - } 65 - 66 - static inline void flush_dcache_line_indexed(unsigned long addr) 67 - { 68 - cache_op(Index_Writeback_Inv_D, addr); 69 - } 70 - 71 - static inline void flush_vcache_line_indexed(unsigned long addr) 72 - { 73 - cache_op(Index_Writeback_Inv_V, addr); 74 - } 75 - 76 - static inline void flush_scache_line_indexed(unsigned long addr) 77 - { 78 - cache_op(Index_Writeback_Inv_S, addr); 79 - } 80 - 81 - static inline void flush_icache_line(unsigned long addr) 82 - { 83 - cache_op(Hit_Invalidate_I, addr); 84 - } 85 - 86 - static inline void flush_dcache_line(unsigned long addr) 87 - { 88 - cache_op(Hit_Writeback_Inv_D, addr); 89 - } 90 - 91 - static inline void flush_vcache_line(unsigned long addr) 92 - { 93 - cache_op(Hit_Writeback_Inv_V, addr); 94 - } 95 - 96 - static inline void flush_scache_line(unsigned long addr) 97 - { 98 - cache_op(Hit_Writeback_Inv_S, addr); 40 + switch (leaf) { 41 + case Cache_LEAF0: 42 + cache_op(Index_Writeback_Inv_LEAF0, addr); 43 + break; 44 + case Cache_LEAF1: 45 + cache_op(Index_Writeback_Inv_LEAF1, addr); 46 + break; 47 + case Cache_LEAF2: 48 + cache_op(Index_Writeback_Inv_LEAF2, addr); 49 + break; 50 + case Cache_LEAF3: 51 + cache_op(Index_Writeback_Inv_LEAF3, addr); 52 + break; 53 + case Cache_LEAF4: 54 + cache_op(Index_Writeback_Inv_LEAF4, addr); 55 + break; 56 + case Cache_LEAF5: 57 + cache_op(Index_Writeback_Inv_LEAF5, addr); 58 + break; 59 + default: 60 + break; 61 + } 99 62 } 100 63 101 64 #include <asm-generic/cacheflush.h>
+21 -15
arch/loongarch/include/asm/cacheops.h
··· 8 8 #define __ASM_CACHEOPS_H 9 9 10 10 /* 11 - * Most cache ops are split into a 2 bit field identifying the cache, and a 3 11 + * Most cache ops are split into a 3 bit field identifying the cache, and a 2 12 12 * bit field identifying the cache operation. 13 13 */ 14 - #define CacheOp_Cache 0x03 15 - #define CacheOp_Op 0x1c 14 + #define CacheOp_Cache 0x07 15 + #define CacheOp_Op 0x18 16 16 17 - #define Cache_I 0x00 18 - #define Cache_D 0x01 19 - #define Cache_V 0x02 20 - #define Cache_S 0x03 17 + #define Cache_LEAF0 0x00 18 + #define Cache_LEAF1 0x01 19 + #define Cache_LEAF2 0x02 20 + #define Cache_LEAF3 0x03 21 + #define Cache_LEAF4 0x04 22 + #define Cache_LEAF5 0x05 21 23 22 24 #define Index_Invalidate 0x08 23 25 #define Index_Writeback_Inv 0x08 ··· 27 25 #define Hit_Writeback_Inv 0x10 28 26 #define CacheOp_User_Defined 0x18 29 27 30 - #define Index_Invalidate_I (Cache_I | Index_Invalidate) 31 - #define Index_Writeback_Inv_D (Cache_D | Index_Writeback_Inv) 32 - #define Index_Writeback_Inv_V (Cache_V | Index_Writeback_Inv) 33 - #define Index_Writeback_Inv_S (Cache_S | Index_Writeback_Inv) 34 - #define Hit_Invalidate_I (Cache_I | Hit_Invalidate) 35 - #define Hit_Writeback_Inv_D (Cache_D | Hit_Writeback_Inv) 36 - #define Hit_Writeback_Inv_V (Cache_V | Hit_Writeback_Inv) 37 - #define Hit_Writeback_Inv_S (Cache_S | Hit_Writeback_Inv) 28 + #define Index_Writeback_Inv_LEAF0 (Cache_LEAF0 | Index_Writeback_Inv) 29 + #define Index_Writeback_Inv_LEAF1 (Cache_LEAF1 | Index_Writeback_Inv) 30 + #define Index_Writeback_Inv_LEAF2 (Cache_LEAF2 | Index_Writeback_Inv) 31 + #define Index_Writeback_Inv_LEAF3 (Cache_LEAF3 | Index_Writeback_Inv) 32 + #define Index_Writeback_Inv_LEAF4 (Cache_LEAF4 | Index_Writeback_Inv) 33 + #define Index_Writeback_Inv_LEAF5 (Cache_LEAF5 | Index_Writeback_Inv) 34 + #define Hit_Writeback_Inv_LEAF0 (Cache_LEAF0 | Hit_Writeback_Inv) 35 + #define Hit_Writeback_Inv_LEAF1 (Cache_LEAF1 | Hit_Writeback_Inv) 36 + #define Hit_Writeback_Inv_LEAF2 (Cache_LEAF2 | Hit_Writeback_Inv) 37 + #define Hit_Writeback_Inv_LEAF3 (Cache_LEAF3 | Hit_Writeback_Inv) 38 + #define Hit_Writeback_Inv_LEAF4 (Cache_LEAF4 | Hit_Writeback_Inv) 39 + #define Hit_Writeback_Inv_LEAF5 (Cache_LEAF5 | Hit_Writeback_Inv) 38 40 39 41 #endif /* __ASM_CACHEOPS_H */
+4 -4
arch/loongarch/include/asm/cmpxchg.h
··· 61 61 return (old32 & mask) >> shift; 62 62 } 63 63 64 - static inline unsigned long __xchg(volatile void *ptr, unsigned long x, 65 - int size) 64 + static __always_inline unsigned long 65 + __xchg(volatile void *ptr, unsigned long x, int size) 66 66 { 67 67 switch (size) { 68 68 case 1: ··· 159 159 return (old32 & mask) >> shift; 160 160 } 161 161 162 - static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old, 163 - unsigned long new, unsigned int size) 162 + static __always_inline unsigned long 163 + __cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, unsigned int size) 164 164 { 165 165 switch (size) { 166 166 case 1:
-5
arch/loongarch/include/asm/cpu-features.h
··· 19 19 #define cpu_has_loongarch32 (cpu_data[0].isa_level & LOONGARCH_CPU_ISA_32BIT) 20 20 #define cpu_has_loongarch64 (cpu_data[0].isa_level & LOONGARCH_CPU_ISA_64BIT) 21 21 22 - #define cpu_icache_line_size() cpu_data[0].icache.linesz 23 - #define cpu_dcache_line_size() cpu_data[0].dcache.linesz 24 - #define cpu_vcache_line_size() cpu_data[0].vcache.linesz 25 - #define cpu_scache_line_size() cpu_data[0].scache.linesz 26 - 27 22 #ifdef CONFIG_32BIT 28 23 # define cpu_has_64bits (cpu_data[0].isa_level & LOONGARCH_CPU_ISA_64BIT) 29 24 # define cpu_vabits 31
+14 -7
arch/loongarch/include/asm/cpu-info.h
··· 10 10 11 11 #include <asm/loongarch.h> 12 12 13 + /* cache_desc->flags */ 14 + enum { 15 + CACHE_PRESENT = (1 << 0), 16 + CACHE_PRIVATE = (1 << 1), /* core private cache */ 17 + CACHE_INCLUSIVE = (1 << 2), /* include the inner level caches */ 18 + }; 19 + 13 20 /* 14 21 * Descriptor for a cache 15 22 */ 16 23 struct cache_desc { 17 - unsigned int waysize; /* Bytes per way */ 24 + unsigned char type; 25 + unsigned char level; 18 26 unsigned short sets; /* Number of lines per set */ 19 27 unsigned char ways; /* Number of ways */ 20 28 unsigned char linesz; /* Size of line in bytes */ 21 - unsigned char waybit; /* Bits to select in a cache set */ 22 29 unsigned char flags; /* Flags describing cache properties */ 23 30 }; 31 + 32 + #define CACHE_LEVEL_MAX 3 33 + #define CACHE_LEAVES_MAX 6 24 34 25 35 struct cpuinfo_loongarch { 26 36 u64 asid_cache; ··· 50 40 int tlbsizemtlb; 51 41 int tlbsizestlbsets; 52 42 int tlbsizestlbways; 53 - struct cache_desc icache; /* Primary I-cache */ 54 - struct cache_desc dcache; /* Primary D or combined I/D cache */ 55 - struct cache_desc vcache; /* Victim cache, between pcache and scache */ 56 - struct cache_desc scache; /* Secondary cache */ 57 - struct cache_desc tcache; /* Tertiary/split secondary cache */ 43 + int cache_leaves_present; /* number of cache_leaves[] elements */ 44 + struct cache_desc cache_leaves[CACHE_LEAVES_MAX]; 58 45 int core; /* physical core number in package */ 59 46 int package;/* physical package number */ 60 47 int vabits; /* Virtual Address size in bits */
+37
arch/loongarch/include/asm/elf.h
··· 74 74 #define R_LARCH_SUB64 56 75 75 #define R_LARCH_GNU_VTINHERIT 57 76 76 #define R_LARCH_GNU_VTENTRY 58 77 + #define R_LARCH_B16 64 78 + #define R_LARCH_B21 65 79 + #define R_LARCH_B26 66 80 + #define R_LARCH_ABS_HI20 67 81 + #define R_LARCH_ABS_LO12 68 82 + #define R_LARCH_ABS64_LO20 69 83 + #define R_LARCH_ABS64_HI12 70 84 + #define R_LARCH_PCALA_HI20 71 85 + #define R_LARCH_PCALA_LO12 72 86 + #define R_LARCH_PCALA64_LO20 73 87 + #define R_LARCH_PCALA64_HI12 74 88 + #define R_LARCH_GOT_PC_HI20 75 89 + #define R_LARCH_GOT_PC_LO12 76 90 + #define R_LARCH_GOT64_PC_LO20 77 91 + #define R_LARCH_GOT64_PC_HI12 78 92 + #define R_LARCH_GOT_HI20 79 93 + #define R_LARCH_GOT_LO12 80 94 + #define R_LARCH_GOT64_LO20 81 95 + #define R_LARCH_GOT64_HI12 82 96 + #define R_LARCH_TLS_LE_HI20 83 97 + #define R_LARCH_TLS_LE_LO12 84 98 + #define R_LARCH_TLS_LE64_LO20 85 99 + #define R_LARCH_TLS_LE64_HI12 86 100 + #define R_LARCH_TLS_IE_PC_HI20 87 101 + #define R_LARCH_TLS_IE_PC_LO12 88 102 + #define R_LARCH_TLS_IE64_PC_LO20 89 103 + #define R_LARCH_TLS_IE64_PC_HI12 90 104 + #define R_LARCH_TLS_IE_HI20 91 105 + #define R_LARCH_TLS_IE_LO12 92 106 + #define R_LARCH_TLS_IE64_LO20 93 107 + #define R_LARCH_TLS_IE64_HI12 94 108 + #define R_LARCH_TLS_LD_PC_HI20 95 109 + #define R_LARCH_TLS_LD_HI20 96 110 + #define R_LARCH_TLS_GD_PC_HI20 97 111 + #define R_LARCH_TLS_GD_HI20 98 112 + #define R_LARCH_32_PCREL 99 113 + #define R_LARCH_RELAX 100 77 114 78 115 #ifndef ELF_ARCH 79 116
+15
arch/loongarch/include/asm/fixmap.h
··· 10 10 11 11 #define NR_FIX_BTMAPS 64 12 12 13 + enum fixed_addresses { 14 + FIX_HOLE, 15 + FIX_EARLYCON_MEM_BASE, 16 + __end_of_fixed_addresses 17 + }; 18 + 19 + #define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT) 20 + #define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE) 21 + #define FIXMAP_PAGE_IO PAGE_KERNEL_SUC 22 + 23 + extern void __set_fixmap(enum fixed_addresses idx, 24 + phys_addr_t phys, pgprot_t flags); 25 + 26 + #include <asm-generic/fixmap.h> 27 + 13 28 #endif
+405 -5
arch/loongarch/include/asm/inst.h
··· 8 8 #include <linux/types.h> 9 9 #include <asm/asm.h> 10 10 11 + #define INSN_BREAK 0x002a0000 12 + 11 13 #define ADDR_IMMMASK_LU52ID 0xFFF0000000000000 12 14 #define ADDR_IMMMASK_LU32ID 0x000FFFFF00000000 13 15 #define ADDR_IMMMASK_ADDU16ID 0x00000000FFFF0000 ··· 20 18 21 19 #define ADDR_IMM(addr, INSN) ((addr & ADDR_IMMMASK_##INSN) >> ADDR_IMMSHIFT_##INSN) 22 20 21 + enum reg0i26_op { 22 + b_op = 0x14, 23 + bl_op = 0x15, 24 + }; 25 + 23 26 enum reg1i20_op { 24 27 lu12iw_op = 0x0a, 25 28 lu32id_op = 0x0b, 29 + pcaddu12i_op = 0x0e, 30 + pcaddu18i_op = 0x0f, 26 31 }; 27 32 28 33 enum reg1i21_op { ··· 37 28 bnez_op = 0x11, 38 29 }; 39 30 31 + enum reg2_op { 32 + revb2h_op = 0x0c, 33 + revb4h_op = 0x0d, 34 + revb2w_op = 0x0e, 35 + revbd_op = 0x0f, 36 + revh2w_op = 0x10, 37 + revhd_op = 0x11, 38 + }; 39 + 40 + enum reg2i5_op { 41 + slliw_op = 0x81, 42 + srliw_op = 0x89, 43 + sraiw_op = 0x91, 44 + }; 45 + 46 + enum reg2i6_op { 47 + sllid_op = 0x41, 48 + srlid_op = 0x45, 49 + sraid_op = 0x49, 50 + }; 51 + 40 52 enum reg2i12_op { 41 53 addiw_op = 0x0a, 42 54 addid_op = 0x0b, 43 55 lu52id_op = 0x0c, 56 + andi_op = 0x0d, 57 + ori_op = 0x0e, 58 + xori_op = 0x0f, 44 59 ldb_op = 0xa0, 45 60 ldh_op = 0xa1, 46 61 ldw_op = 0xa2, ··· 73 40 sth_op = 0xa5, 74 41 stw_op = 0xa6, 75 42 std_op = 0xa7, 43 + ldbu_op = 0xa8, 44 + ldhu_op = 0xa9, 45 + ldwu_op = 0xaa, 46 + }; 47 + 48 + enum reg2i14_op { 49 + llw_op = 0x20, 50 + scw_op = 0x21, 51 + lld_op = 0x22, 52 + scd_op = 0x23, 53 + ldptrw_op = 0x24, 54 + stptrw_op = 0x25, 55 + ldptrd_op = 0x26, 56 + stptrd_op = 0x27, 76 57 }; 77 58 78 59 enum reg2i16_op { ··· 97 50 bge_op = 0x19, 98 51 bltu_op = 0x1a, 99 52 bgeu_op = 0x1b, 53 + }; 54 + 55 + enum reg2bstrd_op { 56 + bstrinsd_op = 0x2, 57 + bstrpickd_op = 0x3, 58 + }; 59 + 60 + enum reg3_op { 61 + addw_op = 0x20, 62 + addd_op = 0x21, 63 + subw_op = 0x22, 64 + subd_op = 0x23, 65 + nor_op = 0x28, 66 + and_op = 0x29, 67 + or_op = 0x2a, 68 + xor_op = 0x2b, 69 + orn_op = 0x2c, 70 + andn_op = 0x2d, 71 + sllw_op = 0x2e, 72 + srlw_op = 0x2f, 73 + sraw_op = 0x30, 74 + slld_op = 0x31, 75 + srld_op = 0x32, 76 + srad_op = 0x33, 77 + mulw_op = 0x38, 78 + mulhw_op = 0x39, 79 + mulhwu_op = 0x3a, 80 + muld_op = 0x3b, 81 + mulhd_op = 0x3c, 82 + mulhdu_op = 0x3d, 83 + divw_op = 0x40, 84 + modw_op = 0x41, 85 + divwu_op = 0x42, 86 + modwu_op = 0x43, 87 + divd_op = 0x44, 88 + modd_op = 0x45, 89 + divdu_op = 0x46, 90 + moddu_op = 0x47, 91 + ldxb_op = 0x7000, 92 + ldxh_op = 0x7008, 93 + ldxw_op = 0x7010, 94 + ldxd_op = 0x7018, 95 + stxb_op = 0x7020, 96 + stxh_op = 0x7028, 97 + stxw_op = 0x7030, 98 + stxd_op = 0x7038, 99 + ldxbu_op = 0x7040, 100 + ldxhu_op = 0x7048, 101 + ldxwu_op = 0x7050, 102 + amswapw_op = 0x70c0, 103 + amswapd_op = 0x70c1, 104 + amaddw_op = 0x70c2, 105 + amaddd_op = 0x70c3, 106 + amandw_op = 0x70c4, 107 + amandd_op = 0x70c5, 108 + amorw_op = 0x70c6, 109 + amord_op = 0x70c7, 110 + amxorw_op = 0x70c8, 111 + amxord_op = 0x70c9, 112 + }; 113 + 114 + enum reg3sa2_op { 115 + alslw_op = 0x02, 116 + alslwu_op = 0x03, 117 + alsld_op = 0x16, 100 118 }; 101 119 102 120 struct reg0i26_format { ··· 183 71 unsigned int opcode : 6; 184 72 }; 185 73 74 + struct reg2_format { 75 + unsigned int rd : 5; 76 + unsigned int rj : 5; 77 + unsigned int opcode : 22; 78 + }; 79 + 80 + struct reg2i5_format { 81 + unsigned int rd : 5; 82 + unsigned int rj : 5; 83 + unsigned int immediate : 5; 84 + unsigned int opcode : 17; 85 + }; 86 + 87 + struct reg2i6_format { 88 + unsigned int rd : 5; 89 + unsigned int rj : 5; 90 + unsigned int immediate : 6; 91 + unsigned int opcode : 16; 92 + }; 93 + 186 94 struct reg2i12_format { 187 95 unsigned int rd : 5; 188 96 unsigned int rj : 5; 189 97 unsigned int immediate : 12; 190 98 unsigned int opcode : 10; 99 + }; 100 + 101 + struct reg2i14_format { 102 + unsigned int rd : 5; 103 + unsigned int rj : 5; 104 + unsigned int immediate : 14; 105 + unsigned int opcode : 8; 191 106 }; 192 107 193 108 struct reg2i16_format { ··· 224 85 unsigned int opcode : 6; 225 86 }; 226 87 88 + struct reg2bstrd_format { 89 + unsigned int rd : 5; 90 + unsigned int rj : 5; 91 + unsigned int lsbd : 6; 92 + unsigned int msbd : 6; 93 + unsigned int opcode : 10; 94 + }; 95 + 96 + struct reg3_format { 97 + unsigned int rd : 5; 98 + unsigned int rj : 5; 99 + unsigned int rk : 5; 100 + unsigned int opcode : 17; 101 + }; 102 + 103 + struct reg3sa2_format { 104 + unsigned int rd : 5; 105 + unsigned int rj : 5; 106 + unsigned int rk : 5; 107 + unsigned int immediate : 2; 108 + unsigned int opcode : 15; 109 + }; 110 + 227 111 union loongarch_instruction { 228 112 unsigned int word; 229 - struct reg0i26_format reg0i26_format; 230 - struct reg1i20_format reg1i20_format; 231 - struct reg1i21_format reg1i21_format; 232 - struct reg2i12_format reg2i12_format; 233 - struct reg2i16_format reg2i16_format; 113 + struct reg0i26_format reg0i26_format; 114 + struct reg1i20_format reg1i20_format; 115 + struct reg1i21_format reg1i21_format; 116 + struct reg2_format reg2_format; 117 + struct reg2i5_format reg2i5_format; 118 + struct reg2i6_format reg2i6_format; 119 + struct reg2i12_format reg2i12_format; 120 + struct reg2i14_format reg2i14_format; 121 + struct reg2i16_format reg2i16_format; 122 + struct reg2bstrd_format reg2bstrd_format; 123 + struct reg3_format reg3_format; 124 + struct reg3sa2_format reg3sa2_format; 234 125 }; 235 126 236 127 #define LOONGARCH_INSN_SIZE sizeof(union loongarch_instruction) ··· 334 165 u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm); 335 166 u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm); 336 167 u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, unsigned long pc, unsigned long dest); 168 + 169 + static inline bool signed_imm_check(long val, unsigned int bit) 170 + { 171 + return -(1L << (bit - 1)) <= val && val < (1L << (bit - 1)); 172 + } 173 + 174 + static inline bool unsigned_imm_check(unsigned long val, unsigned int bit) 175 + { 176 + return val < (1UL << bit); 177 + } 178 + 179 + #define DEF_EMIT_REG0I26_FORMAT(NAME, OP) \ 180 + static inline void emit_##NAME(union loongarch_instruction *insn, \ 181 + int offset) \ 182 + { \ 183 + unsigned int immediate_l, immediate_h; \ 184 + \ 185 + immediate_l = offset & 0xffff; \ 186 + offset >>= 16; \ 187 + immediate_h = offset & 0x3ff; \ 188 + \ 189 + insn->reg0i26_format.opcode = OP; \ 190 + insn->reg0i26_format.immediate_l = immediate_l; \ 191 + insn->reg0i26_format.immediate_h = immediate_h; \ 192 + } 193 + 194 + DEF_EMIT_REG0I26_FORMAT(b, b_op) 195 + 196 + #define DEF_EMIT_REG1I20_FORMAT(NAME, OP) \ 197 + static inline void emit_##NAME(union loongarch_instruction *insn, \ 198 + enum loongarch_gpr rd, int imm) \ 199 + { \ 200 + insn->reg1i20_format.opcode = OP; \ 201 + insn->reg1i20_format.immediate = imm; \ 202 + insn->reg1i20_format.rd = rd; \ 203 + } 204 + 205 + DEF_EMIT_REG1I20_FORMAT(lu12iw, lu12iw_op) 206 + DEF_EMIT_REG1I20_FORMAT(lu32id, lu32id_op) 207 + DEF_EMIT_REG1I20_FORMAT(pcaddu18i, pcaddu18i_op) 208 + 209 + #define DEF_EMIT_REG2_FORMAT(NAME, OP) \ 210 + static inline void emit_##NAME(union loongarch_instruction *insn, \ 211 + enum loongarch_gpr rd, \ 212 + enum loongarch_gpr rj) \ 213 + { \ 214 + insn->reg2_format.opcode = OP; \ 215 + insn->reg2_format.rd = rd; \ 216 + insn->reg2_format.rj = rj; \ 217 + } 218 + 219 + DEF_EMIT_REG2_FORMAT(revb2h, revb2h_op) 220 + DEF_EMIT_REG2_FORMAT(revb2w, revb2w_op) 221 + DEF_EMIT_REG2_FORMAT(revbd, revbd_op) 222 + 223 + #define DEF_EMIT_REG2I5_FORMAT(NAME, OP) \ 224 + static inline void emit_##NAME(union loongarch_instruction *insn, \ 225 + enum loongarch_gpr rd, \ 226 + enum loongarch_gpr rj, \ 227 + int imm) \ 228 + { \ 229 + insn->reg2i5_format.opcode = OP; \ 230 + insn->reg2i5_format.immediate = imm; \ 231 + insn->reg2i5_format.rd = rd; \ 232 + insn->reg2i5_format.rj = rj; \ 233 + } 234 + 235 + DEF_EMIT_REG2I5_FORMAT(slliw, slliw_op) 236 + DEF_EMIT_REG2I5_FORMAT(srliw, srliw_op) 237 + DEF_EMIT_REG2I5_FORMAT(sraiw, sraiw_op) 238 + 239 + #define DEF_EMIT_REG2I6_FORMAT(NAME, OP) \ 240 + static inline void emit_##NAME(union loongarch_instruction *insn, \ 241 + enum loongarch_gpr rd, \ 242 + enum loongarch_gpr rj, \ 243 + int imm) \ 244 + { \ 245 + insn->reg2i6_format.opcode = OP; \ 246 + insn->reg2i6_format.immediate = imm; \ 247 + insn->reg2i6_format.rd = rd; \ 248 + insn->reg2i6_format.rj = rj; \ 249 + } 250 + 251 + DEF_EMIT_REG2I6_FORMAT(sllid, sllid_op) 252 + DEF_EMIT_REG2I6_FORMAT(srlid, srlid_op) 253 + DEF_EMIT_REG2I6_FORMAT(sraid, sraid_op) 254 + 255 + #define DEF_EMIT_REG2I12_FORMAT(NAME, OP) \ 256 + static inline void emit_##NAME(union loongarch_instruction *insn, \ 257 + enum loongarch_gpr rd, \ 258 + enum loongarch_gpr rj, \ 259 + int imm) \ 260 + { \ 261 + insn->reg2i12_format.opcode = OP; \ 262 + insn->reg2i12_format.immediate = imm; \ 263 + insn->reg2i12_format.rd = rd; \ 264 + insn->reg2i12_format.rj = rj; \ 265 + } 266 + 267 + DEF_EMIT_REG2I12_FORMAT(addiw, addiw_op) 268 + DEF_EMIT_REG2I12_FORMAT(addid, addid_op) 269 + DEF_EMIT_REG2I12_FORMAT(lu52id, lu52id_op) 270 + DEF_EMIT_REG2I12_FORMAT(andi, andi_op) 271 + DEF_EMIT_REG2I12_FORMAT(ori, ori_op) 272 + DEF_EMIT_REG2I12_FORMAT(xori, xori_op) 273 + DEF_EMIT_REG2I12_FORMAT(ldbu, ldbu_op) 274 + DEF_EMIT_REG2I12_FORMAT(ldhu, ldhu_op) 275 + DEF_EMIT_REG2I12_FORMAT(ldwu, ldwu_op) 276 + DEF_EMIT_REG2I12_FORMAT(ldd, ldd_op) 277 + DEF_EMIT_REG2I12_FORMAT(stb, stb_op) 278 + DEF_EMIT_REG2I12_FORMAT(sth, sth_op) 279 + DEF_EMIT_REG2I12_FORMAT(stw, stw_op) 280 + DEF_EMIT_REG2I12_FORMAT(std, std_op) 281 + 282 + #define DEF_EMIT_REG2I14_FORMAT(NAME, OP) \ 283 + static inline void emit_##NAME(union loongarch_instruction *insn, \ 284 + enum loongarch_gpr rd, \ 285 + enum loongarch_gpr rj, \ 286 + int imm) \ 287 + { \ 288 + insn->reg2i14_format.opcode = OP; \ 289 + insn->reg2i14_format.immediate = imm; \ 290 + insn->reg2i14_format.rd = rd; \ 291 + insn->reg2i14_format.rj = rj; \ 292 + } 293 + 294 + DEF_EMIT_REG2I14_FORMAT(llw, llw_op) 295 + DEF_EMIT_REG2I14_FORMAT(scw, scw_op) 296 + DEF_EMIT_REG2I14_FORMAT(lld, lld_op) 297 + DEF_EMIT_REG2I14_FORMAT(scd, scd_op) 298 + DEF_EMIT_REG2I14_FORMAT(ldptrw, ldptrw_op) 299 + DEF_EMIT_REG2I14_FORMAT(stptrw, stptrw_op) 300 + DEF_EMIT_REG2I14_FORMAT(ldptrd, ldptrd_op) 301 + DEF_EMIT_REG2I14_FORMAT(stptrd, stptrd_op) 302 + 303 + #define DEF_EMIT_REG2I16_FORMAT(NAME, OP) \ 304 + static inline void emit_##NAME(union loongarch_instruction *insn, \ 305 + enum loongarch_gpr rj, \ 306 + enum loongarch_gpr rd, \ 307 + int offset) \ 308 + { \ 309 + insn->reg2i16_format.opcode = OP; \ 310 + insn->reg2i16_format.immediate = offset; \ 311 + insn->reg2i16_format.rj = rj; \ 312 + insn->reg2i16_format.rd = rd; \ 313 + } 314 + 315 + DEF_EMIT_REG2I16_FORMAT(beq, beq_op) 316 + DEF_EMIT_REG2I16_FORMAT(bne, bne_op) 317 + DEF_EMIT_REG2I16_FORMAT(blt, blt_op) 318 + DEF_EMIT_REG2I16_FORMAT(bge, bge_op) 319 + DEF_EMIT_REG2I16_FORMAT(bltu, bltu_op) 320 + DEF_EMIT_REG2I16_FORMAT(bgeu, bgeu_op) 321 + DEF_EMIT_REG2I16_FORMAT(jirl, jirl_op) 322 + 323 + #define DEF_EMIT_REG2BSTRD_FORMAT(NAME, OP) \ 324 + static inline void emit_##NAME(union loongarch_instruction *insn, \ 325 + enum loongarch_gpr rd, \ 326 + enum loongarch_gpr rj, \ 327 + int msbd, \ 328 + int lsbd) \ 329 + { \ 330 + insn->reg2bstrd_format.opcode = OP; \ 331 + insn->reg2bstrd_format.msbd = msbd; \ 332 + insn->reg2bstrd_format.lsbd = lsbd; \ 333 + insn->reg2bstrd_format.rj = rj; \ 334 + insn->reg2bstrd_format.rd = rd; \ 335 + } 336 + 337 + DEF_EMIT_REG2BSTRD_FORMAT(bstrpickd, bstrpickd_op) 338 + 339 + #define DEF_EMIT_REG3_FORMAT(NAME, OP) \ 340 + static inline void emit_##NAME(union loongarch_instruction *insn, \ 341 + enum loongarch_gpr rd, \ 342 + enum loongarch_gpr rj, \ 343 + enum loongarch_gpr rk) \ 344 + { \ 345 + insn->reg3_format.opcode = OP; \ 346 + insn->reg3_format.rd = rd; \ 347 + insn->reg3_format.rj = rj; \ 348 + insn->reg3_format.rk = rk; \ 349 + } 350 + 351 + DEF_EMIT_REG3_FORMAT(addd, addd_op) 352 + DEF_EMIT_REG3_FORMAT(subd, subd_op) 353 + DEF_EMIT_REG3_FORMAT(muld, muld_op) 354 + DEF_EMIT_REG3_FORMAT(divdu, divdu_op) 355 + DEF_EMIT_REG3_FORMAT(moddu, moddu_op) 356 + DEF_EMIT_REG3_FORMAT(and, and_op) 357 + DEF_EMIT_REG3_FORMAT(or, or_op) 358 + DEF_EMIT_REG3_FORMAT(xor, xor_op) 359 + DEF_EMIT_REG3_FORMAT(sllw, sllw_op) 360 + DEF_EMIT_REG3_FORMAT(slld, slld_op) 361 + DEF_EMIT_REG3_FORMAT(srlw, srlw_op) 362 + DEF_EMIT_REG3_FORMAT(srld, srld_op) 363 + DEF_EMIT_REG3_FORMAT(sraw, sraw_op) 364 + DEF_EMIT_REG3_FORMAT(srad, srad_op) 365 + DEF_EMIT_REG3_FORMAT(ldxbu, ldxbu_op) 366 + DEF_EMIT_REG3_FORMAT(ldxhu, ldxhu_op) 367 + DEF_EMIT_REG3_FORMAT(ldxwu, ldxwu_op) 368 + DEF_EMIT_REG3_FORMAT(ldxd, ldxd_op) 369 + DEF_EMIT_REG3_FORMAT(stxb, stxb_op) 370 + DEF_EMIT_REG3_FORMAT(stxh, stxh_op) 371 + DEF_EMIT_REG3_FORMAT(stxw, stxw_op) 372 + DEF_EMIT_REG3_FORMAT(stxd, stxd_op) 373 + DEF_EMIT_REG3_FORMAT(amaddw, amaddw_op) 374 + DEF_EMIT_REG3_FORMAT(amaddd, amaddd_op) 375 + DEF_EMIT_REG3_FORMAT(amandw, amandw_op) 376 + DEF_EMIT_REG3_FORMAT(amandd, amandd_op) 377 + DEF_EMIT_REG3_FORMAT(amorw, amorw_op) 378 + DEF_EMIT_REG3_FORMAT(amord, amord_op) 379 + DEF_EMIT_REG3_FORMAT(amxorw, amxorw_op) 380 + DEF_EMIT_REG3_FORMAT(amxord, amxord_op) 381 + DEF_EMIT_REG3_FORMAT(amswapw, amswapw_op) 382 + DEF_EMIT_REG3_FORMAT(amswapd, amswapd_op) 383 + 384 + #define DEF_EMIT_REG3SA2_FORMAT(NAME, OP) \ 385 + static inline void emit_##NAME(union loongarch_instruction *insn, \ 386 + enum loongarch_gpr rd, \ 387 + enum loongarch_gpr rj, \ 388 + enum loongarch_gpr rk, \ 389 + int imm) \ 390 + { \ 391 + insn->reg3sa2_format.opcode = OP; \ 392 + insn->reg3sa2_format.immediate = imm; \ 393 + insn->reg3sa2_format.rd = rd; \ 394 + insn->reg3sa2_format.rj = rj; \ 395 + insn->reg3sa2_format.rk = rk; \ 396 + } 397 + 398 + DEF_EMIT_REG3SA2_FORMAT(alsld, alsld_op) 337 399 338 400 #endif /* _ASM_INST_H */
+23 -52
arch/loongarch/include/asm/io.h
··· 27 27 #define early_memremap early_ioremap 28 28 #define early_memunmap early_iounmap 29 29 30 + #ifdef CONFIG_ARCH_IOREMAP 31 + 30 32 static inline void __iomem *ioremap_prot(phys_addr_t offset, unsigned long size, 31 33 unsigned long prot_val) 32 34 { 33 - if (prot_val == _CACHE_CC) 35 + if (prot_val & _CACHE_CC) 34 36 return (void __iomem *)(unsigned long)(CACHE_BASE + offset); 35 37 else 36 38 return (void __iomem *)(unsigned long)(UNCACHE_BASE + offset); 37 39 } 38 40 41 + #define ioremap(offset, size) \ 42 + ioremap_prot((offset), (size), pgprot_val(PAGE_KERNEL_SUC)) 43 + 44 + #define iounmap(addr) ((void)(addr)) 45 + 46 + #endif 47 + 39 48 /* 40 - * ioremap - map bus memory into CPU space 49 + * On LoongArch, ioremap() has two variants, ioremap_wc() and ioremap_cache(). 50 + * They map bus memory into CPU space, the mapped memory is marked uncachable 51 + * (_CACHE_SUC), uncachable but accelerated by write-combine (_CACHE_WUC) and 52 + * cachable (_CACHE_CC) respectively for CPU access. 53 + * 41 54 * @offset: bus address of the memory 42 55 * @size: size of the resource to map 43 - * 44 - * ioremap performs a platform specific sequence of operations to 45 - * make bus memory CPU accessible via the readb/readw/readl/writeb/ 46 - * writew/writel functions and the other mmio helpers. The returned 47 - * address is not guaranteed to be usable directly as a virtual 48 - * address. 49 56 */ 50 - #define ioremap(offset, size) \ 51 - ioremap_prot((offset), (size), _CACHE_SUC) 57 + #define ioremap_wc(offset, size) \ 58 + ioremap_prot((offset), (size), pgprot_val(PAGE_KERNEL_WUC)) 52 59 53 - /* 54 - * ioremap_wc - map bus memory into CPU space 55 - * @offset: bus address of the memory 56 - * @size: size of the resource to map 57 - * 58 - * ioremap_wc performs a platform specific sequence of operations to 59 - * make bus memory CPU accessible via the readb/readw/readl/writeb/ 60 - * writew/writel functions and the other mmio helpers. The returned 61 - * address is not guaranteed to be usable directly as a virtual 62 - * address. 63 - * 64 - * This version of ioremap ensures that the memory is marked uncachable 65 - * but accelerated by means of write-combining feature. It is specifically 66 - * useful for PCIe prefetchable windows, which may vastly improve a 67 - * communications performance. If it was determined on boot stage, what 68 - * CPU CCA doesn't support WUC, the method shall fall-back to the 69 - * _CACHE_SUC option (see cpu_probe() method). 70 - */ 71 - #define ioremap_wc(offset, size) \ 72 - ioremap_prot((offset), (size), _CACHE_WUC) 73 - 74 - /* 75 - * ioremap_cache - map bus memory into CPU space 76 - * @offset: bus address of the memory 77 - * @size: size of the resource to map 78 - * 79 - * ioremap_cache performs a platform specific sequence of operations to 80 - * make bus memory CPU accessible via the readb/readw/readl/writeb/ 81 - * writew/writel functions and the other mmio helpers. The returned 82 - * address is not guaranteed to be usable directly as a virtual 83 - * address. 84 - * 85 - * This version of ioremap ensures that the memory is marked cachable by 86 - * the CPU. Also enables full write-combining. Useful for some 87 - * memory-like regions on I/O busses. 88 - */ 89 - #define ioremap_cache(offset, size) \ 90 - ioremap_prot((offset), (size), _CACHE_CC) 91 - 92 - static inline void iounmap(const volatile void __iomem *addr) 93 - { 94 - } 60 + #define ioremap_cache(offset, size) \ 61 + ioremap_prot((offset), (size), pgprot_val(PAGE_KERNEL)) 95 62 96 63 #define mmiowb() asm volatile ("dbar 0" ::: "memory") 97 64 ··· 73 106 #define memcpy_toio(c, a, l) __memcpy_toio((c), (a), (l)) 74 107 75 108 #include <asm-generic/io.h> 109 + 110 + #define ARCH_HAS_VALID_PHYS_ADDR_RANGE 111 + extern int valid_phys_addr_range(phys_addr_t addr, size_t size); 112 + extern int valid_mmap_phys_addr_range(unsigned long pfn, size_t size); 76 113 77 114 #endif /* _ASM_IO_H */
+60
arch/loongarch/include/asm/kexec.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * kexec.h for kexec 4 + * 5 + * Copyright (C) 2022 Loongson Technology Corporation Limited 6 + */ 7 + 8 + #ifndef _ASM_KEXEC_H 9 + #define _ASM_KEXEC_H 10 + 11 + #include <asm/stacktrace.h> 12 + #include <asm/page.h> 13 + 14 + /* Maximum physical address we can use pages from */ 15 + #define KEXEC_SOURCE_MEMORY_LIMIT (-1UL) 16 + /* Maximum address we can reach in physical address mode */ 17 + #define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL) 18 + /* Maximum address we can use for the control code buffer */ 19 + #define KEXEC_CONTROL_MEMORY_LIMIT (-1UL) 20 + 21 + /* Reserve a page for the control code buffer */ 22 + #define KEXEC_CONTROL_PAGE_SIZE PAGE_SIZE 23 + 24 + /* The native architecture */ 25 + #define KEXEC_ARCH KEXEC_ARCH_LOONGARCH 26 + 27 + static inline void crash_setup_regs(struct pt_regs *newregs, 28 + struct pt_regs *oldregs) 29 + { 30 + if (oldregs) 31 + memcpy(newregs, oldregs, sizeof(*newregs)); 32 + else 33 + prepare_frametrace(newregs); 34 + } 35 + 36 + #define ARCH_HAS_KIMAGE_ARCH 37 + 38 + struct kimage_arch { 39 + unsigned long efi_boot; 40 + unsigned long cmdline_ptr; 41 + unsigned long systable_ptr; 42 + }; 43 + 44 + typedef void (*do_kexec_t)(unsigned long efi_boot, 45 + unsigned long cmdline_ptr, 46 + unsigned long systable_ptr, 47 + unsigned long start_addr, 48 + unsigned long first_ind_entry); 49 + 50 + struct kimage; 51 + extern const unsigned char relocate_new_kernel[]; 52 + extern const size_t relocate_new_kernel_size; 53 + extern void kexec_reboot(void); 54 + 55 + #ifdef CONFIG_SMP 56 + extern atomic_t kexec_ready_to_reboot; 57 + extern const unsigned char kexec_smp_wait[]; 58 + #endif 59 + 60 + #endif /* !_ASM_KEXEC_H */
+6 -27
arch/loongarch/include/asm/loongarch.h
··· 187 187 #define CPUCFG16_L3_DINCL BIT(16) 188 188 189 189 #define LOONGARCH_CPUCFG17 0x11 190 - #define CPUCFG17_L1I_WAYS_M GENMASK(15, 0) 191 - #define CPUCFG17_L1I_SETS_M GENMASK(23, 16) 192 - #define CPUCFG17_L1I_SIZE_M GENMASK(30, 24) 193 - #define CPUCFG17_L1I_WAYS 0 194 - #define CPUCFG17_L1I_SETS 16 195 - #define CPUCFG17_L1I_SIZE 24 196 - 197 190 #define LOONGARCH_CPUCFG18 0x12 198 - #define CPUCFG18_L1D_WAYS_M GENMASK(15, 0) 199 - #define CPUCFG18_L1D_SETS_M GENMASK(23, 16) 200 - #define CPUCFG18_L1D_SIZE_M GENMASK(30, 24) 201 - #define CPUCFG18_L1D_WAYS 0 202 - #define CPUCFG18_L1D_SETS 16 203 - #define CPUCFG18_L1D_SIZE 24 204 - 205 191 #define LOONGARCH_CPUCFG19 0x13 206 - #define CPUCFG19_L2_WAYS_M GENMASK(15, 0) 207 - #define CPUCFG19_L2_SETS_M GENMASK(23, 16) 208 - #define CPUCFG19_L2_SIZE_M GENMASK(30, 24) 209 - #define CPUCFG19_L2_WAYS 0 210 - #define CPUCFG19_L2_SETS 16 211 - #define CPUCFG19_L2_SIZE 24 212 - 213 192 #define LOONGARCH_CPUCFG20 0x14 214 - #define CPUCFG20_L3_WAYS_M GENMASK(15, 0) 215 - #define CPUCFG20_L3_SETS_M GENMASK(23, 16) 216 - #define CPUCFG20_L3_SIZE_M GENMASK(30, 24) 217 - #define CPUCFG20_L3_WAYS 0 218 - #define CPUCFG20_L3_SETS 16 219 - #define CPUCFG20_L3_SIZE 24 193 + #define CPUCFG_CACHE_WAYS_M GENMASK(15, 0) 194 + #define CPUCFG_CACHE_SETS_M GENMASK(23, 16) 195 + #define CPUCFG_CACHE_LSIZE_M GENMASK(30, 24) 196 + #define CPUCFG_CACHE_WAYS 0 197 + #define CPUCFG_CACHE_SETS 16 198 + #define CPUCFG_CACHE_LSIZE 24 220 199 221 200 #define LOONGARCH_CPUCFG48 0x30 222 201 #define CPUCFG48_MCSR_LCK BIT(0)
+25 -2
arch/loongarch/include/asm/module.h
··· 17 17 }; 18 18 19 19 struct mod_arch_specific { 20 + struct mod_section got; 20 21 struct mod_section plt; 21 22 struct mod_section plt_idx; 23 + }; 24 + 25 + struct got_entry { 26 + Elf_Addr symbol_addr; 22 27 }; 23 28 24 29 struct plt_entry { ··· 34 29 }; 35 30 36 31 struct plt_idx_entry { 37 - unsigned long symbol_addr; 32 + Elf_Addr symbol_addr; 38 33 }; 39 34 40 - Elf_Addr module_emit_plt_entry(struct module *mod, unsigned long val); 35 + Elf_Addr module_emit_got_entry(struct module *mod, Elf_Addr val); 36 + Elf_Addr module_emit_plt_entry(struct module *mod, Elf_Addr val); 37 + 38 + static inline struct got_entry emit_got_entry(Elf_Addr val) 39 + { 40 + return (struct got_entry) { val }; 41 + } 41 42 42 43 static inline struct plt_entry emit_plt_entry(unsigned long val) 43 44 { ··· 86 75 return NULL; 87 76 88 77 return plt + plt_idx; 78 + } 79 + 80 + static inline struct got_entry *get_got_entry(Elf_Addr val, 81 + const struct mod_section *sec) 82 + { 83 + struct got_entry *got = (struct got_entry *)sec->shdr->sh_addr; 84 + int i; 85 + 86 + for (i = 0; i < sec->num_entries; i++) 87 + if (got[i].symbol_addr == val) 88 + return &got[i]; 89 + return NULL; 89 90 } 90 91 91 92 #endif /* _ASM_MODULE_H */
+1
arch/loongarch/include/asm/module.lds.h
··· 2 2 /* Copyright (C) 2020-2022 Loongson Technology Corporation Limited */ 3 3 SECTIONS { 4 4 . = ALIGN(4); 5 + .got : { BYTE(0) } 5 6 .plt : { BYTE(0) } 6 7 .plt.idx : { BYTE(0) } 7 8 }
+9
arch/loongarch/include/asm/percpu.h
··· 8 8 #include <asm/cmpxchg.h> 9 9 #include <asm/loongarch.h> 10 10 11 + /* 12 + * The "address" (in fact, offset from $r21) of a per-CPU variable is close to 13 + * the loading address of main kernel image, but far from where the modules are 14 + * loaded. Tell the compiler this fact when using explicit relocs. 15 + */ 16 + #if defined(MODULE) && defined(CONFIG_AS_HAS_EXPLICIT_RELOCS) 17 + #define PER_CPU_ATTRIBUTES __attribute__((model("extreme"))) 18 + #endif 19 + 11 20 /* Use r21 for fast access */ 12 21 register unsigned long __my_cpu_offset __asm__("$r21"); 13 22
+3 -1
arch/loongarch/include/asm/perf_event.h
··· 6 6 7 7 #ifndef __LOONGARCH_PERF_EVENT_H__ 8 8 #define __LOONGARCH_PERF_EVENT_H__ 9 - /* Nothing to show here; the file is required by linux/perf_event.h. */ 9 + 10 + #define perf_arch_bpf_user_pt_regs(regs) (struct user_pt_regs *)regs 11 + 10 12 #endif /* __LOONGARCH_PERF_EVENT_H__ */
+3
arch/loongarch/include/asm/pgtable-bits.h
··· 83 83 _PAGE_GLOBAL | _PAGE_KERN | _CACHE_SUC) 84 84 #define PAGE_KERNEL_WUC __pgprot(_PAGE_PRESENT | __READABLE | __WRITEABLE | \ 85 85 _PAGE_GLOBAL | _PAGE_KERN | _CACHE_WUC) 86 + 86 87 #ifndef __ASSEMBLY__ 88 + 89 + #define _PAGE_IOREMAP pgprot_val(PAGE_KERNEL_SUC) 87 90 88 91 #define pgprot_noncached pgprot_noncached 89 92
+2
arch/loongarch/include/asm/setup.h
··· 13 13 14 14 extern unsigned long eentry; 15 15 extern unsigned long tlbrentry; 16 + extern void tlb_init(int cpu); 16 17 extern void cpu_cache_init(void); 18 + extern void cache_error_setup(void); 17 19 extern void per_cpu_trap_init(int cpu); 18 20 extern void set_handler(unsigned long offset, void *addr, unsigned long len); 19 21 extern void set_merr_handler(unsigned long offset, void *addr, unsigned long len);
+12
arch/loongarch/include/asm/spinlock.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 4 + */ 5 + #ifndef _ASM_SPINLOCK_H 6 + #define _ASM_SPINLOCK_H 7 + 8 + #include <asm/processor.h> 9 + #include <asm/qspinlock.h> 10 + #include <asm/qrwlock.h> 11 + 12 + #endif /* _ASM_SPINLOCK_H */
+11
arch/loongarch/include/asm/spinlock_types.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 4 + */ 5 + #ifndef _ASM_SPINLOCK_TYPES_H 6 + #define _ASM_SPINLOCK_TYPES_H 7 + 8 + #include <asm-generic/qspinlock_types.h> 9 + #include <asm-generic/qrwlock_types.h> 10 + 11 + #endif
+9
arch/loongarch/include/uapi/asm/bpf_perf_event.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 + #ifndef _UAPI__ASM_BPF_PERF_EVENT_H__ 3 + #define _UAPI__ASM_BPF_PERF_EVENT_H__ 4 + 5 + #include <linux/ptrace.h> 6 + 7 + typedef struct user_pt_regs bpf_user_pt_regs_t; 8 + 9 + #endif /* _UAPI__ASM_BPF_PERF_EVENT_H__ */
+40
arch/loongarch/include/uapi/asm/perf_regs.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ 2 + #ifndef _ASM_LOONGARCH_PERF_REGS_H 3 + #define _ASM_LOONGARCH_PERF_REGS_H 4 + 5 + enum perf_event_loongarch_regs { 6 + PERF_REG_LOONGARCH_PC, 7 + PERF_REG_LOONGARCH_R1, 8 + PERF_REG_LOONGARCH_R2, 9 + PERF_REG_LOONGARCH_R3, 10 + PERF_REG_LOONGARCH_R4, 11 + PERF_REG_LOONGARCH_R5, 12 + PERF_REG_LOONGARCH_R6, 13 + PERF_REG_LOONGARCH_R7, 14 + PERF_REG_LOONGARCH_R8, 15 + PERF_REG_LOONGARCH_R9, 16 + PERF_REG_LOONGARCH_R10, 17 + PERF_REG_LOONGARCH_R11, 18 + PERF_REG_LOONGARCH_R12, 19 + PERF_REG_LOONGARCH_R13, 20 + PERF_REG_LOONGARCH_R14, 21 + PERF_REG_LOONGARCH_R15, 22 + PERF_REG_LOONGARCH_R16, 23 + PERF_REG_LOONGARCH_R17, 24 + PERF_REG_LOONGARCH_R18, 25 + PERF_REG_LOONGARCH_R19, 26 + PERF_REG_LOONGARCH_R20, 27 + PERF_REG_LOONGARCH_R21, 28 + PERF_REG_LOONGARCH_R22, 29 + PERF_REG_LOONGARCH_R23, 30 + PERF_REG_LOONGARCH_R24, 31 + PERF_REG_LOONGARCH_R25, 32 + PERF_REG_LOONGARCH_R26, 33 + PERF_REG_LOONGARCH_R27, 34 + PERF_REG_LOONGARCH_R28, 35 + PERF_REG_LOONGARCH_R29, 36 + PERF_REG_LOONGARCH_R30, 37 + PERF_REG_LOONGARCH_R31, 38 + PERF_REG_LOONGARCH_MAX, 39 + }; 40 + #endif /* _ASM_LOONGARCH_PERF_REGS_H */
+7
arch/loongarch/kernel/Makefile
··· 23 23 24 24 obj-$(CONFIG_NUMA) += numa.o 25 25 26 + obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o 27 + 28 + obj-$(CONFIG_KEXEC) += machine_kexec.o relocate_kernel.o 29 + obj-$(CONFIG_CRASH_DUMP) += crash_dump.o 30 + 26 31 obj-$(CONFIG_UNWINDER_GUESS) += unwind_guess.o 27 32 obj-$(CONFIG_UNWINDER_PROLOGUE) += unwind_prologue.o 33 + 34 + obj-$(CONFIG_PERF_EVENTS) += perf_event.o perf_regs.o 28 35 29 36 CPPFLAGS_vmlinux.lds := $(KBUILD_CFLAGS)
+25 -69
arch/loongarch/kernel/cacheinfo.c
··· 5 5 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 6 6 */ 7 7 #include <linux/cacheinfo.h> 8 + #include <linux/topology.h> 8 9 #include <asm/bootinfo.h> 9 10 #include <asm/cpu-info.h> 10 11 11 - /* Populates leaf and increments to next leaf */ 12 - #define populate_cache(cache, leaf, c_level, c_type) \ 13 - do { \ 14 - leaf->type = c_type; \ 15 - leaf->level = c_level; \ 16 - leaf->coherency_line_size = c->cache.linesz; \ 17 - leaf->number_of_sets = c->cache.sets; \ 18 - leaf->ways_of_associativity = c->cache.ways; \ 19 - leaf->size = c->cache.linesz * c->cache.sets * \ 20 - c->cache.ways; \ 21 - if (leaf->level > 2) \ 22 - leaf->size *= nodes_per_package; \ 23 - leaf++; \ 24 - } while (0) 25 - 26 12 int init_cache_level(unsigned int cpu) 27 13 { 28 - struct cpuinfo_loongarch *c = &current_cpu_data; 14 + int cache_present = current_cpu_data.cache_leaves_present; 29 15 struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 30 - int levels = 0, leaves = 0; 31 16 32 - /* 33 - * If Dcache is not set, we assume the cache structures 34 - * are not properly initialized. 35 - */ 36 - if (c->dcache.waysize) 37 - levels += 1; 38 - else 39 - return -ENOENT; 17 + this_cpu_ci->num_levels = 18 + current_cpu_data.cache_leaves[cache_present - 1].level; 19 + this_cpu_ci->num_leaves = cache_present; 40 20 41 - 42 - leaves += (c->icache.waysize) ? 2 : 1; 43 - 44 - if (c->vcache.waysize) { 45 - levels++; 46 - leaves++; 47 - } 48 - 49 - if (c->scache.waysize) { 50 - levels++; 51 - leaves++; 52 - } 53 - 54 - if (c->tcache.waysize) { 55 - levels++; 56 - leaves++; 57 - } 58 - 59 - this_cpu_ci->num_levels = levels; 60 - this_cpu_ci->num_leaves = leaves; 61 21 return 0; 62 22 } 63 23 64 24 static inline bool cache_leaves_are_shared(struct cacheinfo *this_leaf, 65 25 struct cacheinfo *sib_leaf) 66 26 { 67 - return !((this_leaf->level == 1) || (this_leaf->level == 2)); 27 + return (!(*(unsigned char *)(this_leaf->priv) & CACHE_PRIVATE) 28 + && !(*(unsigned char *)(sib_leaf->priv) & CACHE_PRIVATE)); 68 29 } 69 30 70 31 static void cache_cpumap_setup(unsigned int cpu) 71 32 { 72 - struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 73 - struct cacheinfo *this_leaf, *sib_leaf; 74 33 unsigned int index; 34 + struct cacheinfo *this_leaf, *sib_leaf; 35 + struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 75 36 76 37 for (index = 0; index < this_cpu_ci->num_leaves; index++) { 77 38 unsigned int i; ··· 46 85 for_each_online_cpu(i) { 47 86 struct cpu_cacheinfo *sib_cpu_ci = get_cpu_cacheinfo(i); 48 87 49 - if (i == cpu || !sib_cpu_ci->info_list) 50 - continue;/* skip if itself or no cacheinfo */ 88 + if (i == cpu || !sib_cpu_ci->info_list || 89 + (cpu_to_node(i) != cpu_to_node(cpu))) 90 + continue; 91 + 51 92 sib_leaf = sib_cpu_ci->info_list + index; 52 93 if (cache_leaves_are_shared(this_leaf, sib_leaf)) { 53 94 cpumask_set_cpu(cpu, &sib_leaf->shared_cpu_map); ··· 61 98 62 99 int populate_cache_leaves(unsigned int cpu) 63 100 { 64 - int level = 1, nodes_per_package = 1; 65 - struct cpuinfo_loongarch *c = &current_cpu_data; 101 + int i, cache_present = current_cpu_data.cache_leaves_present; 66 102 struct cpu_cacheinfo *this_cpu_ci = get_cpu_cacheinfo(cpu); 67 103 struct cacheinfo *this_leaf = this_cpu_ci->info_list; 104 + struct cache_desc *cd, *cdesc = current_cpu_data.cache_leaves; 68 105 69 - if (loongson_sysconf.nr_nodes > 1) 70 - nodes_per_package = loongson_sysconf.cores_per_package 71 - / loongson_sysconf.cores_per_node; 106 + for (i = 0; i < cache_present; i++) { 107 + cd = cdesc + i; 72 108 73 - if (c->icache.waysize) { 74 - populate_cache(dcache, this_leaf, level, CACHE_TYPE_DATA); 75 - populate_cache(icache, this_leaf, level++, CACHE_TYPE_INST); 76 - } else { 77 - populate_cache(dcache, this_leaf, level++, CACHE_TYPE_UNIFIED); 109 + this_leaf->type = cd->type; 110 + this_leaf->level = cd->level; 111 + this_leaf->coherency_line_size = cd->linesz; 112 + this_leaf->number_of_sets = cd->sets; 113 + this_leaf->ways_of_associativity = cd->ways; 114 + this_leaf->size = cd->linesz * cd->sets * cd->ways; 115 + this_leaf->priv = &cd->flags; 116 + this_leaf++; 78 117 } 79 - 80 - if (c->vcache.waysize) 81 - populate_cache(vcache, this_leaf, level++, CACHE_TYPE_UNIFIED); 82 - 83 - if (c->scache.waysize) 84 - populate_cache(scache, this_leaf, level++, CACHE_TYPE_UNIFIED); 85 - 86 - if (c->tcache.waysize) 87 - populate_cache(tcache, this_leaf, level++, CACHE_TYPE_UNIFIED); 88 118 89 119 cache_cpumap_setup(cpu); 90 120 this_cpu_ci->cpu_map_populated = true;
+3 -1
arch/loongarch/kernel/cpu-probe.c
··· 187 187 uint64_t *vendor = (void *)(&cpu_full_name[VENDOR_OFFSET]); 188 188 uint64_t *cpuname = (void *)(&cpu_full_name[CPUNAME_OFFSET]); 189 189 190 - __cpu_full_name[cpu] = cpu_full_name; 190 + if (!__cpu_full_name[cpu]) 191 + __cpu_full_name[cpu] = cpu_full_name; 192 + 191 193 *vendor = iocsr_read64(LOONGARCH_IOCSR_VENDOR); 192 194 *cpuname = iocsr_read64(LOONGARCH_IOCSR_CPUNAME); 193 195
+23
arch/loongarch/kernel/crash_dump.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + #include <linux/crash_dump.h> 3 + #include <linux/io.h> 4 + #include <linux/uio.h> 5 + 6 + ssize_t copy_oldmem_page(struct iov_iter *iter, unsigned long pfn, 7 + size_t csize, unsigned long offset) 8 + { 9 + void *vaddr; 10 + 11 + if (!csize) 12 + return 0; 13 + 14 + vaddr = memremap(__pfn_to_phys(pfn), PAGE_SIZE, MEMREMAP_WB); 15 + if (!vaddr) 16 + return -ENOMEM; 17 + 18 + csize = copy_to_iter(vaddr + offset, csize, iter); 19 + 20 + memunmap(vaddr); 21 + 22 + return csize; 23 + }
+15 -7
arch/loongarch/kernel/head.S
··· 8 8 #include <asm/addrspace.h> 9 9 #include <asm/asm.h> 10 10 #include <asm/asmmacro.h> 11 + #include <asm/bug.h> 11 12 #include <asm/regdef.h> 12 13 #include <asm/loongarch.h> 13 14 #include <asm/stackframe.h> ··· 21 20 22 21 _head: 23 22 .word MZ_MAGIC /* "MZ", MS-DOS header */ 24 - .org 0x3c /* 0x04 ~ 0x3b reserved */ 23 + .org 0x8 24 + .dword kernel_entry /* Kernel entry point */ 25 + .dword _end - _text /* Kernel image effective size */ 26 + .quad 0 /* Kernel image load offset from start of RAM */ 27 + .org 0x3c /* 0x20 ~ 0x3b reserved */ 25 28 .long pe_header - _head /* Offset to the PE header */ 26 29 27 30 pe_header: ··· 62 57 li.w t0, 0x00 # FPE=0, SXE=0, ASXE=0, BTE=0 63 58 csrwr t0, LOONGARCH_CSR_EUEN 64 59 65 - la t0, __bss_start # clear .bss 60 + la.pcrel t0, __bss_start # clear .bss 66 61 st.d zero, t0, 0 67 - la t1, __bss_stop - LONGSIZE 62 + la.pcrel t1, __bss_stop - LONGSIZE 68 63 1: 69 64 addi.d t0, t0, LONGSIZE 70 65 st.d zero, t0, 0 71 66 bne t0, t1, 1b 72 67 73 - la t0, fw_arg0 68 + la.pcrel t0, fw_arg0 74 69 st.d a0, t0, 0 # firmware arguments 75 - la t0, fw_arg1 70 + la.pcrel t0, fw_arg1 76 71 st.d a1, t0, 0 77 - la t0, fw_arg2 72 + la.pcrel t0, fw_arg2 78 73 st.d a2, t0, 0 79 74 80 75 /* KSave3 used for percpu base, initialized as 0 */ ··· 82 77 /* GPR21 used for percpu base (runtime), initialized as 0 */ 83 78 move u0, zero 84 79 85 - la tp, init_thread_union 80 + la.pcrel tp, init_thread_union 86 81 /* Set the SP after an empty pt_regs. */ 87 82 PTR_LI sp, (_THREAD_SIZE - 32 - PT_SIZE) 88 83 PTR_ADD sp, sp, tp ··· 90 85 PTR_ADDI sp, sp, -4 * SZREG # init stack pointer 91 86 92 87 bl start_kernel 88 + ASM_BUG() 93 89 94 90 SYM_CODE_END(kernel_entry) 95 91 ··· 122 116 ld.d tp, t0, CPU_BOOT_TINFO 123 117 124 118 bl start_secondary 119 + ASM_BUG() 120 + 125 121 SYM_CODE_END(smpboot_entry) 126 122 127 123 #endif /* CONFIG_SMP */
+304
arch/loongarch/kernel/machine_kexec.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * machine_kexec.c for kexec 4 + * 5 + * Copyright (C) 2022 Loongson Technology Corporation Limited 6 + */ 7 + #include <linux/compiler.h> 8 + #include <linux/cpu.h> 9 + #include <linux/kexec.h> 10 + #include <linux/crash_dump.h> 11 + #include <linux/delay.h> 12 + #include <linux/irq.h> 13 + #include <linux/libfdt.h> 14 + #include <linux/mm.h> 15 + #include <linux/of_fdt.h> 16 + #include <linux/reboot.h> 17 + #include <linux/sched.h> 18 + #include <linux/sched/task_stack.h> 19 + 20 + #include <asm/bootinfo.h> 21 + #include <asm/cacheflush.h> 22 + #include <asm/page.h> 23 + 24 + /* 0x100000 ~ 0x200000 is safe */ 25 + #define KEXEC_CONTROL_CODE TO_CACHE(0x100000UL) 26 + #define KEXEC_CMDLINE_ADDR TO_CACHE(0x108000UL) 27 + 28 + static unsigned long reboot_code_buffer; 29 + static cpumask_t cpus_in_crash = CPU_MASK_NONE; 30 + 31 + #ifdef CONFIG_SMP 32 + static void (*relocated_kexec_smp_wait)(void *); 33 + atomic_t kexec_ready_to_reboot = ATOMIC_INIT(0); 34 + #endif 35 + 36 + static unsigned long efi_boot; 37 + static unsigned long cmdline_ptr; 38 + static unsigned long systable_ptr; 39 + static unsigned long start_addr; 40 + static unsigned long first_ind_entry; 41 + 42 + static void kexec_image_info(const struct kimage *kimage) 43 + { 44 + unsigned long i; 45 + 46 + pr_debug("kexec kimage info:\n"); 47 + pr_debug("\ttype: %d\n", kimage->type); 48 + pr_debug("\tstart: %lx\n", kimage->start); 49 + pr_debug("\thead: %lx\n", kimage->head); 50 + pr_debug("\tnr_segments: %lu\n", kimage->nr_segments); 51 + 52 + for (i = 0; i < kimage->nr_segments; i++) { 53 + pr_debug("\t segment[%lu]: %016lx - %016lx", i, 54 + kimage->segment[i].mem, 55 + kimage->segment[i].mem + kimage->segment[i].memsz); 56 + pr_debug("\t\t0x%lx bytes, %lu pages\n", 57 + (unsigned long)kimage->segment[i].memsz, 58 + (unsigned long)kimage->segment[i].memsz / PAGE_SIZE); 59 + } 60 + } 61 + 62 + int machine_kexec_prepare(struct kimage *kimage) 63 + { 64 + int i; 65 + char *bootloader = "kexec"; 66 + void *cmdline_ptr = (void *)KEXEC_CMDLINE_ADDR; 67 + 68 + kexec_image_info(kimage); 69 + 70 + kimage->arch.efi_boot = fw_arg0; 71 + kimage->arch.systable_ptr = fw_arg2; 72 + 73 + /* Find the command line */ 74 + for (i = 0; i < kimage->nr_segments; i++) { 75 + if (!strncmp(bootloader, (char __user *)kimage->segment[i].buf, strlen(bootloader))) { 76 + if (!copy_from_user(cmdline_ptr, kimage->segment[i].buf, COMMAND_LINE_SIZE)) 77 + kimage->arch.cmdline_ptr = (unsigned long)cmdline_ptr; 78 + break; 79 + } 80 + } 81 + 82 + if (!kimage->arch.cmdline_ptr) { 83 + pr_err("Command line not included in the provided image\n"); 84 + return -EINVAL; 85 + } 86 + 87 + /* kexec/kdump need a safe page to save reboot_code_buffer */ 88 + kimage->control_code_page = virt_to_page((void *)KEXEC_CONTROL_CODE); 89 + 90 + reboot_code_buffer = (unsigned long)page_address(kimage->control_code_page); 91 + memcpy((void *)reboot_code_buffer, relocate_new_kernel, relocate_new_kernel_size); 92 + 93 + #ifdef CONFIG_SMP 94 + /* All secondary cpus now may jump to kexec_smp_wait cycle */ 95 + relocated_kexec_smp_wait = reboot_code_buffer + (void *)(kexec_smp_wait - relocate_new_kernel); 96 + #endif 97 + 98 + return 0; 99 + } 100 + 101 + void machine_kexec_cleanup(struct kimage *kimage) 102 + { 103 + } 104 + 105 + void kexec_reboot(void) 106 + { 107 + do_kexec_t do_kexec = NULL; 108 + 109 + /* 110 + * We know we were online, and there will be no incoming IPIs at 111 + * this point. Mark online again before rebooting so that the crash 112 + * analysis tool will see us correctly. 113 + */ 114 + set_cpu_online(smp_processor_id(), true); 115 + 116 + /* Ensure remote CPUs observe that we're online before rebooting. */ 117 + smp_mb__after_atomic(); 118 + 119 + /* 120 + * Make sure we get correct instructions written by the 121 + * machine_kexec_prepare() CPU. 122 + */ 123 + __asm__ __volatile__ ("\tibar 0\n"::); 124 + 125 + #ifdef CONFIG_SMP 126 + /* All secondary cpus go to kexec_smp_wait */ 127 + if (smp_processor_id() > 0) { 128 + relocated_kexec_smp_wait(NULL); 129 + unreachable(); 130 + } 131 + #endif 132 + 133 + do_kexec = (void *)reboot_code_buffer; 134 + do_kexec(efi_boot, cmdline_ptr, systable_ptr, start_addr, first_ind_entry); 135 + 136 + unreachable(); 137 + } 138 + 139 + 140 + #ifdef CONFIG_SMP 141 + static void kexec_shutdown_secondary(void *regs) 142 + { 143 + int cpu = smp_processor_id(); 144 + 145 + if (!cpu_online(cpu)) 146 + return; 147 + 148 + /* We won't be sent IPIs any more. */ 149 + set_cpu_online(cpu, false); 150 + 151 + local_irq_disable(); 152 + while (!atomic_read(&kexec_ready_to_reboot)) 153 + cpu_relax(); 154 + 155 + kexec_reboot(); 156 + } 157 + 158 + static void crash_shutdown_secondary(void *passed_regs) 159 + { 160 + int cpu = smp_processor_id(); 161 + struct pt_regs *regs = passed_regs; 162 + 163 + /* 164 + * If we are passed registers, use those. Otherwise get the 165 + * regs from the last interrupt, which should be correct, as 166 + * we are in an interrupt. But if the regs are not there, 167 + * pull them from the top of the stack. They are probably 168 + * wrong, but we need something to keep from crashing again. 169 + */ 170 + if (!regs) 171 + regs = get_irq_regs(); 172 + if (!regs) 173 + regs = task_pt_regs(current); 174 + 175 + if (!cpu_online(cpu)) 176 + return; 177 + 178 + /* We won't be sent IPIs any more. */ 179 + set_cpu_online(cpu, false); 180 + 181 + local_irq_disable(); 182 + if (!cpumask_test_cpu(cpu, &cpus_in_crash)) 183 + crash_save_cpu(regs, cpu); 184 + cpumask_set_cpu(cpu, &cpus_in_crash); 185 + 186 + while (!atomic_read(&kexec_ready_to_reboot)) 187 + cpu_relax(); 188 + 189 + kexec_reboot(); 190 + } 191 + 192 + void crash_smp_send_stop(void) 193 + { 194 + unsigned int ncpus; 195 + unsigned long timeout; 196 + static int cpus_stopped; 197 + 198 + /* 199 + * This function can be called twice in panic path, but obviously 200 + * we should execute this only once. 201 + */ 202 + if (cpus_stopped) 203 + return; 204 + 205 + cpus_stopped = 1; 206 + 207 + /* Excluding the panic cpu */ 208 + ncpus = num_online_cpus() - 1; 209 + 210 + smp_call_function(crash_shutdown_secondary, NULL, 0); 211 + smp_wmb(); 212 + 213 + /* 214 + * The crash CPU sends an IPI and wait for other CPUs to 215 + * respond. Delay of at least 10 seconds. 216 + */ 217 + timeout = MSEC_PER_SEC * 10; 218 + pr_emerg("Sending IPI to other cpus...\n"); 219 + while ((cpumask_weight(&cpus_in_crash) < ncpus) && timeout--) { 220 + mdelay(1); 221 + cpu_relax(); 222 + } 223 + } 224 + #endif /* defined(CONFIG_SMP) */ 225 + 226 + void machine_shutdown(void) 227 + { 228 + int cpu; 229 + 230 + /* All CPUs go to reboot_code_buffer */ 231 + for_each_possible_cpu(cpu) 232 + if (!cpu_online(cpu)) 233 + cpu_device_up(get_cpu_device(cpu)); 234 + 235 + #ifdef CONFIG_SMP 236 + smp_call_function(kexec_shutdown_secondary, NULL, 0); 237 + #endif 238 + } 239 + 240 + void machine_crash_shutdown(struct pt_regs *regs) 241 + { 242 + int crashing_cpu; 243 + 244 + local_irq_disable(); 245 + 246 + crashing_cpu = smp_processor_id(); 247 + crash_save_cpu(regs, crashing_cpu); 248 + 249 + #ifdef CONFIG_SMP 250 + crash_smp_send_stop(); 251 + #endif 252 + cpumask_set_cpu(crashing_cpu, &cpus_in_crash); 253 + 254 + pr_info("Starting crashdump kernel...\n"); 255 + } 256 + 257 + void machine_kexec(struct kimage *image) 258 + { 259 + unsigned long entry, *ptr; 260 + struct kimage_arch *internal = &image->arch; 261 + 262 + efi_boot = internal->efi_boot; 263 + cmdline_ptr = internal->cmdline_ptr; 264 + systable_ptr = internal->systable_ptr; 265 + 266 + start_addr = (unsigned long)phys_to_virt(image->start); 267 + 268 + first_ind_entry = (image->type == KEXEC_TYPE_DEFAULT) ? 269 + (unsigned long)phys_to_virt(image->head & PAGE_MASK) : 0; 270 + 271 + /* 272 + * The generic kexec code builds a page list with physical 273 + * addresses. they are directly accessible through XKPRANGE 274 + * hence the phys_to_virt() call. 275 + */ 276 + for (ptr = &image->head; (entry = *ptr) && !(entry & IND_DONE); 277 + ptr = (entry & IND_INDIRECTION) ? 278 + phys_to_virt(entry & PAGE_MASK) : ptr + 1) { 279 + if (*ptr & IND_SOURCE || *ptr & IND_INDIRECTION || 280 + *ptr & IND_DESTINATION) 281 + *ptr = (unsigned long) phys_to_virt(*ptr); 282 + } 283 + 284 + /* Mark offline before disabling local irq. */ 285 + set_cpu_online(smp_processor_id(), false); 286 + 287 + /* We do not want to be bothered. */ 288 + local_irq_disable(); 289 + 290 + pr_notice("EFI boot flag 0x%lx\n", efi_boot); 291 + pr_notice("Command line at 0x%lx\n", cmdline_ptr); 292 + pr_notice("System table at 0x%lx\n", systable_ptr); 293 + pr_notice("We will call new kernel at 0x%lx\n", start_addr); 294 + pr_notice("Bye ...\n"); 295 + 296 + /* Make reboot code buffer available to the boot CPU. */ 297 + flush_cache_all(); 298 + 299 + #ifdef CONFIG_SMP 300 + atomic_set(&kexec_ready_to_reboot, 1); 301 + #endif 302 + 303 + kexec_reboot(); 304 + }
-3
arch/loongarch/kernel/mem.c
··· 58 58 /* Reserve the kernel text/data/bss */ 59 59 memblock_reserve(__pa_symbol(&_text), 60 60 __pa_symbol(&_end) - __pa_symbol(&_text)); 61 - 62 - /* Reserve the initrd */ 63 - reserve_initrd_mem(); 64 61 }
+55 -6
arch/loongarch/kernel/module-sections.c
··· 7 7 #include <linux/kernel.h> 8 8 #include <linux/module.h> 9 9 10 - Elf_Addr module_emit_plt_entry(struct module *mod, unsigned long val) 10 + Elf_Addr module_emit_got_entry(struct module *mod, Elf_Addr val) 11 + { 12 + struct mod_section *got_sec = &mod->arch.got; 13 + int i = got_sec->num_entries; 14 + struct got_entry *got = get_got_entry(val, got_sec); 15 + 16 + if (got) 17 + return (Elf_Addr)got; 18 + 19 + /* There is no GOT entry for val yet, create a new one. */ 20 + got = (struct got_entry *)got_sec->shdr->sh_addr; 21 + got[i] = emit_got_entry(val); 22 + 23 + got_sec->num_entries++; 24 + if (got_sec->num_entries > got_sec->max_entries) { 25 + /* 26 + * This may happen when the module contains a GOT_HI20 without 27 + * a paired GOT_LO12. Such a module is broken, reject it. 28 + */ 29 + pr_err("%s: module contains bad GOT relocation\n", mod->name); 30 + return 0; 31 + } 32 + 33 + return (Elf_Addr)&got[i]; 34 + } 35 + 36 + Elf_Addr module_emit_plt_entry(struct module *mod, Elf_Addr val) 11 37 { 12 38 int nr; 13 39 struct mod_section *plt_sec = &mod->arch.plt; ··· 76 50 return false; 77 51 } 78 52 79 - static void count_max_entries(Elf_Rela *relas, int num, unsigned int *plts) 53 + static void count_max_entries(Elf_Rela *relas, int num, 54 + unsigned int *plts, unsigned int *gots) 80 55 { 81 56 unsigned int i, type; 82 57 83 58 for (i = 0; i < num; i++) { 84 59 type = ELF_R_TYPE(relas[i].r_info); 85 - if (type == R_LARCH_SOP_PUSH_PLT_PCREL) { 60 + switch (type) { 61 + case R_LARCH_SOP_PUSH_PLT_PCREL: 62 + case R_LARCH_B26: 86 63 if (!duplicate_rela(relas, i)) 87 64 (*plts)++; 65 + break; 66 + case R_LARCH_GOT_PC_HI20: 67 + if (!duplicate_rela(relas, i)) 68 + (*gots)++; 69 + break; 70 + default: 71 + break; /* Do nothing. */ 88 72 } 89 73 } 90 74 } ··· 102 66 int module_frob_arch_sections(Elf_Ehdr *ehdr, Elf_Shdr *sechdrs, 103 67 char *secstrings, struct module *mod) 104 68 { 105 - unsigned int i, num_plts = 0; 69 + unsigned int i, num_plts = 0, num_gots = 0; 106 70 107 71 /* 108 72 * Find the empty .plt sections. 109 73 */ 110 74 for (i = 0; i < ehdr->e_shnum; i++) { 111 - if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt")) 75 + if (!strcmp(secstrings + sechdrs[i].sh_name, ".got")) 76 + mod->arch.got.shdr = sechdrs + i; 77 + else if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt")) 112 78 mod->arch.plt.shdr = sechdrs + i; 113 79 else if (!strcmp(secstrings + sechdrs[i].sh_name, ".plt.idx")) 114 80 mod->arch.plt_idx.shdr = sechdrs + i; 115 81 } 116 82 83 + if (!mod->arch.got.shdr) { 84 + pr_err("%s: module GOT section(s) missing\n", mod->name); 85 + return -ENOEXEC; 86 + } 117 87 if (!mod->arch.plt.shdr) { 118 88 pr_err("%s: module PLT section(s) missing\n", mod->name); 119 89 return -ENOEXEC; ··· 142 100 if (!(dst_sec->sh_flags & SHF_EXECINSTR)) 143 101 continue; 144 102 145 - count_max_entries(relas, num_rela, &num_plts); 103 + count_max_entries(relas, num_rela, &num_plts, &num_gots); 146 104 } 105 + 106 + mod->arch.got.shdr->sh_type = SHT_NOBITS; 107 + mod->arch.got.shdr->sh_flags = SHF_ALLOC; 108 + mod->arch.got.shdr->sh_addralign = L1_CACHE_BYTES; 109 + mod->arch.got.shdr->sh_size = (num_gots + 1) * sizeof(struct got_entry); 110 + mod->arch.got.num_entries = 0; 111 + mod->arch.got.max_entries = num_gots; 147 112 148 113 mod->arch.plt.shdr->sh_type = SHT_NOBITS; 149 114 mod->arch.plt.shdr->sh_flags = SHF_EXECINSTR | SHF_ALLOC;
+94 -11
arch/loongarch/kernel/module.c
··· 18 18 #include <linux/string.h> 19 19 #include <linux/kernel.h> 20 20 21 - static inline bool signed_imm_check(long val, unsigned int bit) 22 - { 23 - return -(1L << (bit - 1)) <= val && val < (1L << (bit - 1)); 24 - } 25 - 26 - static inline bool unsigned_imm_check(unsigned long val, unsigned int bit) 27 - { 28 - return val < (1UL << bit); 29 - } 30 - 31 21 static int rela_stack_push(s64 stack_value, s64 *rela_stack, size_t *rela_stack_top) 32 22 { 33 23 if (*rela_stack_top >= RELA_STACK_DEPTH) ··· 271 281 } 272 282 } 273 283 284 + static int apply_r_larch_b26(struct module *mod, u32 *location, Elf_Addr v, 285 + s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 286 + { 287 + ptrdiff_t offset = (void *)v - (void *)location; 288 + union loongarch_instruction *insn = (union loongarch_instruction *)location; 289 + 290 + if (offset >= SZ_128M) 291 + v = module_emit_plt_entry(mod, v); 292 + 293 + if (offset < -SZ_128M) 294 + v = module_emit_plt_entry(mod, v); 295 + 296 + offset = (void *)v - (void *)location; 297 + 298 + if (offset & 3) { 299 + pr_err("module %s: jump offset = 0x%llx unaligned! dangerous R_LARCH_B26 (%u) relocation\n", 300 + mod->name, (long long)offset, type); 301 + return -ENOEXEC; 302 + } 303 + 304 + if (!signed_imm_check(offset, 28)) { 305 + pr_err("module %s: jump offset = 0x%llx overflow! dangerous R_LARCH_B26 (%u) relocation\n", 306 + mod->name, (long long)offset, type); 307 + return -ENOEXEC; 308 + } 309 + 310 + offset >>= 2; 311 + insn->reg0i26_format.immediate_l = offset & 0xffff; 312 + insn->reg0i26_format.immediate_h = (offset >> 16) & 0x3ff; 313 + 314 + return 0; 315 + } 316 + 317 + static int apply_r_larch_pcala(struct module *mod, u32 *location, Elf_Addr v, 318 + s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 319 + { 320 + union loongarch_instruction *insn = (union loongarch_instruction *)location; 321 + /* Use s32 for a sign-extension deliberately. */ 322 + s32 offset_hi20 = (void *)((v + 0x800) & ~0xfff) - 323 + (void *)((Elf_Addr)location & ~0xfff); 324 + Elf_Addr anchor = (((Elf_Addr)location) & ~0xfff) + offset_hi20; 325 + ptrdiff_t offset_rem = (void *)v - (void *)anchor; 326 + 327 + switch (type) { 328 + case R_LARCH_PCALA_LO12: 329 + insn->reg2i12_format.immediate = v & 0xfff; 330 + break; 331 + case R_LARCH_PCALA_HI20: 332 + v = offset_hi20 >> 12; 333 + insn->reg1i20_format.immediate = v & 0xfffff; 334 + break; 335 + case R_LARCH_PCALA64_LO20: 336 + v = offset_rem >> 32; 337 + insn->reg1i20_format.immediate = v & 0xfffff; 338 + break; 339 + case R_LARCH_PCALA64_HI12: 340 + v = offset_rem >> 52; 341 + insn->reg2i12_format.immediate = v & 0xfff; 342 + break; 343 + default: 344 + pr_err("%s: Unsupport relocation type %u\n", mod->name, type); 345 + return -EINVAL; 346 + } 347 + 348 + return 0; 349 + } 350 + 351 + static int apply_r_larch_got_pc(struct module *mod, u32 *location, Elf_Addr v, 352 + s64 *rela_stack, size_t *rela_stack_top, unsigned int type) 353 + { 354 + Elf_Addr got = module_emit_got_entry(mod, v); 355 + 356 + if (!got) 357 + return -EINVAL; 358 + 359 + switch (type) { 360 + case R_LARCH_GOT_PC_LO12: 361 + type = R_LARCH_PCALA_LO12; 362 + break; 363 + case R_LARCH_GOT_PC_HI20: 364 + type = R_LARCH_PCALA_HI20; 365 + break; 366 + default: 367 + pr_err("%s: Unsupport relocation type %u\n", mod->name, type); 368 + return -EINVAL; 369 + } 370 + 371 + return apply_r_larch_pcala(mod, location, got, rela_stack, rela_stack_top, type); 372 + } 373 + 274 374 /* 275 375 * reloc_handlers_rela() - Apply a particular relocation to a module 276 376 * @mod: the module to apply the reloc to ··· 376 296 377 297 /* The handlers for known reloc types */ 378 298 static reloc_rela_handler reloc_rela_handlers[] = { 379 - [R_LARCH_NONE ... R_LARCH_SUB64] = apply_r_larch_error, 299 + [R_LARCH_NONE ... R_LARCH_RELAX] = apply_r_larch_error, 380 300 381 301 [R_LARCH_NONE] = apply_r_larch_none, 382 302 [R_LARCH_32] = apply_r_larch_32, ··· 390 310 [R_LARCH_SOP_SUB ... R_LARCH_SOP_IF_ELSE] = apply_r_larch_sop, 391 311 [R_LARCH_SOP_POP_32_S_10_5 ... R_LARCH_SOP_POP_32_U] = apply_r_larch_sop_imm_field, 392 312 [R_LARCH_ADD32 ... R_LARCH_SUB64] = apply_r_larch_add_sub, 313 + [R_LARCH_B26] = apply_r_larch_b26, 314 + [R_LARCH_PCALA_HI20...R_LARCH_PCALA64_HI12] = apply_r_larch_pcala, 315 + [R_LARCH_GOT_PC_HI20...R_LARCH_GOT_PC_LO12] = apply_r_larch_got_pc, 393 316 }; 394 317 395 318 int apply_relocate_add(Elf_Shdr *sechdrs, const char *strtab,
+887
arch/loongarch/kernel/perf_event.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Linux performance counter support for LoongArch. 4 + * 5 + * Copyright (C) 2022 Loongson Technology Corporation Limited 6 + * 7 + * Derived from MIPS: 8 + * Copyright (C) 2010 MIPS Technologies, Inc. 9 + * Copyright (C) 2011 Cavium Networks, Inc. 10 + * Author: Deng-Cheng Zhu 11 + */ 12 + 13 + #include <linux/cpumask.h> 14 + #include <linux/interrupt.h> 15 + #include <linux/smp.h> 16 + #include <linux/kernel.h> 17 + #include <linux/perf_event.h> 18 + #include <linux/uaccess.h> 19 + #include <linux/sched/task_stack.h> 20 + 21 + #include <asm/irq.h> 22 + #include <asm/irq_regs.h> 23 + #include <asm/stacktrace.h> 24 + #include <asm/unwind.h> 25 + 26 + /* 27 + * Get the return address for a single stackframe and return a pointer to the 28 + * next frame tail. 29 + */ 30 + static unsigned long 31 + user_backtrace(struct perf_callchain_entry_ctx *entry, unsigned long fp) 32 + { 33 + unsigned long err; 34 + unsigned long __user *user_frame_tail; 35 + struct stack_frame buftail; 36 + 37 + user_frame_tail = (unsigned long __user *)(fp - sizeof(struct stack_frame)); 38 + 39 + /* Also check accessibility of one struct frame_tail beyond */ 40 + if (!access_ok(user_frame_tail, sizeof(buftail))) 41 + return 0; 42 + 43 + pagefault_disable(); 44 + err = __copy_from_user_inatomic(&buftail, user_frame_tail, sizeof(buftail)); 45 + pagefault_enable(); 46 + 47 + if (err || (unsigned long)user_frame_tail >= buftail.fp) 48 + return 0; 49 + 50 + perf_callchain_store(entry, buftail.ra); 51 + 52 + return buftail.fp; 53 + } 54 + 55 + void perf_callchain_user(struct perf_callchain_entry_ctx *entry, 56 + struct pt_regs *regs) 57 + { 58 + unsigned long fp; 59 + 60 + if (perf_guest_state()) { 61 + /* We don't support guest os callchain now */ 62 + return; 63 + } 64 + 65 + perf_callchain_store(entry, regs->csr_era); 66 + 67 + fp = regs->regs[22]; 68 + 69 + while (entry->nr < entry->max_stack && fp && !((unsigned long)fp & 0xf)) 70 + fp = user_backtrace(entry, fp); 71 + } 72 + 73 + void perf_callchain_kernel(struct perf_callchain_entry_ctx *entry, 74 + struct pt_regs *regs) 75 + { 76 + struct unwind_state state; 77 + unsigned long addr; 78 + 79 + for (unwind_start(&state, current, regs); 80 + !unwind_done(&state); unwind_next_frame(&state)) { 81 + addr = unwind_get_return_address(&state); 82 + if (!addr || perf_callchain_store(entry, addr)) 83 + return; 84 + } 85 + } 86 + 87 + #define LOONGARCH_MAX_HWEVENTS 32 88 + 89 + struct cpu_hw_events { 90 + /* Array of events on this cpu. */ 91 + struct perf_event *events[LOONGARCH_MAX_HWEVENTS]; 92 + 93 + /* 94 + * Set the bit (indexed by the counter number) when the counter 95 + * is used for an event. 96 + */ 97 + unsigned long used_mask[BITS_TO_LONGS(LOONGARCH_MAX_HWEVENTS)]; 98 + 99 + /* 100 + * Software copy of the control register for each performance counter. 101 + */ 102 + unsigned int saved_ctrl[LOONGARCH_MAX_HWEVENTS]; 103 + }; 104 + static DEFINE_PER_CPU(struct cpu_hw_events, cpu_hw_events) = { 105 + .saved_ctrl = {0}, 106 + }; 107 + 108 + /* The description of LoongArch performance events. */ 109 + struct loongarch_perf_event { 110 + unsigned int event_id; 111 + }; 112 + 113 + static struct loongarch_perf_event raw_event; 114 + static DEFINE_MUTEX(raw_event_mutex); 115 + 116 + #define C(x) PERF_COUNT_HW_CACHE_##x 117 + #define HW_OP_UNSUPPORTED 0xffffffff 118 + #define CACHE_OP_UNSUPPORTED 0xffffffff 119 + 120 + #define PERF_MAP_ALL_UNSUPPORTED \ 121 + [0 ... PERF_COUNT_HW_MAX - 1] = {HW_OP_UNSUPPORTED} 122 + 123 + #define PERF_CACHE_MAP_ALL_UNSUPPORTED \ 124 + [0 ... C(MAX) - 1] = { \ 125 + [0 ... C(OP_MAX) - 1] = { \ 126 + [0 ... C(RESULT_MAX) - 1] = {CACHE_OP_UNSUPPORTED}, \ 127 + }, \ 128 + } 129 + 130 + struct loongarch_pmu { 131 + u64 max_period; 132 + u64 valid_count; 133 + u64 overflow; 134 + const char *name; 135 + unsigned int num_counters; 136 + u64 (*read_counter)(unsigned int idx); 137 + void (*write_counter)(unsigned int idx, u64 val); 138 + const struct loongarch_perf_event *(*map_raw_event)(u64 config); 139 + const struct loongarch_perf_event (*general_event_map)[PERF_COUNT_HW_MAX]; 140 + const struct loongarch_perf_event (*cache_event_map) 141 + [PERF_COUNT_HW_CACHE_MAX] 142 + [PERF_COUNT_HW_CACHE_OP_MAX] 143 + [PERF_COUNT_HW_CACHE_RESULT_MAX]; 144 + }; 145 + 146 + static struct loongarch_pmu loongarch_pmu; 147 + 148 + #define M_PERFCTL_EVENT(event) (event & CSR_PERFCTRL_EVENT) 149 + 150 + #define M_PERFCTL_COUNT_EVENT_WHENEVER (CSR_PERFCTRL_PLV0 | \ 151 + CSR_PERFCTRL_PLV1 | \ 152 + CSR_PERFCTRL_PLV2 | \ 153 + CSR_PERFCTRL_PLV3 | \ 154 + CSR_PERFCTRL_IE) 155 + 156 + #define M_PERFCTL_CONFIG_MASK 0x1f0000 157 + 158 + static void pause_local_counters(void); 159 + static void resume_local_counters(void); 160 + 161 + static u64 loongarch_pmu_read_counter(unsigned int idx) 162 + { 163 + u64 val = -1; 164 + 165 + switch (idx) { 166 + case 0: 167 + val = read_csr_perfcntr0(); 168 + break; 169 + case 1: 170 + val = read_csr_perfcntr1(); 171 + break; 172 + case 2: 173 + val = read_csr_perfcntr2(); 174 + break; 175 + case 3: 176 + val = read_csr_perfcntr3(); 177 + break; 178 + default: 179 + WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx); 180 + return 0; 181 + } 182 + 183 + return val; 184 + } 185 + 186 + static void loongarch_pmu_write_counter(unsigned int idx, u64 val) 187 + { 188 + switch (idx) { 189 + case 0: 190 + write_csr_perfcntr0(val); 191 + return; 192 + case 1: 193 + write_csr_perfcntr1(val); 194 + return; 195 + case 2: 196 + write_csr_perfcntr2(val); 197 + return; 198 + case 3: 199 + write_csr_perfcntr3(val); 200 + return; 201 + default: 202 + WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx); 203 + return; 204 + } 205 + } 206 + 207 + static unsigned int loongarch_pmu_read_control(unsigned int idx) 208 + { 209 + unsigned int val = -1; 210 + 211 + switch (idx) { 212 + case 0: 213 + val = read_csr_perfctrl0(); 214 + break; 215 + case 1: 216 + val = read_csr_perfctrl1(); 217 + break; 218 + case 2: 219 + val = read_csr_perfctrl2(); 220 + break; 221 + case 3: 222 + val = read_csr_perfctrl3(); 223 + break; 224 + default: 225 + WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx); 226 + return 0; 227 + } 228 + 229 + return val; 230 + } 231 + 232 + static void loongarch_pmu_write_control(unsigned int idx, unsigned int val) 233 + { 234 + switch (idx) { 235 + case 0: 236 + write_csr_perfctrl0(val); 237 + return; 238 + case 1: 239 + write_csr_perfctrl1(val); 240 + return; 241 + case 2: 242 + write_csr_perfctrl2(val); 243 + return; 244 + case 3: 245 + write_csr_perfctrl3(val); 246 + return; 247 + default: 248 + WARN_ONCE(1, "Invalid performance counter number (%d)\n", idx); 249 + return; 250 + } 251 + } 252 + 253 + static int loongarch_pmu_alloc_counter(struct cpu_hw_events *cpuc, struct hw_perf_event *hwc) 254 + { 255 + int i; 256 + 257 + for (i = 0; i < loongarch_pmu.num_counters; i++) { 258 + if (!test_and_set_bit(i, cpuc->used_mask)) 259 + return i; 260 + } 261 + 262 + return -EAGAIN; 263 + } 264 + 265 + static void loongarch_pmu_enable_event(struct hw_perf_event *evt, int idx) 266 + { 267 + unsigned int cpu; 268 + struct perf_event *event = container_of(evt, struct perf_event, hw); 269 + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 270 + 271 + WARN_ON(idx < 0 || idx >= loongarch_pmu.num_counters); 272 + 273 + /* Make sure interrupt enabled. */ 274 + cpuc->saved_ctrl[idx] = M_PERFCTL_EVENT(evt->event_base & 0xff) | 275 + (evt->config_base & M_PERFCTL_CONFIG_MASK) | CSR_PERFCTRL_IE; 276 + 277 + cpu = (event->cpu >= 0) ? event->cpu : smp_processor_id(); 278 + 279 + /* 280 + * We do not actually let the counter run. Leave it until start(). 281 + */ 282 + pr_debug("Enabling perf counter for CPU%d\n", cpu); 283 + } 284 + 285 + static void loongarch_pmu_disable_event(int idx) 286 + { 287 + unsigned long flags; 288 + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 289 + 290 + WARN_ON(idx < 0 || idx >= loongarch_pmu.num_counters); 291 + 292 + local_irq_save(flags); 293 + cpuc->saved_ctrl[idx] = loongarch_pmu_read_control(idx) & 294 + ~M_PERFCTL_COUNT_EVENT_WHENEVER; 295 + loongarch_pmu_write_control(idx, cpuc->saved_ctrl[idx]); 296 + local_irq_restore(flags); 297 + } 298 + 299 + static int loongarch_pmu_event_set_period(struct perf_event *event, 300 + struct hw_perf_event *hwc, 301 + int idx) 302 + { 303 + int ret = 0; 304 + u64 left = local64_read(&hwc->period_left); 305 + u64 period = hwc->sample_period; 306 + 307 + if (unlikely((left + period) & (1ULL << 63))) { 308 + /* left underflowed by more than period. */ 309 + left = period; 310 + local64_set(&hwc->period_left, left); 311 + hwc->last_period = period; 312 + ret = 1; 313 + } else if (unlikely((left + period) <= period)) { 314 + /* left underflowed by less than period. */ 315 + left += period; 316 + local64_set(&hwc->period_left, left); 317 + hwc->last_period = period; 318 + ret = 1; 319 + } 320 + 321 + if (left > loongarch_pmu.max_period) { 322 + left = loongarch_pmu.max_period; 323 + local64_set(&hwc->period_left, left); 324 + } 325 + 326 + local64_set(&hwc->prev_count, loongarch_pmu.overflow - left); 327 + 328 + loongarch_pmu.write_counter(idx, loongarch_pmu.overflow - left); 329 + 330 + perf_event_update_userpage(event); 331 + 332 + return ret; 333 + } 334 + 335 + static void loongarch_pmu_event_update(struct perf_event *event, 336 + struct hw_perf_event *hwc, 337 + int idx) 338 + { 339 + u64 delta; 340 + u64 prev_raw_count, new_raw_count; 341 + 342 + again: 343 + prev_raw_count = local64_read(&hwc->prev_count); 344 + new_raw_count = loongarch_pmu.read_counter(idx); 345 + 346 + if (local64_cmpxchg(&hwc->prev_count, prev_raw_count, 347 + new_raw_count) != prev_raw_count) 348 + goto again; 349 + 350 + delta = new_raw_count - prev_raw_count; 351 + 352 + local64_add(delta, &event->count); 353 + local64_sub(delta, &hwc->period_left); 354 + } 355 + 356 + static void loongarch_pmu_start(struct perf_event *event, int flags) 357 + { 358 + struct hw_perf_event *hwc = &event->hw; 359 + 360 + if (flags & PERF_EF_RELOAD) 361 + WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE)); 362 + 363 + hwc->state = 0; 364 + 365 + /* Set the period for the event. */ 366 + loongarch_pmu_event_set_period(event, hwc, hwc->idx); 367 + 368 + /* Enable the event. */ 369 + loongarch_pmu_enable_event(hwc, hwc->idx); 370 + } 371 + 372 + static void loongarch_pmu_stop(struct perf_event *event, int flags) 373 + { 374 + struct hw_perf_event *hwc = &event->hw; 375 + 376 + if (!(hwc->state & PERF_HES_STOPPED)) { 377 + /* We are working on a local event. */ 378 + loongarch_pmu_disable_event(hwc->idx); 379 + barrier(); 380 + loongarch_pmu_event_update(event, hwc, hwc->idx); 381 + hwc->state |= PERF_HES_STOPPED | PERF_HES_UPTODATE; 382 + } 383 + } 384 + 385 + static int loongarch_pmu_add(struct perf_event *event, int flags) 386 + { 387 + int idx, err = 0; 388 + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 389 + struct hw_perf_event *hwc = &event->hw; 390 + 391 + perf_pmu_disable(event->pmu); 392 + 393 + /* To look for a free counter for this event. */ 394 + idx = loongarch_pmu_alloc_counter(cpuc, hwc); 395 + if (idx < 0) { 396 + err = idx; 397 + goto out; 398 + } 399 + 400 + /* 401 + * If there is an event in the counter we are going to use then 402 + * make sure it is disabled. 403 + */ 404 + event->hw.idx = idx; 405 + loongarch_pmu_disable_event(idx); 406 + cpuc->events[idx] = event; 407 + 408 + hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE; 409 + if (flags & PERF_EF_START) 410 + loongarch_pmu_start(event, PERF_EF_RELOAD); 411 + 412 + /* Propagate our changes to the userspace mapping. */ 413 + perf_event_update_userpage(event); 414 + 415 + out: 416 + perf_pmu_enable(event->pmu); 417 + return err; 418 + } 419 + 420 + static void loongarch_pmu_del(struct perf_event *event, int flags) 421 + { 422 + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 423 + struct hw_perf_event *hwc = &event->hw; 424 + int idx = hwc->idx; 425 + 426 + WARN_ON(idx < 0 || idx >= loongarch_pmu.num_counters); 427 + 428 + loongarch_pmu_stop(event, PERF_EF_UPDATE); 429 + cpuc->events[idx] = NULL; 430 + clear_bit(idx, cpuc->used_mask); 431 + 432 + perf_event_update_userpage(event); 433 + } 434 + 435 + static void loongarch_pmu_read(struct perf_event *event) 436 + { 437 + struct hw_perf_event *hwc = &event->hw; 438 + 439 + /* Don't read disabled counters! */ 440 + if (hwc->idx < 0) 441 + return; 442 + 443 + loongarch_pmu_event_update(event, hwc, hwc->idx); 444 + } 445 + 446 + static void loongarch_pmu_enable(struct pmu *pmu) 447 + { 448 + resume_local_counters(); 449 + } 450 + 451 + static void loongarch_pmu_disable(struct pmu *pmu) 452 + { 453 + pause_local_counters(); 454 + } 455 + 456 + static DEFINE_MUTEX(pmu_reserve_mutex); 457 + static atomic_t active_events = ATOMIC_INIT(0); 458 + 459 + static int get_pmc_irq(void) 460 + { 461 + struct irq_domain *d = irq_find_matching_fwnode(cpuintc_handle, DOMAIN_BUS_ANY); 462 + 463 + if (d) 464 + return irq_create_mapping(d, EXCCODE_PMC - EXCCODE_INT_START); 465 + 466 + return -EINVAL; 467 + } 468 + 469 + static void reset_counters(void *arg); 470 + static int __hw_perf_event_init(struct perf_event *event); 471 + 472 + static void hw_perf_event_destroy(struct perf_event *event) 473 + { 474 + if (atomic_dec_and_mutex_lock(&active_events, &pmu_reserve_mutex)) { 475 + on_each_cpu(reset_counters, NULL, 1); 476 + free_irq(get_pmc_irq(), &loongarch_pmu); 477 + mutex_unlock(&pmu_reserve_mutex); 478 + } 479 + } 480 + 481 + static void handle_associated_event(struct cpu_hw_events *cpuc, int idx, 482 + struct perf_sample_data *data, struct pt_regs *regs) 483 + { 484 + struct perf_event *event = cpuc->events[idx]; 485 + struct hw_perf_event *hwc = &event->hw; 486 + 487 + loongarch_pmu_event_update(event, hwc, idx); 488 + data->period = event->hw.last_period; 489 + if (!loongarch_pmu_event_set_period(event, hwc, idx)) 490 + return; 491 + 492 + if (perf_event_overflow(event, data, regs)) 493 + loongarch_pmu_disable_event(idx); 494 + } 495 + 496 + static irqreturn_t pmu_handle_irq(int irq, void *dev) 497 + { 498 + int n; 499 + int handled = IRQ_NONE; 500 + uint64_t counter; 501 + struct pt_regs *regs; 502 + struct perf_sample_data data; 503 + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 504 + 505 + /* 506 + * First we pause the local counters, so that when we are locked 507 + * here, the counters are all paused. When it gets locked due to 508 + * perf_disable(), the timer interrupt handler will be delayed. 509 + * 510 + * See also loongarch_pmu_start(). 511 + */ 512 + pause_local_counters(); 513 + 514 + regs = get_irq_regs(); 515 + 516 + perf_sample_data_init(&data, 0, 0); 517 + 518 + for (n = 0; n < loongarch_pmu.num_counters; n++) { 519 + if (test_bit(n, cpuc->used_mask)) { 520 + counter = loongarch_pmu.read_counter(n); 521 + if (counter & loongarch_pmu.overflow) { 522 + handle_associated_event(cpuc, n, &data, regs); 523 + handled = IRQ_HANDLED; 524 + } 525 + } 526 + } 527 + 528 + resume_local_counters(); 529 + 530 + /* 531 + * Do all the work for the pending perf events. We can do this 532 + * in here because the performance counter interrupt is a regular 533 + * interrupt, not NMI. 534 + */ 535 + if (handled == IRQ_HANDLED) 536 + irq_work_run(); 537 + 538 + return handled; 539 + } 540 + 541 + static int loongarch_pmu_event_init(struct perf_event *event) 542 + { 543 + int r, irq; 544 + unsigned long flags; 545 + 546 + /* does not support taken branch sampling */ 547 + if (has_branch_stack(event)) 548 + return -EOPNOTSUPP; 549 + 550 + switch (event->attr.type) { 551 + case PERF_TYPE_RAW: 552 + case PERF_TYPE_HARDWARE: 553 + case PERF_TYPE_HW_CACHE: 554 + break; 555 + 556 + default: 557 + /* Init it to avoid false validate_group */ 558 + event->hw.event_base = 0xffffffff; 559 + return -ENOENT; 560 + } 561 + 562 + if (event->cpu >= 0 && !cpu_online(event->cpu)) 563 + return -ENODEV; 564 + 565 + irq = get_pmc_irq(); 566 + flags = IRQF_PERCPU | IRQF_NOBALANCING | IRQF_NO_THREAD | IRQF_NO_SUSPEND | IRQF_SHARED; 567 + if (!atomic_inc_not_zero(&active_events)) { 568 + mutex_lock(&pmu_reserve_mutex); 569 + if (atomic_read(&active_events) == 0) { 570 + r = request_irq(irq, pmu_handle_irq, flags, "Perf_PMU", &loongarch_pmu); 571 + if (r < 0) { 572 + mutex_unlock(&pmu_reserve_mutex); 573 + pr_warn("PMU IRQ request failed\n"); 574 + return -ENODEV; 575 + } 576 + } 577 + atomic_inc(&active_events); 578 + mutex_unlock(&pmu_reserve_mutex); 579 + } 580 + 581 + return __hw_perf_event_init(event); 582 + } 583 + 584 + static struct pmu pmu = { 585 + .pmu_enable = loongarch_pmu_enable, 586 + .pmu_disable = loongarch_pmu_disable, 587 + .event_init = loongarch_pmu_event_init, 588 + .add = loongarch_pmu_add, 589 + .del = loongarch_pmu_del, 590 + .start = loongarch_pmu_start, 591 + .stop = loongarch_pmu_stop, 592 + .read = loongarch_pmu_read, 593 + }; 594 + 595 + static unsigned int loongarch_pmu_perf_event_encode(const struct loongarch_perf_event *pev) 596 + { 597 + return (pev->event_id & 0xff); 598 + } 599 + 600 + static const struct loongarch_perf_event *loongarch_pmu_map_general_event(int idx) 601 + { 602 + const struct loongarch_perf_event *pev; 603 + 604 + pev = &(*loongarch_pmu.general_event_map)[idx]; 605 + 606 + if (pev->event_id == HW_OP_UNSUPPORTED) 607 + return ERR_PTR(-ENOENT); 608 + 609 + return pev; 610 + } 611 + 612 + static const struct loongarch_perf_event *loongarch_pmu_map_cache_event(u64 config) 613 + { 614 + unsigned int cache_type, cache_op, cache_result; 615 + const struct loongarch_perf_event *pev; 616 + 617 + cache_type = (config >> 0) & 0xff; 618 + if (cache_type >= PERF_COUNT_HW_CACHE_MAX) 619 + return ERR_PTR(-EINVAL); 620 + 621 + cache_op = (config >> 8) & 0xff; 622 + if (cache_op >= PERF_COUNT_HW_CACHE_OP_MAX) 623 + return ERR_PTR(-EINVAL); 624 + 625 + cache_result = (config >> 16) & 0xff; 626 + if (cache_result >= PERF_COUNT_HW_CACHE_RESULT_MAX) 627 + return ERR_PTR(-EINVAL); 628 + 629 + pev = &((*loongarch_pmu.cache_event_map) 630 + [cache_type] 631 + [cache_op] 632 + [cache_result]); 633 + 634 + if (pev->event_id == CACHE_OP_UNSUPPORTED) 635 + return ERR_PTR(-ENOENT); 636 + 637 + return pev; 638 + } 639 + 640 + static int validate_group(struct perf_event *event) 641 + { 642 + struct cpu_hw_events fake_cpuc; 643 + struct perf_event *sibling, *leader = event->group_leader; 644 + 645 + memset(&fake_cpuc, 0, sizeof(fake_cpuc)); 646 + 647 + if (loongarch_pmu_alloc_counter(&fake_cpuc, &leader->hw) < 0) 648 + return -EINVAL; 649 + 650 + for_each_sibling_event(sibling, leader) { 651 + if (loongarch_pmu_alloc_counter(&fake_cpuc, &sibling->hw) < 0) 652 + return -EINVAL; 653 + } 654 + 655 + if (loongarch_pmu_alloc_counter(&fake_cpuc, &event->hw) < 0) 656 + return -EINVAL; 657 + 658 + return 0; 659 + } 660 + 661 + static void reset_counters(void *arg) 662 + { 663 + int n; 664 + int counters = loongarch_pmu.num_counters; 665 + 666 + for (n = 0; n < counters; n++) { 667 + loongarch_pmu_write_control(n, 0); 668 + loongarch_pmu.write_counter(n, 0); 669 + } 670 + } 671 + 672 + static const struct loongarch_perf_event loongson_event_map[PERF_COUNT_HW_MAX] = { 673 + PERF_MAP_ALL_UNSUPPORTED, 674 + [PERF_COUNT_HW_CPU_CYCLES] = { 0x00 }, 675 + [PERF_COUNT_HW_INSTRUCTIONS] = { 0x01 }, 676 + [PERF_COUNT_HW_CACHE_REFERENCES] = { 0x08 }, 677 + [PERF_COUNT_HW_CACHE_MISSES] = { 0x09 }, 678 + [PERF_COUNT_HW_BRANCH_INSTRUCTIONS] = { 0x02 }, 679 + [PERF_COUNT_HW_BRANCH_MISSES] = { 0x03 }, 680 + }; 681 + 682 + static const struct loongarch_perf_event loongson_cache_map 683 + [PERF_COUNT_HW_CACHE_MAX] 684 + [PERF_COUNT_HW_CACHE_OP_MAX] 685 + [PERF_COUNT_HW_CACHE_RESULT_MAX] = { 686 + PERF_CACHE_MAP_ALL_UNSUPPORTED, 687 + [C(L1D)] = { 688 + /* 689 + * Like some other architectures (e.g. ARM), the performance 690 + * counters don't differentiate between read and write 691 + * accesses/misses, so this isn't strictly correct, but it's the 692 + * best we can do. Writes and reads get combined. 693 + */ 694 + [C(OP_READ)] = { 695 + [C(RESULT_ACCESS)] = { 0x8 }, 696 + [C(RESULT_MISS)] = { 0x9 }, 697 + }, 698 + [C(OP_WRITE)] = { 699 + [C(RESULT_ACCESS)] = { 0x8 }, 700 + [C(RESULT_MISS)] = { 0x9 }, 701 + }, 702 + [C(OP_PREFETCH)] = { 703 + [C(RESULT_ACCESS)] = { 0xaa }, 704 + [C(RESULT_MISS)] = { 0xa9 }, 705 + }, 706 + }, 707 + [C(L1I)] = { 708 + [C(OP_READ)] = { 709 + [C(RESULT_ACCESS)] = { 0x6 }, 710 + [C(RESULT_MISS)] = { 0x7 }, 711 + }, 712 + }, 713 + [C(LL)] = { 714 + [C(OP_READ)] = { 715 + [C(RESULT_ACCESS)] = { 0xc }, 716 + [C(RESULT_MISS)] = { 0xd }, 717 + }, 718 + [C(OP_WRITE)] = { 719 + [C(RESULT_ACCESS)] = { 0xc }, 720 + [C(RESULT_MISS)] = { 0xd }, 721 + }, 722 + }, 723 + [C(ITLB)] = { 724 + [C(OP_READ)] = { 725 + [C(RESULT_MISS)] = { 0x3b }, 726 + }, 727 + }, 728 + [C(DTLB)] = { 729 + [C(OP_READ)] = { 730 + [C(RESULT_ACCESS)] = { 0x4 }, 731 + [C(RESULT_MISS)] = { 0x3c }, 732 + }, 733 + [C(OP_WRITE)] = { 734 + [C(RESULT_ACCESS)] = { 0x4 }, 735 + [C(RESULT_MISS)] = { 0x3c }, 736 + }, 737 + }, 738 + [C(BPU)] = { 739 + /* Using the same code for *HW_BRANCH* */ 740 + [C(OP_READ)] = { 741 + [C(RESULT_ACCESS)] = { 0x02 }, 742 + [C(RESULT_MISS)] = { 0x03 }, 743 + }, 744 + }, 745 + }; 746 + 747 + static int __hw_perf_event_init(struct perf_event *event) 748 + { 749 + int err; 750 + struct hw_perf_event *hwc = &event->hw; 751 + struct perf_event_attr *attr = &event->attr; 752 + const struct loongarch_perf_event *pev; 753 + 754 + /* Returning LoongArch event descriptor for generic perf event. */ 755 + if (PERF_TYPE_HARDWARE == event->attr.type) { 756 + if (event->attr.config >= PERF_COUNT_HW_MAX) 757 + return -EINVAL; 758 + pev = loongarch_pmu_map_general_event(event->attr.config); 759 + } else if (PERF_TYPE_HW_CACHE == event->attr.type) { 760 + pev = loongarch_pmu_map_cache_event(event->attr.config); 761 + } else if (PERF_TYPE_RAW == event->attr.type) { 762 + /* We are working on the global raw event. */ 763 + mutex_lock(&raw_event_mutex); 764 + pev = loongarch_pmu.map_raw_event(event->attr.config); 765 + } else { 766 + /* The event type is not (yet) supported. */ 767 + return -EOPNOTSUPP; 768 + } 769 + 770 + if (IS_ERR(pev)) { 771 + if (PERF_TYPE_RAW == event->attr.type) 772 + mutex_unlock(&raw_event_mutex); 773 + return PTR_ERR(pev); 774 + } 775 + 776 + /* 777 + * We allow max flexibility on how each individual counter shared 778 + * by the single CPU operates (the mode exclusion and the range). 779 + */ 780 + hwc->config_base = CSR_PERFCTRL_IE; 781 + 782 + hwc->event_base = loongarch_pmu_perf_event_encode(pev); 783 + if (PERF_TYPE_RAW == event->attr.type) 784 + mutex_unlock(&raw_event_mutex); 785 + 786 + if (!attr->exclude_user) { 787 + hwc->config_base |= CSR_PERFCTRL_PLV3; 788 + hwc->config_base |= CSR_PERFCTRL_PLV2; 789 + } 790 + if (!attr->exclude_kernel) { 791 + hwc->config_base |= CSR_PERFCTRL_PLV0; 792 + } 793 + if (!attr->exclude_hv) { 794 + hwc->config_base |= CSR_PERFCTRL_PLV1; 795 + } 796 + 797 + hwc->config_base &= M_PERFCTL_CONFIG_MASK; 798 + /* 799 + * The event can belong to another cpu. We do not assign a local 800 + * counter for it for now. 801 + */ 802 + hwc->idx = -1; 803 + hwc->config = 0; 804 + 805 + if (!hwc->sample_period) { 806 + hwc->sample_period = loongarch_pmu.max_period; 807 + hwc->last_period = hwc->sample_period; 808 + local64_set(&hwc->period_left, hwc->sample_period); 809 + } 810 + 811 + err = 0; 812 + if (event->group_leader != event) 813 + err = validate_group(event); 814 + 815 + event->destroy = hw_perf_event_destroy; 816 + 817 + if (err) 818 + event->destroy(event); 819 + 820 + return err; 821 + } 822 + 823 + static void pause_local_counters(void) 824 + { 825 + unsigned long flags; 826 + int ctr = loongarch_pmu.num_counters; 827 + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 828 + 829 + local_irq_save(flags); 830 + do { 831 + ctr--; 832 + cpuc->saved_ctrl[ctr] = loongarch_pmu_read_control(ctr); 833 + loongarch_pmu_write_control(ctr, cpuc->saved_ctrl[ctr] & 834 + ~M_PERFCTL_COUNT_EVENT_WHENEVER); 835 + } while (ctr > 0); 836 + local_irq_restore(flags); 837 + } 838 + 839 + static void resume_local_counters(void) 840 + { 841 + int ctr = loongarch_pmu.num_counters; 842 + struct cpu_hw_events *cpuc = this_cpu_ptr(&cpu_hw_events); 843 + 844 + do { 845 + ctr--; 846 + loongarch_pmu_write_control(ctr, cpuc->saved_ctrl[ctr]); 847 + } while (ctr > 0); 848 + } 849 + 850 + static const struct loongarch_perf_event *loongarch_pmu_map_raw_event(u64 config) 851 + { 852 + raw_event.event_id = config & 0xff; 853 + 854 + return &raw_event; 855 + } 856 + 857 + static int __init init_hw_perf_events(void) 858 + { 859 + int counters; 860 + 861 + if (!cpu_has_pmp) 862 + return -ENODEV; 863 + 864 + pr_info("Performance counters: "); 865 + counters = ((read_cpucfg(LOONGARCH_CPUCFG6) & CPUCFG6_PMNUM) >> 4) + 1; 866 + 867 + loongarch_pmu.num_counters = counters; 868 + loongarch_pmu.max_period = (1ULL << 63) - 1; 869 + loongarch_pmu.valid_count = (1ULL << 63) - 1; 870 + loongarch_pmu.overflow = 1ULL << 63; 871 + loongarch_pmu.name = "loongarch/loongson64"; 872 + loongarch_pmu.read_counter = loongarch_pmu_read_counter; 873 + loongarch_pmu.write_counter = loongarch_pmu_write_counter; 874 + loongarch_pmu.map_raw_event = loongarch_pmu_map_raw_event; 875 + loongarch_pmu.general_event_map = &loongson_event_map; 876 + loongarch_pmu.cache_event_map = &loongson_cache_map; 877 + 878 + on_each_cpu(reset_counters, NULL, 1); 879 + 880 + pr_cont("%s PMU enabled, %d %d-bit counters available to each CPU.\n", 881 + loongarch_pmu.name, counters, 64); 882 + 883 + perf_pmu_register(&pmu, "cpu", PERF_TYPE_RAW); 884 + 885 + return 0; 886 + } 887 + early_initcall(init_hw_perf_events);
+53
arch/loongarch/kernel/perf_regs.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Copyright (C) 2022 Loongson Technology Corporation Limited 4 + * 5 + * Derived from MIPS: 6 + * Copyright (C) 2013 Cavium, Inc. 7 + */ 8 + 9 + #include <linux/perf_event.h> 10 + 11 + #include <asm/ptrace.h> 12 + 13 + #ifdef CONFIG_32BIT 14 + u64 perf_reg_abi(struct task_struct *tsk) 15 + { 16 + return PERF_SAMPLE_REGS_ABI_32; 17 + } 18 + #else /* Must be CONFIG_64BIT */ 19 + u64 perf_reg_abi(struct task_struct *tsk) 20 + { 21 + if (test_tsk_thread_flag(tsk, TIF_32BIT_REGS)) 22 + return PERF_SAMPLE_REGS_ABI_32; 23 + else 24 + return PERF_SAMPLE_REGS_ABI_64; 25 + } 26 + #endif /* CONFIG_32BIT */ 27 + 28 + int perf_reg_validate(u64 mask) 29 + { 30 + if (!mask) 31 + return -EINVAL; 32 + if (mask & ~((1ull << PERF_REG_LOONGARCH_MAX) - 1)) 33 + return -EINVAL; 34 + return 0; 35 + } 36 + 37 + u64 perf_reg_value(struct pt_regs *regs, int idx) 38 + { 39 + if (WARN_ON_ONCE((u32)idx >= PERF_REG_LOONGARCH_MAX)) 40 + return 0; 41 + 42 + if ((u32)idx == PERF_REG_LOONGARCH_PC) 43 + return regs->csr_era; 44 + 45 + return regs->regs[idx]; 46 + } 47 + 48 + void perf_get_regs_user(struct perf_regs *regs_user, 49 + struct pt_regs *regs) 50 + { 51 + regs_user->regs = task_pt_regs(current); 52 + regs_user->abi = perf_reg_abi(current); 53 + }
+112
arch/loongarch/kernel/relocate_kernel.S
··· 1 + /* SPDX-License-Identifier: GPL-2.0 */ 2 + /* 3 + * relocate_kernel.S for kexec 4 + * 5 + * Copyright (C) 2022 Loongson Technology Corporation Limited 6 + */ 7 + 8 + #include <linux/kexec.h> 9 + 10 + #include <asm/asm.h> 11 + #include <asm/asmmacro.h> 12 + #include <asm/regdef.h> 13 + #include <asm/loongarch.h> 14 + #include <asm/stackframe.h> 15 + #include <asm/addrspace.h> 16 + 17 + SYM_CODE_START(relocate_new_kernel) 18 + /* 19 + * a0: EFI boot flag for the new kernel 20 + * a1: Command line pointer for the new kernel 21 + * a2: System table pointer for the new kernel 22 + * a3: Start address to jump to after relocation 23 + * a4: Pointer to the current indirection page entry 24 + */ 25 + move s0, a4 26 + 27 + /* 28 + * In case of a kdump/crash kernel, the indirection page is not 29 + * populated as the kernel is directly copied to a reserved location 30 + */ 31 + beqz s0, done 32 + 33 + process_entry: 34 + PTR_L s1, s0, 0 35 + PTR_ADDI s0, s0, SZREG 36 + 37 + /* destination page */ 38 + andi s2, s1, IND_DESTINATION 39 + beqz s2, 1f 40 + li.w t0, ~0x1 41 + and s3, s1, t0 /* store destination addr in s3 */ 42 + b process_entry 43 + 44 + 1: 45 + /* indirection page, update s0 */ 46 + andi s2, s1, IND_INDIRECTION 47 + beqz s2, 1f 48 + li.w t0, ~0x2 49 + and s0, s1, t0 50 + b process_entry 51 + 52 + 1: 53 + /* done page */ 54 + andi s2, s1, IND_DONE 55 + beqz s2, 1f 56 + b done 57 + 58 + 1: 59 + /* source page */ 60 + andi s2, s1, IND_SOURCE 61 + beqz s2, process_entry 62 + li.w t0, ~0x8 63 + and s1, s1, t0 64 + li.w s5, (1 << _PAGE_SHIFT) / SZREG 65 + 66 + copy_word: 67 + /* copy page word by word */ 68 + REG_L s4, s1, 0 69 + REG_S s4, s3, 0 70 + PTR_ADDI s3, s3, SZREG 71 + PTR_ADDI s1, s1, SZREG 72 + LONG_ADDI s5, s5, -1 73 + beqz s5, process_entry 74 + b copy_word 75 + b process_entry 76 + 77 + done: 78 + ibar 0 79 + dbar 0 80 + 81 + /* 82 + * Jump to the new kernel, 83 + * make sure the values of a0, a1, a2 and a3 are not changed. 84 + */ 85 + jr a3 86 + SYM_CODE_END(relocate_new_kernel) 87 + 88 + #ifdef CONFIG_SMP 89 + /* 90 + * Other CPUs should wait until code is relocated and 91 + * then start at the entry point from LOONGARCH_IOCSR_MBUF0. 92 + */ 93 + SYM_CODE_START(kexec_smp_wait) 94 + 1: li.w t0, 0x100 /* wait for init loop */ 95 + 2: addi.w t0, t0, -1 /* limit mailbox access */ 96 + bnez t0, 2b 97 + li.w t1, LOONGARCH_IOCSR_MBUF0 98 + iocsrrd.w s0, t1 /* check PC as an indicator */ 99 + beqz s0, 1b 100 + iocsrrd.d s0, t1 /* get PC via mailbox */ 101 + 102 + li.d t0, CACHE_BASE 103 + or s0, s0, t0 /* s0 = TO_CACHE(s0) */ 104 + jr s0 /* jump to initial PC */ 105 + SYM_CODE_END(kexec_smp_wait) 106 + #endif 107 + 108 + relocate_new_kernel_end: 109 + 110 + SYM_DATA_START(relocate_new_kernel_size) 111 + PTR relocate_new_kernel_end - relocate_new_kernel 112 + SYM_DATA_END(relocate_new_kernel_size)
+75 -1
arch/loongarch/kernel/setup.c
··· 19 19 #include <linux/memblock.h> 20 20 #include <linux/initrd.h> 21 21 #include <linux/ioport.h> 22 + #include <linux/kexec.h> 23 + #include <linux/crash_dump.h> 22 24 #include <linux/root_dev.h> 23 25 #include <linux/console.h> 24 26 #include <linux/pfn.h> ··· 187 185 } 188 186 early_param("mem", early_parse_mem); 189 187 188 + static void __init arch_reserve_vmcore(void) 189 + { 190 + #ifdef CONFIG_PROC_VMCORE 191 + u64 i; 192 + phys_addr_t start, end; 193 + 194 + if (!is_kdump_kernel()) 195 + return; 196 + 197 + if (!elfcorehdr_size) { 198 + for_each_mem_range(i, &start, &end) { 199 + if (elfcorehdr_addr >= start && elfcorehdr_addr < end) { 200 + /* 201 + * Reserve from the elf core header to the end of 202 + * the memory segment, that should all be kdump 203 + * reserved memory. 204 + */ 205 + elfcorehdr_size = end - elfcorehdr_addr; 206 + break; 207 + } 208 + } 209 + } 210 + 211 + if (memblock_is_region_reserved(elfcorehdr_addr, elfcorehdr_size)) { 212 + pr_warn("elfcorehdr is overlapped\n"); 213 + return; 214 + } 215 + 216 + memblock_reserve(elfcorehdr_addr, elfcorehdr_size); 217 + 218 + pr_info("Reserving %llu KiB of memory at 0x%llx for elfcorehdr\n", 219 + elfcorehdr_size >> 10, elfcorehdr_addr); 220 + #endif 221 + } 222 + 223 + static void __init arch_parse_crashkernel(void) 224 + { 225 + #ifdef CONFIG_KEXEC 226 + int ret; 227 + unsigned long long start; 228 + unsigned long long total_mem; 229 + unsigned long long crash_base, crash_size; 230 + 231 + total_mem = memblock_phys_mem_size(); 232 + ret = parse_crashkernel(boot_command_line, total_mem, &crash_size, &crash_base); 233 + if (ret < 0 || crash_size <= 0) 234 + return; 235 + 236 + start = memblock_phys_alloc_range(crash_size, 1, crash_base, crash_base + crash_size); 237 + if (start != crash_base) { 238 + pr_warn("Invalid memory region reserved for crash kernel\n"); 239 + return; 240 + } 241 + 242 + crashk_res.start = crash_base; 243 + crashk_res.end = crash_base + crash_size - 1; 244 + #endif 245 + } 246 + 190 247 void __init platform_init(void) 191 248 { 249 + arch_reserve_vmcore(); 250 + arch_parse_crashkernel(); 251 + 192 252 #ifdef CONFIG_ACPI_TABLE_UPGRADE 193 253 acpi_table_upgrade(); 194 254 #endif ··· 353 289 request_resource(res, &data_resource); 354 290 request_resource(res, &bss_resource); 355 291 } 292 + 293 + #ifdef CONFIG_KEXEC 294 + if (crashk_res.start < crashk_res.end) { 295 + insert_resource(&iomem_resource, &crashk_res); 296 + pr_info("Reserving %ldMB of memory at %ldMB for crashkernel\n", 297 + (unsigned long)((crashk_res.end - crashk_res.start + 1) >> 20), 298 + (unsigned long)(crashk_res.start >> 20)); 299 + } 300 + #endif 356 301 } 357 302 358 303 static int __init reserve_memblock_reserved_regions(void) ··· 421 348 init_environ(); 422 349 efi_init(); 423 350 memblock_init(); 351 + pagetable_init(); 424 352 parse_early_param(); 353 + reserve_initrd_mem(); 425 354 426 355 platform_init(); 427 - pagetable_init(); 428 356 arch_mem_init(cmdline_p); 429 357 430 358 resource_init();
-5
arch/loongarch/kernel/smp.c
··· 240 240 241 241 #ifdef CONFIG_HOTPLUG_CPU 242 242 243 - static bool io_master(int cpu) 244 - { 245 - return test_bit(cpu, &loongson_sysconf.cores_io_master); 246 - } 247 - 248 243 int loongson3_cpu_disable(void) 249 244 { 250 245 unsigned long flags;
+65
arch/loongarch/kernel/sysrq.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * LoongArch specific sysrq operations. 4 + * 5 + * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 6 + */ 7 + #include <linux/init.h> 8 + #include <linux/smp.h> 9 + #include <linux/spinlock.h> 10 + #include <linux/sysrq.h> 11 + #include <linux/workqueue.h> 12 + 13 + #include <asm/cpu-features.h> 14 + #include <asm/tlb.h> 15 + 16 + /* 17 + * Dump TLB entries on all CPUs. 18 + */ 19 + 20 + static DEFINE_SPINLOCK(show_lock); 21 + 22 + static void sysrq_tlbdump_single(void *dummy) 23 + { 24 + unsigned long flags; 25 + 26 + spin_lock_irqsave(&show_lock, flags); 27 + 28 + pr_info("CPU%d:\n", smp_processor_id()); 29 + dump_tlb_regs(); 30 + pr_info("\n"); 31 + dump_tlb_all(); 32 + pr_info("\n"); 33 + 34 + spin_unlock_irqrestore(&show_lock, flags); 35 + } 36 + 37 + #ifdef CONFIG_SMP 38 + static void sysrq_tlbdump_othercpus(struct work_struct *dummy) 39 + { 40 + smp_call_function(sysrq_tlbdump_single, NULL, 0); 41 + } 42 + 43 + static DECLARE_WORK(sysrq_tlbdump, sysrq_tlbdump_othercpus); 44 + #endif 45 + 46 + static void sysrq_handle_tlbdump(int key) 47 + { 48 + sysrq_tlbdump_single(NULL); 49 + #ifdef CONFIG_SMP 50 + schedule_work(&sysrq_tlbdump); 51 + #endif 52 + } 53 + 54 + static struct sysrq_key_op sysrq_tlbdump_op = { 55 + .handler = sysrq_handle_tlbdump, 56 + .help_msg = "show-tlbs(x)", 57 + .action_msg = "Show TLB entries", 58 + .enable_mask = SYSRQ_ENABLE_DUMP, 59 + }; 60 + 61 + static int __init loongarch_sysrq_init(void) 62 + { 63 + return register_sysrq_key('x', &sysrq_tlbdump_op); 64 + } 65 + arch_initcall(loongarch_sysrq_init);
+2 -1
arch/loongarch/kernel/topology.c
··· 5 5 #include <linux/node.h> 6 6 #include <linux/nodemask.h> 7 7 #include <linux/percpu.h> 8 + #include <asm/bootinfo.h> 8 9 9 10 static DEFINE_PER_CPU(struct cpu, cpu_devices); 10 11 ··· 41 40 for_each_present_cpu(i) { 42 41 struct cpu *c = &per_cpu(cpu_devices, i); 43 42 44 - c->hotpluggable = !!i; 43 + c->hotpluggable = !io_master(i); 45 44 ret = register_cpu(c, i); 46 45 if (ret < 0) 47 46 pr_warn("topology_init: register_cpu %d failed (%d)\n", i, ret);
+28 -5
arch/loongarch/kernel/traps.c
··· 10 10 #include <linux/entry-common.h> 11 11 #include <linux/init.h> 12 12 #include <linux/kernel.h> 13 + #include <linux/kexec.h> 13 14 #include <linux/module.h> 14 15 #include <linux/extable.h> 15 16 #include <linux/mm.h> ··· 247 246 248 247 oops_exit(); 249 248 249 + if (regs && kexec_should_crash(current)) 250 + crash_kexec(regs); 251 + 250 252 if (in_interrupt()) 251 253 panic("Fatal exception in interrupt"); 252 254 ··· 378 374 irqentry_exit(regs, state); 379 375 } 380 376 377 + #ifdef CONFIG_GENERIC_BUG 378 + int is_valid_bugaddr(unsigned long addr) 379 + { 380 + return 1; 381 + } 382 + #endif /* CONFIG_GENERIC_BUG */ 383 + 384 + static void bug_handler(struct pt_regs *regs) 385 + { 386 + switch (report_bug(regs->csr_era, regs)) { 387 + case BUG_TRAP_TYPE_BUG: 388 + case BUG_TRAP_TYPE_NONE: 389 + die_if_kernel("Oops - BUG", regs); 390 + force_sig(SIGTRAP); 391 + break; 392 + 393 + case BUG_TRAP_TYPE_WARN: 394 + /* Skip the BUG instruction and continue */ 395 + regs->csr_era += LOONGARCH_INSN_SIZE; 396 + break; 397 + } 398 + } 399 + 381 400 asmlinkage void noinstr do_bp(struct pt_regs *regs) 382 401 { 383 402 bool user = user_mode(regs); ··· 454 427 455 428 switch (bcode) { 456 429 case BRK_BUG: 457 - die_if_kernel("Kernel bug detected", regs); 458 - force_sig(SIGTRAP); 430 + bug_handler(regs); 459 431 break; 460 432 case BRK_DIVZERO: 461 433 die_if_kernel("Break instruction in kernel code", regs); ··· 645 619 646 620 irqentry_exit(regs, state); 647 621 } 648 - 649 - extern void tlb_init(int cpu); 650 - extern void cache_error_setup(void); 651 622 652 623 unsigned long eentry; 653 624 unsigned long tlbrentry;
+4
arch/loongarch/kernel/vmlinux.lds.S
··· 55 55 56 56 EXCEPTION_TABLE(16) 57 57 58 + .got : ALIGN(16) { *(.got) } 59 + .plt : ALIGN(16) { *(.plt) } 60 + .got.plt : ALIGN(16) { *(.got.plt) } 61 + 58 62 . = ALIGN(PECOFF_SEGMENT_ALIGN); 59 63 __init_begin = .; 60 64 __inittext_begin = .;
+110 -91
arch/loongarch/mm/cache.c
··· 6 6 * Copyright (C) 1994 - 2003, 06, 07 by Ralf Baechle (ralf@linux-mips.org) 7 7 * Copyright (C) 2007 MIPS Technologies, Inc. 8 8 */ 9 + #include <linux/cacheinfo.h> 9 10 #include <linux/export.h> 10 - #include <linux/fcntl.h> 11 11 #include <linux/fs.h> 12 12 #include <linux/highmem.h> 13 13 #include <linux/kernel.h> ··· 16 16 #include <linux/sched.h> 17 17 #include <linux/syscalls.h> 18 18 19 + #include <asm/bootinfo.h> 19 20 #include <asm/cacheflush.h> 20 21 #include <asm/cpu.h> 21 22 #include <asm/cpu-features.h> 22 - #include <asm/dma.h> 23 23 #include <asm/loongarch.h> 24 + #include <asm/numa.h> 24 25 #include <asm/processor.h> 25 26 #include <asm/setup.h> 27 + 28 + void cache_error_setup(void) 29 + { 30 + extern char __weak except_vec_cex; 31 + set_merr_handler(0x0, &except_vec_cex, 0x80); 32 + } 26 33 27 34 /* 28 35 * LoongArch maintains ICache/DCache coherency by hardware, ··· 41 34 } 42 35 EXPORT_SYMBOL(local_flush_icache_range); 43 36 44 - void cache_error_setup(void) 37 + static void flush_cache_leaf(unsigned int leaf) 45 38 { 46 - extern char __weak except_vec_cex; 47 - set_merr_handler(0x0, &except_vec_cex, 0x80); 39 + int i, j, nr_nodes; 40 + uint64_t addr = CSR_DMW0_BASE; 41 + struct cache_desc *cdesc = current_cpu_data.cache_leaves + leaf; 42 + 43 + nr_nodes = cache_private(cdesc) ? 1 : loongson_sysconf.nr_nodes; 44 + 45 + do { 46 + for (i = 0; i < cdesc->sets; i++) { 47 + for (j = 0; j < cdesc->ways; j++) { 48 + flush_cache_line(leaf, addr); 49 + addr++; 50 + } 51 + 52 + addr -= cdesc->ways; 53 + addr += cdesc->linesz; 54 + } 55 + addr += (1ULL << NODE_ADDRSPACE_SHIFT); 56 + } while (--nr_nodes > 0); 48 57 } 49 58 50 - static unsigned long icache_size __read_mostly; 51 - static unsigned long dcache_size __read_mostly; 52 - static unsigned long vcache_size __read_mostly; 53 - static unsigned long scache_size __read_mostly; 54 - 55 - static char *way_string[] = { NULL, "direct mapped", "2-way", 56 - "3-way", "4-way", "5-way", "6-way", "7-way", "8-way", 57 - "9-way", "10-way", "11-way", "12-way", 58 - "13-way", "14-way", "15-way", "16-way", 59 - }; 60 - 61 - static void probe_pcache(void) 59 + asmlinkage __visible void __flush_cache_all(void) 62 60 { 63 - struct cpuinfo_loongarch *c = &current_cpu_data; 64 - unsigned int lsize, sets, ways; 65 - unsigned int config; 61 + int leaf; 62 + struct cache_desc *cdesc = current_cpu_data.cache_leaves; 63 + unsigned int cache_present = current_cpu_data.cache_leaves_present; 66 64 67 - config = read_cpucfg(LOONGARCH_CPUCFG17); 68 - lsize = 1 << ((config & CPUCFG17_L1I_SIZE_M) >> CPUCFG17_L1I_SIZE); 69 - sets = 1 << ((config & CPUCFG17_L1I_SETS_M) >> CPUCFG17_L1I_SETS); 70 - ways = ((config & CPUCFG17_L1I_WAYS_M) >> CPUCFG17_L1I_WAYS) + 1; 65 + leaf = cache_present - 1; 66 + if (cache_inclusive(cdesc + leaf)) { 67 + flush_cache_leaf(leaf); 68 + return; 69 + } 71 70 72 - c->icache.linesz = lsize; 73 - c->icache.sets = sets; 74 - c->icache.ways = ways; 75 - icache_size = sets * ways * lsize; 76 - c->icache.waysize = icache_size / c->icache.ways; 77 - 78 - config = read_cpucfg(LOONGARCH_CPUCFG18); 79 - lsize = 1 << ((config & CPUCFG18_L1D_SIZE_M) >> CPUCFG18_L1D_SIZE); 80 - sets = 1 << ((config & CPUCFG18_L1D_SETS_M) >> CPUCFG18_L1D_SETS); 81 - ways = ((config & CPUCFG18_L1D_WAYS_M) >> CPUCFG18_L1D_WAYS) + 1; 82 - 83 - c->dcache.linesz = lsize; 84 - c->dcache.sets = sets; 85 - c->dcache.ways = ways; 86 - dcache_size = sets * ways * lsize; 87 - c->dcache.waysize = dcache_size / c->dcache.ways; 88 - 89 - c->options |= LOONGARCH_CPU_PREFETCH; 90 - 91 - pr_info("Primary instruction cache %ldkB, %s, %s, linesize %d bytes.\n", 92 - icache_size >> 10, way_string[c->icache.ways], "VIPT", c->icache.linesz); 93 - 94 - pr_info("Primary data cache %ldkB, %s, %s, %s, linesize %d bytes\n", 95 - dcache_size >> 10, way_string[c->dcache.ways], "VIPT", "no aliases", c->dcache.linesz); 71 + for (leaf = 0; leaf < cache_present; leaf++) 72 + flush_cache_leaf(leaf); 96 73 } 97 74 98 - static void probe_vcache(void) 99 - { 100 - struct cpuinfo_loongarch *c = &current_cpu_data; 101 - unsigned int lsize, sets, ways; 102 - unsigned int config; 75 + #define L1IUPRE (1 << 0) 76 + #define L1IUUNIFY (1 << 1) 77 + #define L1DPRE (1 << 2) 103 78 104 - config = read_cpucfg(LOONGARCH_CPUCFG19); 105 - lsize = 1 << ((config & CPUCFG19_L2_SIZE_M) >> CPUCFG19_L2_SIZE); 106 - sets = 1 << ((config & CPUCFG19_L2_SETS_M) >> CPUCFG19_L2_SETS); 107 - ways = ((config & CPUCFG19_L2_WAYS_M) >> CPUCFG19_L2_WAYS) + 1; 79 + #define LXIUPRE (1 << 0) 80 + #define LXIUUNIFY (1 << 1) 81 + #define LXIUPRIV (1 << 2) 82 + #define LXIUINCL (1 << 3) 83 + #define LXDPRE (1 << 4) 84 + #define LXDPRIV (1 << 5) 85 + #define LXDINCL (1 << 6) 108 86 109 - c->vcache.linesz = lsize; 110 - c->vcache.sets = sets; 111 - c->vcache.ways = ways; 112 - vcache_size = lsize * sets * ways; 113 - c->vcache.waysize = vcache_size / c->vcache.ways; 114 - 115 - pr_info("Unified victim cache %ldkB %s, linesize %d bytes.\n", 116 - vcache_size >> 10, way_string[c->vcache.ways], c->vcache.linesz); 117 - } 118 - 119 - static void probe_scache(void) 120 - { 121 - struct cpuinfo_loongarch *c = &current_cpu_data; 122 - unsigned int lsize, sets, ways; 123 - unsigned int config; 124 - 125 - config = read_cpucfg(LOONGARCH_CPUCFG20); 126 - lsize = 1 << ((config & CPUCFG20_L3_SIZE_M) >> CPUCFG20_L3_SIZE); 127 - sets = 1 << ((config & CPUCFG20_L3_SETS_M) >> CPUCFG20_L3_SETS); 128 - ways = ((config & CPUCFG20_L3_WAYS_M) >> CPUCFG20_L3_WAYS) + 1; 129 - 130 - c->scache.linesz = lsize; 131 - c->scache.sets = sets; 132 - c->scache.ways = ways; 133 - /* 4 cores. scaches are shared */ 134 - scache_size = lsize * sets * ways; 135 - c->scache.waysize = scache_size / c->scache.ways; 136 - 137 - pr_info("Unified secondary cache %ldkB %s, linesize %d bytes.\n", 138 - scache_size >> 10, way_string[c->scache.ways], c->scache.linesz); 139 - } 87 + #define populate_cache_properties(cfg0, cdesc, level, leaf) \ 88 + do { \ 89 + unsigned int cfg1; \ 90 + \ 91 + cfg1 = read_cpucfg(LOONGARCH_CPUCFG17 + leaf); \ 92 + if (level == 1) { \ 93 + cdesc->flags |= CACHE_PRIVATE; \ 94 + } else { \ 95 + if (cfg0 & LXIUPRIV) \ 96 + cdesc->flags |= CACHE_PRIVATE; \ 97 + if (cfg0 & LXIUINCL) \ 98 + cdesc->flags |= CACHE_INCLUSIVE; \ 99 + } \ 100 + cdesc->level = level; \ 101 + cdesc->flags |= CACHE_PRESENT; \ 102 + cdesc->ways = ((cfg1 & CPUCFG_CACHE_WAYS_M) >> CPUCFG_CACHE_WAYS) + 1; \ 103 + cdesc->sets = 1 << ((cfg1 & CPUCFG_CACHE_SETS_M) >> CPUCFG_CACHE_SETS); \ 104 + cdesc->linesz = 1 << ((cfg1 & CPUCFG_CACHE_LSIZE_M) >> CPUCFG_CACHE_LSIZE); \ 105 + cdesc++; leaf++; \ 106 + } while (0) 140 107 141 108 void cpu_cache_init(void) 142 109 { 143 - probe_pcache(); 144 - probe_vcache(); 145 - probe_scache(); 110 + unsigned int leaf = 0, level = 1; 111 + unsigned int config = read_cpucfg(LOONGARCH_CPUCFG16); 112 + struct cache_desc *cdesc = current_cpu_data.cache_leaves; 146 113 114 + if (config & L1IUPRE) { 115 + if (config & L1IUUNIFY) 116 + cdesc->type = CACHE_TYPE_UNIFIED; 117 + else 118 + cdesc->type = CACHE_TYPE_INST; 119 + populate_cache_properties(config, cdesc, level, leaf); 120 + } 121 + 122 + if (config & L1DPRE) { 123 + cdesc->type = CACHE_TYPE_DATA; 124 + populate_cache_properties(config, cdesc, level, leaf); 125 + } 126 + 127 + config = config >> 3; 128 + for (level = 2; level <= CACHE_LEVEL_MAX; level++) { 129 + if (!config) 130 + break; 131 + 132 + if (config & LXIUPRE) { 133 + if (config & LXIUUNIFY) 134 + cdesc->type = CACHE_TYPE_UNIFIED; 135 + else 136 + cdesc->type = CACHE_TYPE_INST; 137 + populate_cache_properties(config, cdesc, level, leaf); 138 + } 139 + 140 + if (config & LXDPRE) { 141 + cdesc->type = CACHE_TYPE_DATA; 142 + populate_cache_properties(config, cdesc, level, leaf); 143 + } 144 + 145 + config = config >> 7; 146 + } 147 + 148 + BUG_ON(leaf > CACHE_LEAVES_MAX); 149 + 150 + current_cpu_data.cache_leaves_present = leaf; 151 + current_cpu_data.options |= LOONGARCH_CPU_PREFETCH; 147 152 shm_align_mask = PAGE_SIZE - 1; 148 153 } 149 154
+64
arch/loongarch/mm/init.c
··· 152 152 #endif 153 153 #endif 154 154 155 + static pte_t *fixmap_pte(unsigned long addr) 156 + { 157 + pgd_t *pgd; 158 + p4d_t *p4d; 159 + pud_t *pud; 160 + pmd_t *pmd; 161 + 162 + pgd = pgd_offset_k(addr); 163 + p4d = p4d_offset(pgd, addr); 164 + 165 + if (pgd_none(*pgd)) { 166 + pud_t *new __maybe_unused; 167 + 168 + new = memblock_alloc_low(PAGE_SIZE, PAGE_SIZE); 169 + pgd_populate(&init_mm, pgd, new); 170 + #ifndef __PAGETABLE_PUD_FOLDED 171 + pud_init((unsigned long)new, (unsigned long)invalid_pmd_table); 172 + #endif 173 + } 174 + 175 + pud = pud_offset(p4d, addr); 176 + if (pud_none(*pud)) { 177 + pmd_t *new __maybe_unused; 178 + 179 + new = memblock_alloc_low(PAGE_SIZE, PAGE_SIZE); 180 + pud_populate(&init_mm, pud, new); 181 + #ifndef __PAGETABLE_PMD_FOLDED 182 + pmd_init((unsigned long)new, (unsigned long)invalid_pte_table); 183 + #endif 184 + } 185 + 186 + pmd = pmd_offset(pud, addr); 187 + if (pmd_none(*pmd)) { 188 + pte_t *new __maybe_unused; 189 + 190 + new = memblock_alloc_low(PAGE_SIZE, PAGE_SIZE); 191 + pmd_populate_kernel(&init_mm, pmd, new); 192 + } 193 + 194 + return pte_offset_kernel(pmd, addr); 195 + } 196 + 197 + void __init __set_fixmap(enum fixed_addresses idx, 198 + phys_addr_t phys, pgprot_t flags) 199 + { 200 + unsigned long addr = __fix_to_virt(idx); 201 + pte_t *ptep; 202 + 203 + BUG_ON(idx <= FIX_HOLE || idx >= __end_of_fixed_addresses); 204 + 205 + ptep = fixmap_pte(addr); 206 + if (!pte_none(*ptep)) { 207 + pte_ERROR(*ptep); 208 + return; 209 + } 210 + 211 + if (pgprot_val(flags)) 212 + set_pte(ptep, pfn_pte(phys >> PAGE_SHIFT, flags)); 213 + else { 214 + pte_clear(&init_mm, addr, ptep); 215 + flush_tlb_kernel_range(addr, addr + PAGE_SIZE); 216 + } 217 + } 218 + 155 219 /* 156 220 * Align swapper_pg_dir in to 64K, allows its address to be loaded 157 221 * with a single LUI instruction in the TLB handlers. If we used
+29
arch/loongarch/mm/mmap.c
··· 3 3 * Copyright (C) 2020-2022 Loongson Technology Corporation Limited 4 4 */ 5 5 #include <linux/export.h> 6 + #include <linux/io.h> 7 + #include <linux/memblock.h> 6 8 #include <linux/mm.h> 7 9 #include <linux/mman.h> 8 10 ··· 118 116 return pfn_valid(PFN_DOWN(PHYSADDR(kaddr))); 119 117 } 120 118 EXPORT_SYMBOL_GPL(__virt_addr_valid); 119 + 120 + /* 121 + * You really shouldn't be using read() or write() on /dev/mem. This might go 122 + * away in the future. 123 + */ 124 + int valid_phys_addr_range(phys_addr_t addr, size_t size) 125 + { 126 + /* 127 + * Check whether addr is covered by a memory region without the 128 + * MEMBLOCK_NOMAP attribute, and whether that region covers the 129 + * entire range. In theory, this could lead to false negatives 130 + * if the range is covered by distinct but adjacent memory regions 131 + * that only differ in other attributes. However, few of such 132 + * attributes have been defined, and it is debatable whether it 133 + * follows that /dev/mem read() calls should be able traverse 134 + * such boundaries. 135 + */ 136 + return memblock_is_region_memory(addr, size) && memblock_is_map_memory(addr); 137 + } 138 + 139 + /* 140 + * Do not allow /dev/mem mappings beyond the supported physical range. 141 + */ 142 + int valid_mmap_phys_addr_range(unsigned long pfn, size_t size) 143 + { 144 + return !(((pfn << PAGE_SHIFT) + size) & ~(GENMASK_ULL(cpu_pabits, 0))); 145 + }
+3 -2
arch/loongarch/mm/tlb.c
··· 258 258 void setup_tlb_handler(int cpu) 259 259 { 260 260 setup_ptwalker(); 261 - output_pgtable_bits_defines(); 261 + local_flush_tlb_all(); 262 262 263 263 /* The tlb handlers are generated only once */ 264 264 if (cpu == 0) { ··· 301 301 write_csr_pagesize(PS_DEFAULT_SIZE); 302 302 write_csr_stlbpgsize(PS_DEFAULT_SIZE); 303 303 write_csr_tlbrefill_pagesize(PS_DEFAULT_SIZE); 304 + 304 305 setup_tlb_handler(cpu); 305 - local_flush_tlb_all(); 306 + output_pgtable_bits_defines(); 306 307 }
+256 -299
arch/loongarch/mm/tlbex.S
··· 10 10 #include <asm/regdef.h> 11 11 #include <asm/stackframe.h> 12 12 13 + #define PTRS_PER_PGD_BITS (PAGE_SHIFT - 3) 14 + #define PTRS_PER_PUD_BITS (PAGE_SHIFT - 3) 15 + #define PTRS_PER_PMD_BITS (PAGE_SHIFT - 3) 16 + #define PTRS_PER_PTE_BITS (PAGE_SHIFT - 3) 17 + 13 18 .macro tlb_do_page_fault, write 14 19 SYM_FUNC_START(tlb_do_page_fault_\write) 15 20 SAVE_ALL 16 - csrrd a2, LOONGARCH_CSR_BADV 17 - move a0, sp 18 - REG_S a2, sp, PT_BVADDR 19 - li.w a1, \write 20 - la.abs t0, do_page_fault 21 - jirl ra, t0, 0 21 + csrrd a2, LOONGARCH_CSR_BADV 22 + move a0, sp 23 + REG_S a2, sp, PT_BVADDR 24 + li.w a1, \write 25 + la.abs t0, do_page_fault 26 + jirl ra, t0, 0 22 27 RESTORE_ALL_AND_RET 23 28 SYM_FUNC_END(tlb_do_page_fault_\write) 24 29 .endm ··· 34 29 SYM_FUNC_START(handle_tlb_protect) 35 30 BACKUP_T0T1 36 31 SAVE_ALL 37 - move a0, sp 38 - move a1, zero 39 - csrrd a2, LOONGARCH_CSR_BADV 40 - REG_S a2, sp, PT_BVADDR 41 - la.abs t0, do_page_fault 42 - jirl ra, t0, 0 32 + move a0, sp 33 + move a1, zero 34 + csrrd a2, LOONGARCH_CSR_BADV 35 + REG_S a2, sp, PT_BVADDR 36 + la.abs t0, do_page_fault 37 + jirl ra, t0, 0 43 38 RESTORE_ALL_AND_RET 44 39 SYM_FUNC_END(handle_tlb_protect) 45 40 46 41 SYM_FUNC_START(handle_tlb_load) 47 - csrwr t0, EXCEPTION_KS0 48 - csrwr t1, EXCEPTION_KS1 49 - csrwr ra, EXCEPTION_KS2 42 + csrwr t0, EXCEPTION_KS0 43 + csrwr t1, EXCEPTION_KS1 44 + csrwr ra, EXCEPTION_KS2 50 45 51 46 /* 52 47 * The vmalloc handling is not in the hotpath. 53 48 */ 54 - csrrd t0, LOONGARCH_CSR_BADV 55 - bltz t0, vmalloc_load 56 - csrrd t1, LOONGARCH_CSR_PGDL 49 + csrrd t0, LOONGARCH_CSR_BADV 50 + bltz t0, vmalloc_load 51 + csrrd t1, LOONGARCH_CSR_PGDL 57 52 58 53 vmalloc_done_load: 59 54 /* Get PGD offset in bytes */ 60 - srli.d t0, t0, PGDIR_SHIFT 61 - andi t0, t0, (PTRS_PER_PGD - 1) 62 - slli.d t0, t0, 3 63 - add.d t1, t1, t0 55 + bstrpick.d ra, t0, PTRS_PER_PGD_BITS + PGDIR_SHIFT - 1, PGDIR_SHIFT 56 + alsl.d t1, ra, t1, 3 64 57 #if CONFIG_PGTABLE_LEVELS > 3 65 - csrrd t0, LOONGARCH_CSR_BADV 66 - ld.d t1, t1, 0 67 - srli.d t0, t0, PUD_SHIFT 68 - andi t0, t0, (PTRS_PER_PUD - 1) 69 - slli.d t0, t0, 3 70 - add.d t1, t1, t0 58 + ld.d t1, t1, 0 59 + bstrpick.d ra, t0, PTRS_PER_PUD_BITS + PUD_SHIFT - 1, PUD_SHIFT 60 + alsl.d t1, ra, t1, 3 71 61 #endif 72 62 #if CONFIG_PGTABLE_LEVELS > 2 73 - csrrd t0, LOONGARCH_CSR_BADV 74 - ld.d t1, t1, 0 75 - srli.d t0, t0, PMD_SHIFT 76 - andi t0, t0, (PTRS_PER_PMD - 1) 77 - slli.d t0, t0, 3 78 - add.d t1, t1, t0 63 + ld.d t1, t1, 0 64 + bstrpick.d ra, t0, PTRS_PER_PMD_BITS + PMD_SHIFT - 1, PMD_SHIFT 65 + alsl.d t1, ra, t1, 3 79 66 #endif 80 - ld.d ra, t1, 0 67 + ld.d ra, t1, 0 81 68 82 69 /* 83 70 * For huge tlb entries, pmde doesn't contain an address but 84 71 * instead contains the tlb pte. Check the PAGE_HUGE bit and 85 72 * see if we need to jump to huge tlb processing. 86 73 */ 87 - andi t0, ra, _PAGE_HUGE 88 - bnez t0, tlb_huge_update_load 74 + rotri.d ra, ra, _PAGE_HUGE_SHIFT + 1 75 + bltz ra, tlb_huge_update_load 89 76 90 - csrrd t0, LOONGARCH_CSR_BADV 91 - srli.d t0, t0, PAGE_SHIFT 92 - andi t0, t0, (PTRS_PER_PTE - 1) 93 - slli.d t0, t0, _PTE_T_LOG2 94 - add.d t1, ra, t0 77 + rotri.d ra, ra, 64 - (_PAGE_HUGE_SHIFT + 1) 78 + bstrpick.d t0, t0, PTRS_PER_PTE_BITS + PAGE_SHIFT - 1, PAGE_SHIFT 79 + alsl.d t1, t0, ra, _PTE_T_LOG2 95 80 96 81 #ifdef CONFIG_SMP 97 82 smp_pgtable_change_load: 98 - #endif 99 - #ifdef CONFIG_SMP 100 - ll.d t0, t1, 0 83 + ll.d t0, t1, 0 101 84 #else 102 - ld.d t0, t1, 0 85 + ld.d t0, t1, 0 86 + #endif 87 + andi ra, t0, _PAGE_PRESENT 88 + beqz ra, nopage_tlb_load 89 + 90 + ori t0, t0, _PAGE_VALID 91 + #ifdef CONFIG_SMP 92 + sc.d t0, t1, 0 93 + beqz t0, smp_pgtable_change_load 94 + #else 95 + st.d t0, t1, 0 103 96 #endif 104 97 tlbsrch 105 - 106 - srli.d ra, t0, _PAGE_PRESENT_SHIFT 107 - andi ra, ra, 1 108 - beqz ra, nopage_tlb_load 109 - 110 - ori t0, t0, _PAGE_VALID 111 - #ifdef CONFIG_SMP 112 - sc.d t0, t1, 0 113 - beqz t0, smp_pgtable_change_load 114 - #else 115 - st.d t0, t1, 0 116 - #endif 117 - ori t1, t1, 8 118 - xori t1, t1, 8 119 - ld.d t0, t1, 0 120 - ld.d t1, t1, 8 121 - csrwr t0, LOONGARCH_CSR_TLBELO0 122 - csrwr t1, LOONGARCH_CSR_TLBELO1 98 + bstrins.d t1, zero, 3, 3 99 + ld.d t0, t1, 0 100 + ld.d t1, t1, 8 101 + csrwr t0, LOONGARCH_CSR_TLBELO0 102 + csrwr t1, LOONGARCH_CSR_TLBELO1 123 103 tlbwr 124 - leave_load: 125 - csrrd t0, EXCEPTION_KS0 126 - csrrd t1, EXCEPTION_KS1 127 - csrrd ra, EXCEPTION_KS2 104 + 105 + csrrd t0, EXCEPTION_KS0 106 + csrrd t1, EXCEPTION_KS1 107 + csrrd ra, EXCEPTION_KS2 128 108 ertn 109 + 129 110 #ifdef CONFIG_64BIT 130 111 vmalloc_load: 131 - la.abs t1, swapper_pg_dir 132 - b vmalloc_done_load 112 + la.abs t1, swapper_pg_dir 113 + b vmalloc_done_load 133 114 #endif 134 115 135 - /* 136 - * This is the entry point when build_tlbchange_handler_head 137 - * spots a huge page. 138 - */ 116 + /* This is the entry point of a huge page. */ 139 117 tlb_huge_update_load: 140 118 #ifdef CONFIG_SMP 141 - ll.d t0, t1, 0 142 - #else 143 - ld.d t0, t1, 0 119 + ll.d ra, t1, 0 144 120 #endif 145 - srli.d ra, t0, _PAGE_PRESENT_SHIFT 146 - andi ra, ra, 1 147 - beqz ra, nopage_tlb_load 148 - tlbsrch 121 + andi t0, ra, _PAGE_PRESENT 122 + beqz t0, nopage_tlb_load 149 123 150 - ori t0, t0, _PAGE_VALID 151 124 #ifdef CONFIG_SMP 152 - sc.d t0, t1, 0 153 - beqz t0, tlb_huge_update_load 154 - ld.d t0, t1, 0 125 + ori t0, ra, _PAGE_VALID 126 + sc.d t0, t1, 0 127 + beqz t0, tlb_huge_update_load 128 + ori t0, ra, _PAGE_VALID 155 129 #else 156 - st.d t0, t1, 0 130 + rotri.d ra, ra, 64 - (_PAGE_HUGE_SHIFT + 1) 131 + ori t0, ra, _PAGE_VALID 132 + st.d t0, t1, 0 157 133 #endif 134 + tlbsrch 158 135 addu16i.d t1, zero, -(CSR_TLBIDX_EHINV >> 16) 159 136 addi.d ra, t1, 0 160 137 csrxchg ra, t1, LOONGARCH_CSR_TLBIDX 161 138 tlbwr 162 139 163 - csrxchg zero, t1, LOONGARCH_CSR_TLBIDX 140 + csrxchg zero, t1, LOONGARCH_CSR_TLBIDX 164 141 165 142 /* 166 143 * A huge PTE describes an area the size of the ··· 154 167 * address space. 155 168 */ 156 169 /* Huge page: Move Global bit */ 157 - xori t0, t0, _PAGE_HUGE 158 - lu12i.w t1, _PAGE_HGLOBAL >> 12 159 - and t1, t0, t1 160 - srli.d t1, t1, (_PAGE_HGLOBAL_SHIFT - _PAGE_GLOBAL_SHIFT) 161 - or t0, t0, t1 170 + xori t0, t0, _PAGE_HUGE 171 + lu12i.w t1, _PAGE_HGLOBAL >> 12 172 + and t1, t0, t1 173 + srli.d t1, t1, (_PAGE_HGLOBAL_SHIFT - _PAGE_GLOBAL_SHIFT) 174 + or t0, t0, t1 162 175 163 - addi.d ra, t0, 0 164 - csrwr t0, LOONGARCH_CSR_TLBELO0 165 - addi.d t0, ra, 0 176 + move ra, t0 177 + csrwr ra, LOONGARCH_CSR_TLBELO0 166 178 167 179 /* Convert to entrylo1 */ 168 - addi.d t1, zero, 1 169 - slli.d t1, t1, (HPAGE_SHIFT - 1) 170 - add.d t0, t0, t1 171 - csrwr t0, LOONGARCH_CSR_TLBELO1 180 + addi.d t1, zero, 1 181 + slli.d t1, t1, (HPAGE_SHIFT - 1) 182 + add.d t0, t0, t1 183 + csrwr t0, LOONGARCH_CSR_TLBELO1 172 184 173 185 /* Set huge page tlb entry size */ 174 186 addu16i.d t0, zero, (CSR_TLBIDX_PS >> 16) ··· 180 194 addu16i.d t1, zero, (PS_DEFAULT_SIZE << (CSR_TLBIDX_PS_SHIFT - 16)) 181 195 csrxchg t1, t0, LOONGARCH_CSR_TLBIDX 182 196 197 + csrrd t0, EXCEPTION_KS0 198 + csrrd t1, EXCEPTION_KS1 199 + csrrd ra, EXCEPTION_KS2 200 + ertn 201 + 183 202 nopage_tlb_load: 184 - dbar 0 185 - csrrd ra, EXCEPTION_KS2 186 - la.abs t0, tlb_do_page_fault_0 187 - jr t0 203 + dbar 0 204 + csrrd ra, EXCEPTION_KS2 205 + la.abs t0, tlb_do_page_fault_0 206 + jr t0 188 207 SYM_FUNC_END(handle_tlb_load) 189 208 190 209 SYM_FUNC_START(handle_tlb_store) 191 - csrwr t0, EXCEPTION_KS0 192 - csrwr t1, EXCEPTION_KS1 193 - csrwr ra, EXCEPTION_KS2 210 + csrwr t0, EXCEPTION_KS0 211 + csrwr t1, EXCEPTION_KS1 212 + csrwr ra, EXCEPTION_KS2 194 213 195 214 /* 196 215 * The vmalloc handling is not in the hotpath. 197 216 */ 198 - csrrd t0, LOONGARCH_CSR_BADV 199 - bltz t0, vmalloc_store 200 - csrrd t1, LOONGARCH_CSR_PGDL 217 + csrrd t0, LOONGARCH_CSR_BADV 218 + bltz t0, vmalloc_store 219 + csrrd t1, LOONGARCH_CSR_PGDL 201 220 202 221 vmalloc_done_store: 203 222 /* Get PGD offset in bytes */ 204 - srli.d t0, t0, PGDIR_SHIFT 205 - andi t0, t0, (PTRS_PER_PGD - 1) 206 - slli.d t0, t0, 3 207 - add.d t1, t1, t0 208 - 223 + bstrpick.d ra, t0, PTRS_PER_PGD_BITS + PGDIR_SHIFT - 1, PGDIR_SHIFT 224 + alsl.d t1, ra, t1, 3 209 225 #if CONFIG_PGTABLE_LEVELS > 3 210 - csrrd t0, LOONGARCH_CSR_BADV 211 - ld.d t1, t1, 0 212 - srli.d t0, t0, PUD_SHIFT 213 - andi t0, t0, (PTRS_PER_PUD - 1) 214 - slli.d t0, t0, 3 215 - add.d t1, t1, t0 226 + ld.d t1, t1, 0 227 + bstrpick.d ra, t0, PTRS_PER_PUD_BITS + PUD_SHIFT - 1, PUD_SHIFT 228 + alsl.d t1, ra, t1, 3 216 229 #endif 217 230 #if CONFIG_PGTABLE_LEVELS > 2 218 - csrrd t0, LOONGARCH_CSR_BADV 219 - ld.d t1, t1, 0 220 - srli.d t0, t0, PMD_SHIFT 221 - andi t0, t0, (PTRS_PER_PMD - 1) 222 - slli.d t0, t0, 3 223 - add.d t1, t1, t0 231 + ld.d t1, t1, 0 232 + bstrpick.d ra, t0, PTRS_PER_PMD_BITS + PMD_SHIFT - 1, PMD_SHIFT 233 + alsl.d t1, ra, t1, 3 224 234 #endif 225 - ld.d ra, t1, 0 235 + ld.d ra, t1, 0 226 236 227 237 /* 228 238 * For huge tlb entries, pmde doesn't contain an address but 229 239 * instead contains the tlb pte. Check the PAGE_HUGE bit and 230 240 * see if we need to jump to huge tlb processing. 231 241 */ 232 - andi t0, ra, _PAGE_HUGE 233 - bnez t0, tlb_huge_update_store 242 + rotri.d ra, ra, _PAGE_HUGE_SHIFT + 1 243 + bltz ra, tlb_huge_update_store 234 244 235 - csrrd t0, LOONGARCH_CSR_BADV 236 - srli.d t0, t0, PAGE_SHIFT 237 - andi t0, t0, (PTRS_PER_PTE - 1) 238 - slli.d t0, t0, _PTE_T_LOG2 239 - add.d t1, ra, t0 245 + rotri.d ra, ra, 64 - (_PAGE_HUGE_SHIFT + 1) 246 + bstrpick.d t0, t0, PTRS_PER_PTE_BITS + PAGE_SHIFT - 1, PAGE_SHIFT 247 + alsl.d t1, t0, ra, _PTE_T_LOG2 240 248 241 249 #ifdef CONFIG_SMP 242 250 smp_pgtable_change_store: 243 - #endif 244 - #ifdef CONFIG_SMP 245 - ll.d t0, t1, 0 251 + ll.d t0, t1, 0 246 252 #else 247 - ld.d t0, t1, 0 253 + ld.d t0, t1, 0 254 + #endif 255 + andi ra, t0, _PAGE_PRESENT | _PAGE_WRITE 256 + xori ra, ra, _PAGE_PRESENT | _PAGE_WRITE 257 + bnez ra, nopage_tlb_store 258 + 259 + ori t0, t0, (_PAGE_VALID | _PAGE_DIRTY | _PAGE_MODIFIED) 260 + #ifdef CONFIG_SMP 261 + sc.d t0, t1, 0 262 + beqz t0, smp_pgtable_change_store 263 + #else 264 + st.d t0, t1, 0 248 265 #endif 249 266 tlbsrch 250 - 251 - srli.d ra, t0, _PAGE_PRESENT_SHIFT 252 - andi ra, ra, ((_PAGE_PRESENT | _PAGE_WRITE) >> _PAGE_PRESENT_SHIFT) 253 - xori ra, ra, ((_PAGE_PRESENT | _PAGE_WRITE) >> _PAGE_PRESENT_SHIFT) 254 - bnez ra, nopage_tlb_store 255 - 256 - ori t0, t0, (_PAGE_VALID | _PAGE_DIRTY | _PAGE_MODIFIED) 257 - #ifdef CONFIG_SMP 258 - sc.d t0, t1, 0 259 - beqz t0, smp_pgtable_change_store 260 - #else 261 - st.d t0, t1, 0 262 - #endif 263 - 264 - ori t1, t1, 8 265 - xori t1, t1, 8 266 - ld.d t0, t1, 0 267 - ld.d t1, t1, 8 268 - csrwr t0, LOONGARCH_CSR_TLBELO0 269 - csrwr t1, LOONGARCH_CSR_TLBELO1 267 + bstrins.d t1, zero, 3, 3 268 + ld.d t0, t1, 0 269 + ld.d t1, t1, 8 270 + csrwr t0, LOONGARCH_CSR_TLBELO0 271 + csrwr t1, LOONGARCH_CSR_TLBELO1 270 272 tlbwr 271 - leave_store: 272 - csrrd t0, EXCEPTION_KS0 273 - csrrd t1, EXCEPTION_KS1 274 - csrrd ra, EXCEPTION_KS2 273 + 274 + csrrd t0, EXCEPTION_KS0 275 + csrrd t1, EXCEPTION_KS1 276 + csrrd ra, EXCEPTION_KS2 275 277 ertn 278 + 276 279 #ifdef CONFIG_64BIT 277 280 vmalloc_store: 278 - la.abs t1, swapper_pg_dir 279 - b vmalloc_done_store 281 + la.abs t1, swapper_pg_dir 282 + b vmalloc_done_store 280 283 #endif 281 284 282 - /* 283 - * This is the entry point when build_tlbchange_handler_head 284 - * spots a huge page. 285 - */ 285 + /* This is the entry point of a huge page. */ 286 286 tlb_huge_update_store: 287 287 #ifdef CONFIG_SMP 288 - ll.d t0, t1, 0 289 - #else 290 - ld.d t0, t1, 0 288 + ll.d ra, t1, 0 291 289 #endif 292 - srli.d ra, t0, _PAGE_PRESENT_SHIFT 293 - andi ra, ra, ((_PAGE_PRESENT | _PAGE_WRITE) >> _PAGE_PRESENT_SHIFT) 294 - xori ra, ra, ((_PAGE_PRESENT | _PAGE_WRITE) >> _PAGE_PRESENT_SHIFT) 295 - bnez ra, nopage_tlb_store 296 - 297 - tlbsrch 298 - ori t0, t0, (_PAGE_VALID | _PAGE_DIRTY | _PAGE_MODIFIED) 290 + andi t0, ra, _PAGE_PRESENT | _PAGE_WRITE 291 + xori t0, t0, _PAGE_PRESENT | _PAGE_WRITE 292 + bnez t0, nopage_tlb_store 299 293 300 294 #ifdef CONFIG_SMP 301 - sc.d t0, t1, 0 302 - beqz t0, tlb_huge_update_store 303 - ld.d t0, t1, 0 295 + ori t0, ra, (_PAGE_VALID | _PAGE_DIRTY | _PAGE_MODIFIED) 296 + sc.d t0, t1, 0 297 + beqz t0, tlb_huge_update_store 298 + ori t0, ra, (_PAGE_VALID | _PAGE_DIRTY | _PAGE_MODIFIED) 304 299 #else 305 - st.d t0, t1, 0 300 + rotri.d ra, ra, 64 - (_PAGE_HUGE_SHIFT + 1) 301 + ori t0, ra, (_PAGE_VALID | _PAGE_DIRTY | _PAGE_MODIFIED) 302 + st.d t0, t1, 0 306 303 #endif 304 + tlbsrch 307 305 addu16i.d t1, zero, -(CSR_TLBIDX_EHINV >> 16) 308 306 addi.d ra, t1, 0 309 307 csrxchg ra, t1, LOONGARCH_CSR_TLBIDX 310 308 tlbwr 311 309 312 - csrxchg zero, t1, LOONGARCH_CSR_TLBIDX 310 + csrxchg zero, t1, LOONGARCH_CSR_TLBIDX 313 311 /* 314 312 * A huge PTE describes an area the size of the 315 313 * configured huge page size. This is twice the ··· 304 334 * address space. 305 335 */ 306 336 /* Huge page: Move Global bit */ 307 - xori t0, t0, _PAGE_HUGE 308 - lu12i.w t1, _PAGE_HGLOBAL >> 12 309 - and t1, t0, t1 310 - srli.d t1, t1, (_PAGE_HGLOBAL_SHIFT - _PAGE_GLOBAL_SHIFT) 311 - or t0, t0, t1 337 + xori t0, t0, _PAGE_HUGE 338 + lu12i.w t1, _PAGE_HGLOBAL >> 12 339 + and t1, t0, t1 340 + srli.d t1, t1, (_PAGE_HGLOBAL_SHIFT - _PAGE_GLOBAL_SHIFT) 341 + or t0, t0, t1 312 342 313 - addi.d ra, t0, 0 314 - csrwr t0, LOONGARCH_CSR_TLBELO0 315 - addi.d t0, ra, 0 343 + move ra, t0 344 + csrwr ra, LOONGARCH_CSR_TLBELO0 316 345 317 346 /* Convert to entrylo1 */ 318 - addi.d t1, zero, 1 319 - slli.d t1, t1, (HPAGE_SHIFT - 1) 320 - add.d t0, t0, t1 321 - csrwr t0, LOONGARCH_CSR_TLBELO1 347 + addi.d t1, zero, 1 348 + slli.d t1, t1, (HPAGE_SHIFT - 1) 349 + add.d t0, t0, t1 350 + csrwr t0, LOONGARCH_CSR_TLBELO1 322 351 323 352 /* Set huge page tlb entry size */ 324 353 addu16i.d t0, zero, (CSR_TLBIDX_PS >> 16) ··· 331 362 addu16i.d t1, zero, (PS_DEFAULT_SIZE << (CSR_TLBIDX_PS_SHIFT - 16)) 332 363 csrxchg t1, t0, LOONGARCH_CSR_TLBIDX 333 364 365 + csrrd t0, EXCEPTION_KS0 366 + csrrd t1, EXCEPTION_KS1 367 + csrrd ra, EXCEPTION_KS2 368 + ertn 369 + 334 370 nopage_tlb_store: 335 - dbar 0 336 - csrrd ra, EXCEPTION_KS2 337 - la.abs t0, tlb_do_page_fault_1 338 - jr t0 371 + dbar 0 372 + csrrd ra, EXCEPTION_KS2 373 + la.abs t0, tlb_do_page_fault_1 374 + jr t0 339 375 SYM_FUNC_END(handle_tlb_store) 340 376 341 377 SYM_FUNC_START(handle_tlb_modify) 342 - csrwr t0, EXCEPTION_KS0 343 - csrwr t1, EXCEPTION_KS1 344 - csrwr ra, EXCEPTION_KS2 378 + csrwr t0, EXCEPTION_KS0 379 + csrwr t1, EXCEPTION_KS1 380 + csrwr ra, EXCEPTION_KS2 345 381 346 382 /* 347 383 * The vmalloc handling is not in the hotpath. 348 384 */ 349 - csrrd t0, LOONGARCH_CSR_BADV 350 - bltz t0, vmalloc_modify 351 - csrrd t1, LOONGARCH_CSR_PGDL 385 + csrrd t0, LOONGARCH_CSR_BADV 386 + bltz t0, vmalloc_modify 387 + csrrd t1, LOONGARCH_CSR_PGDL 352 388 353 389 vmalloc_done_modify: 354 390 /* Get PGD offset in bytes */ 355 - srli.d t0, t0, PGDIR_SHIFT 356 - andi t0, t0, (PTRS_PER_PGD - 1) 357 - slli.d t0, t0, 3 358 - add.d t1, t1, t0 391 + bstrpick.d ra, t0, PTRS_PER_PGD_BITS + PGDIR_SHIFT - 1, PGDIR_SHIFT 392 + alsl.d t1, ra, t1, 3 359 393 #if CONFIG_PGTABLE_LEVELS > 3 360 - csrrd t0, LOONGARCH_CSR_BADV 361 - ld.d t1, t1, 0 362 - srli.d t0, t0, PUD_SHIFT 363 - andi t0, t0, (PTRS_PER_PUD - 1) 364 - slli.d t0, t0, 3 365 - add.d t1, t1, t0 394 + ld.d t1, t1, 0 395 + bstrpick.d ra, t0, PTRS_PER_PUD_BITS + PUD_SHIFT - 1, PUD_SHIFT 396 + alsl.d t1, ra, t1, 3 366 397 #endif 367 398 #if CONFIG_PGTABLE_LEVELS > 2 368 - csrrd t0, LOONGARCH_CSR_BADV 369 - ld.d t1, t1, 0 370 - srli.d t0, t0, PMD_SHIFT 371 - andi t0, t0, (PTRS_PER_PMD - 1) 372 - slli.d t0, t0, 3 373 - add.d t1, t1, t0 399 + ld.d t1, t1, 0 400 + bstrpick.d ra, t0, PTRS_PER_PMD_BITS + PMD_SHIFT - 1, PMD_SHIFT 401 + alsl.d t1, ra, t1, 3 374 402 #endif 375 - ld.d ra, t1, 0 403 + ld.d ra, t1, 0 376 404 377 405 /* 378 406 * For huge tlb entries, pmde doesn't contain an address but 379 407 * instead contains the tlb pte. Check the PAGE_HUGE bit and 380 408 * see if we need to jump to huge tlb processing. 381 409 */ 382 - andi t0, ra, _PAGE_HUGE 383 - bnez t0, tlb_huge_update_modify 410 + rotri.d ra, ra, _PAGE_HUGE_SHIFT + 1 411 + bltz ra, tlb_huge_update_modify 384 412 385 - csrrd t0, LOONGARCH_CSR_BADV 386 - srli.d t0, t0, PAGE_SHIFT 387 - andi t0, t0, (PTRS_PER_PTE - 1) 388 - slli.d t0, t0, _PTE_T_LOG2 389 - add.d t1, ra, t0 413 + rotri.d ra, ra, 64 - (_PAGE_HUGE_SHIFT + 1) 414 + bstrpick.d t0, t0, PTRS_PER_PTE_BITS + PAGE_SHIFT - 1, PAGE_SHIFT 415 + alsl.d t1, t0, ra, _PTE_T_LOG2 390 416 391 417 #ifdef CONFIG_SMP 392 418 smp_pgtable_change_modify: 393 - #endif 394 - #ifdef CONFIG_SMP 395 - ll.d t0, t1, 0 419 + ll.d t0, t1, 0 396 420 #else 397 - ld.d t0, t1, 0 421 + ld.d t0, t1, 0 422 + #endif 423 + andi ra, t0, _PAGE_WRITE 424 + beqz ra, nopage_tlb_modify 425 + 426 + ori t0, t0, (_PAGE_VALID | _PAGE_DIRTY | _PAGE_MODIFIED) 427 + #ifdef CONFIG_SMP 428 + sc.d t0, t1, 0 429 + beqz t0, smp_pgtable_change_modify 430 + #else 431 + st.d t0, t1, 0 398 432 #endif 399 433 tlbsrch 400 - 401 - srli.d ra, t0, _PAGE_WRITE_SHIFT 402 - andi ra, ra, 1 403 - beqz ra, nopage_tlb_modify 404 - 405 - ori t0, t0, (_PAGE_VALID | _PAGE_DIRTY | _PAGE_MODIFIED) 406 - #ifdef CONFIG_SMP 407 - sc.d t0, t1, 0 408 - beqz t0, smp_pgtable_change_modify 409 - #else 410 - st.d t0, t1, 0 411 - #endif 412 - ori t1, t1, 8 413 - xori t1, t1, 8 414 - ld.d t0, t1, 0 415 - ld.d t1, t1, 8 416 - csrwr t0, LOONGARCH_CSR_TLBELO0 417 - csrwr t1, LOONGARCH_CSR_TLBELO1 434 + bstrins.d t1, zero, 3, 3 435 + ld.d t0, t1, 0 436 + ld.d t1, t1, 8 437 + csrwr t0, LOONGARCH_CSR_TLBELO0 438 + csrwr t1, LOONGARCH_CSR_TLBELO1 418 439 tlbwr 419 - leave_modify: 420 - csrrd t0, EXCEPTION_KS0 421 - csrrd t1, EXCEPTION_KS1 422 - csrrd ra, EXCEPTION_KS2 440 + 441 + csrrd t0, EXCEPTION_KS0 442 + csrrd t1, EXCEPTION_KS1 443 + csrrd ra, EXCEPTION_KS2 423 444 ertn 445 + 424 446 #ifdef CONFIG_64BIT 425 447 vmalloc_modify: 426 - la.abs t1, swapper_pg_dir 427 - b vmalloc_done_modify 448 + la.abs t1, swapper_pg_dir 449 + b vmalloc_done_modify 428 450 #endif 429 451 430 - /* 431 - * This is the entry point when 432 - * build_tlbchange_handler_head spots a huge page. 433 - */ 452 + /* This is the entry point of a huge page. */ 434 453 tlb_huge_update_modify: 435 454 #ifdef CONFIG_SMP 436 - ll.d t0, t1, 0 437 - #else 438 - ld.d t0, t1, 0 455 + ll.d ra, t1, 0 439 456 #endif 440 - 441 - srli.d ra, t0, _PAGE_WRITE_SHIFT 442 - andi ra, ra, 1 443 - beqz ra, nopage_tlb_modify 444 - 445 - tlbsrch 446 - ori t0, t0, (_PAGE_VALID | _PAGE_DIRTY | _PAGE_MODIFIED) 457 + andi t0, ra, _PAGE_WRITE 458 + beqz t0, nopage_tlb_modify 447 459 448 460 #ifdef CONFIG_SMP 449 - sc.d t0, t1, 0 450 - beqz t0, tlb_huge_update_modify 451 - ld.d t0, t1, 0 461 + ori t0, ra, (_PAGE_VALID | _PAGE_DIRTY | _PAGE_MODIFIED) 462 + sc.d t0, t1, 0 463 + beqz t0, tlb_huge_update_modify 464 + ori t0, ra, (_PAGE_VALID | _PAGE_DIRTY | _PAGE_MODIFIED) 452 465 #else 453 - st.d t0, t1, 0 466 + rotri.d ra, ra, 64 - (_PAGE_HUGE_SHIFT + 1) 467 + ori t0, ra, (_PAGE_VALID | _PAGE_DIRTY | _PAGE_MODIFIED) 468 + st.d t0, t1, 0 454 469 #endif 455 470 /* 456 471 * A huge PTE describes an area the size of the ··· 446 493 * address space. 447 494 */ 448 495 /* Huge page: Move Global bit */ 449 - xori t0, t0, _PAGE_HUGE 450 - lu12i.w t1, _PAGE_HGLOBAL >> 12 451 - and t1, t0, t1 452 - srli.d t1, t1, (_PAGE_HGLOBAL_SHIFT - _PAGE_GLOBAL_SHIFT) 453 - or t0, t0, t1 496 + xori t0, t0, _PAGE_HUGE 497 + lu12i.w t1, _PAGE_HGLOBAL >> 12 498 + and t1, t0, t1 499 + srli.d t1, t1, (_PAGE_HGLOBAL_SHIFT - _PAGE_GLOBAL_SHIFT) 500 + or t0, t0, t1 454 501 455 - addi.d ra, t0, 0 456 - csrwr t0, LOONGARCH_CSR_TLBELO0 457 - addi.d t0, ra, 0 502 + move ra, t0 503 + csrwr ra, LOONGARCH_CSR_TLBELO0 458 504 459 505 /* Convert to entrylo1 */ 460 - addi.d t1, zero, 1 461 - slli.d t1, t1, (HPAGE_SHIFT - 1) 462 - add.d t0, t0, t1 463 - csrwr t0, LOONGARCH_CSR_TLBELO1 506 + addi.d t1, zero, 1 507 + slli.d t1, t1, (HPAGE_SHIFT - 1) 508 + add.d t0, t0, t1 509 + csrwr t0, LOONGARCH_CSR_TLBELO1 464 510 465 511 /* Set huge page tlb entry size */ 466 512 addu16i.d t0, zero, (CSR_TLBIDX_PS >> 16) ··· 473 521 addu16i.d t1, zero, (PS_DEFAULT_SIZE << (CSR_TLBIDX_PS_SHIFT - 16)) 474 522 csrxchg t1, t0, LOONGARCH_CSR_TLBIDX 475 523 524 + csrrd t0, EXCEPTION_KS0 525 + csrrd t1, EXCEPTION_KS1 526 + csrrd ra, EXCEPTION_KS2 527 + ertn 528 + 476 529 nopage_tlb_modify: 477 - dbar 0 478 - csrrd ra, EXCEPTION_KS2 479 - la.abs t0, tlb_do_page_fault_1 480 - jr t0 530 + dbar 0 531 + csrrd ra, EXCEPTION_KS2 532 + la.abs t0, tlb_do_page_fault_1 533 + jr t0 481 534 SYM_FUNC_END(handle_tlb_modify) 482 535 483 536 SYM_FUNC_START(handle_tlb_refill) 484 - csrwr t0, LOONGARCH_CSR_TLBRSAVE 485 - csrrd t0, LOONGARCH_CSR_PGD 486 - lddir t0, t0, 3 537 + csrwr t0, LOONGARCH_CSR_TLBRSAVE 538 + csrrd t0, LOONGARCH_CSR_PGD 539 + lddir t0, t0, 3 487 540 #if CONFIG_PGTABLE_LEVELS > 3 488 - lddir t0, t0, 2 541 + lddir t0, t0, 2 489 542 #endif 490 543 #if CONFIG_PGTABLE_LEVELS > 2 491 - lddir t0, t0, 1 544 + lddir t0, t0, 1 492 545 #endif 493 - ldpte t0, 0 494 - ldpte t0, 1 546 + ldpte t0, 0 547 + ldpte t0, 1 495 548 tlbfill 496 - csrrd t0, LOONGARCH_CSR_TLBRSAVE 549 + csrrd t0, LOONGARCH_CSR_TLBRSAVE 497 550 ertn 498 551 SYM_FUNC_END(handle_tlb_refill)
+7
arch/loongarch/net/Makefile
··· 1 + # SPDX-License-Identifier: GPL-2.0-only 2 + # 3 + # Makefile for arch/loongarch/net 4 + # 5 + # Copyright (C) 2022 Loongson Technology Corporation Limited 6 + # 7 + obj-$(CONFIG_BPF_JIT) += bpf_jit.o
+1179
arch/loongarch/net/bpf_jit.c
··· 1 + // SPDX-License-Identifier: GPL-2.0-only 2 + /* 3 + * BPF JIT compiler for LoongArch 4 + * 5 + * Copyright (C) 2022 Loongson Technology Corporation Limited 6 + */ 7 + #include "bpf_jit.h" 8 + 9 + #define REG_TCC LOONGARCH_GPR_A6 10 + #define TCC_SAVED LOONGARCH_GPR_S5 11 + 12 + #define SAVE_RA BIT(0) 13 + #define SAVE_TCC BIT(1) 14 + 15 + static const int regmap[] = { 16 + /* return value from in-kernel function, and exit value for eBPF program */ 17 + [BPF_REG_0] = LOONGARCH_GPR_A5, 18 + /* arguments from eBPF program to in-kernel function */ 19 + [BPF_REG_1] = LOONGARCH_GPR_A0, 20 + [BPF_REG_2] = LOONGARCH_GPR_A1, 21 + [BPF_REG_3] = LOONGARCH_GPR_A2, 22 + [BPF_REG_4] = LOONGARCH_GPR_A3, 23 + [BPF_REG_5] = LOONGARCH_GPR_A4, 24 + /* callee saved registers that in-kernel function will preserve */ 25 + [BPF_REG_6] = LOONGARCH_GPR_S0, 26 + [BPF_REG_7] = LOONGARCH_GPR_S1, 27 + [BPF_REG_8] = LOONGARCH_GPR_S2, 28 + [BPF_REG_9] = LOONGARCH_GPR_S3, 29 + /* read-only frame pointer to access stack */ 30 + [BPF_REG_FP] = LOONGARCH_GPR_S4, 31 + /* temporary register for blinding constants */ 32 + [BPF_REG_AX] = LOONGARCH_GPR_T0, 33 + }; 34 + 35 + static void mark_call(struct jit_ctx *ctx) 36 + { 37 + ctx->flags |= SAVE_RA; 38 + } 39 + 40 + static void mark_tail_call(struct jit_ctx *ctx) 41 + { 42 + ctx->flags |= SAVE_TCC; 43 + } 44 + 45 + static bool seen_call(struct jit_ctx *ctx) 46 + { 47 + return (ctx->flags & SAVE_RA); 48 + } 49 + 50 + static bool seen_tail_call(struct jit_ctx *ctx) 51 + { 52 + return (ctx->flags & SAVE_TCC); 53 + } 54 + 55 + static u8 tail_call_reg(struct jit_ctx *ctx) 56 + { 57 + if (seen_call(ctx)) 58 + return TCC_SAVED; 59 + 60 + return REG_TCC; 61 + } 62 + 63 + /* 64 + * eBPF prog stack layout: 65 + * 66 + * high 67 + * original $sp ------------> +-------------------------+ <--LOONGARCH_GPR_FP 68 + * | $ra | 69 + * +-------------------------+ 70 + * | $fp | 71 + * +-------------------------+ 72 + * | $s0 | 73 + * +-------------------------+ 74 + * | $s1 | 75 + * +-------------------------+ 76 + * | $s2 | 77 + * +-------------------------+ 78 + * | $s3 | 79 + * +-------------------------+ 80 + * | $s4 | 81 + * +-------------------------+ 82 + * | $s5 | 83 + * +-------------------------+ <--BPF_REG_FP 84 + * | prog->aux->stack_depth | 85 + * | (optional) | 86 + * current $sp -------------> +-------------------------+ 87 + * low 88 + */ 89 + static void build_prologue(struct jit_ctx *ctx) 90 + { 91 + int stack_adjust = 0, store_offset, bpf_stack_adjust; 92 + 93 + bpf_stack_adjust = round_up(ctx->prog->aux->stack_depth, 16); 94 + 95 + /* To store ra, fp, s0, s1, s2, s3, s4 and s5. */ 96 + stack_adjust += sizeof(long) * 8; 97 + 98 + stack_adjust = round_up(stack_adjust, 16); 99 + stack_adjust += bpf_stack_adjust; 100 + 101 + /* 102 + * First instruction initializes the tail call count (TCC). 103 + * On tail call we skip this instruction, and the TCC is 104 + * passed in REG_TCC from the caller. 105 + */ 106 + emit_insn(ctx, addid, REG_TCC, LOONGARCH_GPR_ZERO, MAX_TAIL_CALL_CNT); 107 + 108 + emit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, -stack_adjust); 109 + 110 + store_offset = stack_adjust - sizeof(long); 111 + emit_insn(ctx, std, LOONGARCH_GPR_RA, LOONGARCH_GPR_SP, store_offset); 112 + 113 + store_offset -= sizeof(long); 114 + emit_insn(ctx, std, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, store_offset); 115 + 116 + store_offset -= sizeof(long); 117 + emit_insn(ctx, std, LOONGARCH_GPR_S0, LOONGARCH_GPR_SP, store_offset); 118 + 119 + store_offset -= sizeof(long); 120 + emit_insn(ctx, std, LOONGARCH_GPR_S1, LOONGARCH_GPR_SP, store_offset); 121 + 122 + store_offset -= sizeof(long); 123 + emit_insn(ctx, std, LOONGARCH_GPR_S2, LOONGARCH_GPR_SP, store_offset); 124 + 125 + store_offset -= sizeof(long); 126 + emit_insn(ctx, std, LOONGARCH_GPR_S3, LOONGARCH_GPR_SP, store_offset); 127 + 128 + store_offset -= sizeof(long); 129 + emit_insn(ctx, std, LOONGARCH_GPR_S4, LOONGARCH_GPR_SP, store_offset); 130 + 131 + store_offset -= sizeof(long); 132 + emit_insn(ctx, std, LOONGARCH_GPR_S5, LOONGARCH_GPR_SP, store_offset); 133 + 134 + emit_insn(ctx, addid, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, stack_adjust); 135 + 136 + if (bpf_stack_adjust) 137 + emit_insn(ctx, addid, regmap[BPF_REG_FP], LOONGARCH_GPR_SP, bpf_stack_adjust); 138 + 139 + /* 140 + * Program contains calls and tail calls, so REG_TCC need 141 + * to be saved across calls. 142 + */ 143 + if (seen_tail_call(ctx) && seen_call(ctx)) 144 + move_reg(ctx, TCC_SAVED, REG_TCC); 145 + 146 + ctx->stack_size = stack_adjust; 147 + } 148 + 149 + static void __build_epilogue(struct jit_ctx *ctx, bool is_tail_call) 150 + { 151 + int stack_adjust = ctx->stack_size; 152 + int load_offset; 153 + 154 + load_offset = stack_adjust - sizeof(long); 155 + emit_insn(ctx, ldd, LOONGARCH_GPR_RA, LOONGARCH_GPR_SP, load_offset); 156 + 157 + load_offset -= sizeof(long); 158 + emit_insn(ctx, ldd, LOONGARCH_GPR_FP, LOONGARCH_GPR_SP, load_offset); 159 + 160 + load_offset -= sizeof(long); 161 + emit_insn(ctx, ldd, LOONGARCH_GPR_S0, LOONGARCH_GPR_SP, load_offset); 162 + 163 + load_offset -= sizeof(long); 164 + emit_insn(ctx, ldd, LOONGARCH_GPR_S1, LOONGARCH_GPR_SP, load_offset); 165 + 166 + load_offset -= sizeof(long); 167 + emit_insn(ctx, ldd, LOONGARCH_GPR_S2, LOONGARCH_GPR_SP, load_offset); 168 + 169 + load_offset -= sizeof(long); 170 + emit_insn(ctx, ldd, LOONGARCH_GPR_S3, LOONGARCH_GPR_SP, load_offset); 171 + 172 + load_offset -= sizeof(long); 173 + emit_insn(ctx, ldd, LOONGARCH_GPR_S4, LOONGARCH_GPR_SP, load_offset); 174 + 175 + load_offset -= sizeof(long); 176 + emit_insn(ctx, ldd, LOONGARCH_GPR_S5, LOONGARCH_GPR_SP, load_offset); 177 + 178 + emit_insn(ctx, addid, LOONGARCH_GPR_SP, LOONGARCH_GPR_SP, stack_adjust); 179 + 180 + if (!is_tail_call) { 181 + /* Set return value */ 182 + move_reg(ctx, LOONGARCH_GPR_A0, regmap[BPF_REG_0]); 183 + /* Return to the caller */ 184 + emit_insn(ctx, jirl, LOONGARCH_GPR_RA, LOONGARCH_GPR_ZERO, 0); 185 + } else { 186 + /* 187 + * Call the next bpf prog and skip the first instruction 188 + * of TCC initialization. 189 + */ 190 + emit_insn(ctx, jirl, LOONGARCH_GPR_T3, LOONGARCH_GPR_ZERO, 1); 191 + } 192 + } 193 + 194 + static void build_epilogue(struct jit_ctx *ctx) 195 + { 196 + __build_epilogue(ctx, false); 197 + } 198 + 199 + bool bpf_jit_supports_kfunc_call(void) 200 + { 201 + return true; 202 + } 203 + 204 + /* initialized on the first pass of build_body() */ 205 + static int out_offset = -1; 206 + static int emit_bpf_tail_call(struct jit_ctx *ctx) 207 + { 208 + int off; 209 + u8 tcc = tail_call_reg(ctx); 210 + u8 a1 = LOONGARCH_GPR_A1; 211 + u8 a2 = LOONGARCH_GPR_A2; 212 + u8 t1 = LOONGARCH_GPR_T1; 213 + u8 t2 = LOONGARCH_GPR_T2; 214 + u8 t3 = LOONGARCH_GPR_T3; 215 + const int idx0 = ctx->idx; 216 + 217 + #define cur_offset (ctx->idx - idx0) 218 + #define jmp_offset (out_offset - (cur_offset)) 219 + 220 + /* 221 + * a0: &ctx 222 + * a1: &array 223 + * a2: index 224 + * 225 + * if (index >= array->map.max_entries) 226 + * goto out; 227 + */ 228 + off = offsetof(struct bpf_array, map.max_entries); 229 + emit_insn(ctx, ldwu, t1, a1, off); 230 + /* bgeu $a2, $t1, jmp_offset */ 231 + if (emit_tailcall_jmp(ctx, BPF_JGE, a2, t1, jmp_offset) < 0) 232 + goto toofar; 233 + 234 + /* 235 + * if (--TCC < 0) 236 + * goto out; 237 + */ 238 + emit_insn(ctx, addid, REG_TCC, tcc, -1); 239 + if (emit_tailcall_jmp(ctx, BPF_JSLT, REG_TCC, LOONGARCH_GPR_ZERO, jmp_offset) < 0) 240 + goto toofar; 241 + 242 + /* 243 + * prog = array->ptrs[index]; 244 + * if (!prog) 245 + * goto out; 246 + */ 247 + emit_insn(ctx, alsld, t2, a2, a1, 2); 248 + off = offsetof(struct bpf_array, ptrs); 249 + emit_insn(ctx, ldd, t2, t2, off); 250 + /* beq $t2, $zero, jmp_offset */ 251 + if (emit_tailcall_jmp(ctx, BPF_JEQ, t2, LOONGARCH_GPR_ZERO, jmp_offset) < 0) 252 + goto toofar; 253 + 254 + /* goto *(prog->bpf_func + 4); */ 255 + off = offsetof(struct bpf_prog, bpf_func); 256 + emit_insn(ctx, ldd, t3, t2, off); 257 + __build_epilogue(ctx, true); 258 + 259 + /* out: */ 260 + if (out_offset == -1) 261 + out_offset = cur_offset; 262 + if (cur_offset != out_offset) { 263 + pr_err_once("tail_call out_offset = %d, expected %d!\n", 264 + cur_offset, out_offset); 265 + return -1; 266 + } 267 + 268 + return 0; 269 + 270 + toofar: 271 + pr_info_once("tail_call: jump too far\n"); 272 + return -1; 273 + #undef cur_offset 274 + #undef jmp_offset 275 + } 276 + 277 + static void emit_atomic(const struct bpf_insn *insn, struct jit_ctx *ctx) 278 + { 279 + const u8 t1 = LOONGARCH_GPR_T1; 280 + const u8 t2 = LOONGARCH_GPR_T2; 281 + const u8 t3 = LOONGARCH_GPR_T3; 282 + const u8 src = regmap[insn->src_reg]; 283 + const u8 dst = regmap[insn->dst_reg]; 284 + const s16 off = insn->off; 285 + const s32 imm = insn->imm; 286 + const bool isdw = BPF_SIZE(insn->code) == BPF_DW; 287 + 288 + move_imm(ctx, t1, off, false); 289 + emit_insn(ctx, addd, t1, dst, t1); 290 + move_reg(ctx, t3, src); 291 + 292 + switch (imm) { 293 + /* lock *(size *)(dst + off) <op>= src */ 294 + case BPF_ADD: 295 + if (isdw) 296 + emit_insn(ctx, amaddd, t2, t1, src); 297 + else 298 + emit_insn(ctx, amaddw, t2, t1, src); 299 + break; 300 + case BPF_AND: 301 + if (isdw) 302 + emit_insn(ctx, amandd, t2, t1, src); 303 + else 304 + emit_insn(ctx, amandw, t2, t1, src); 305 + break; 306 + case BPF_OR: 307 + if (isdw) 308 + emit_insn(ctx, amord, t2, t1, src); 309 + else 310 + emit_insn(ctx, amorw, t2, t1, src); 311 + break; 312 + case BPF_XOR: 313 + if (isdw) 314 + emit_insn(ctx, amxord, t2, t1, src); 315 + else 316 + emit_insn(ctx, amxorw, t2, t1, src); 317 + break; 318 + /* src = atomic_fetch_<op>(dst + off, src) */ 319 + case BPF_ADD | BPF_FETCH: 320 + if (isdw) { 321 + emit_insn(ctx, amaddd, src, t1, t3); 322 + } else { 323 + emit_insn(ctx, amaddw, src, t1, t3); 324 + emit_zext_32(ctx, src, true); 325 + } 326 + break; 327 + case BPF_AND | BPF_FETCH: 328 + if (isdw) { 329 + emit_insn(ctx, amandd, src, t1, t3); 330 + } else { 331 + emit_insn(ctx, amandw, src, t1, t3); 332 + emit_zext_32(ctx, src, true); 333 + } 334 + break; 335 + case BPF_OR | BPF_FETCH: 336 + if (isdw) { 337 + emit_insn(ctx, amord, src, t1, t3); 338 + } else { 339 + emit_insn(ctx, amorw, src, t1, t3); 340 + emit_zext_32(ctx, src, true); 341 + } 342 + break; 343 + case BPF_XOR | BPF_FETCH: 344 + if (isdw) { 345 + emit_insn(ctx, amxord, src, t1, t3); 346 + } else { 347 + emit_insn(ctx, amxorw, src, t1, t3); 348 + emit_zext_32(ctx, src, true); 349 + } 350 + break; 351 + /* src = atomic_xchg(dst + off, src); */ 352 + case BPF_XCHG: 353 + if (isdw) { 354 + emit_insn(ctx, amswapd, src, t1, t3); 355 + } else { 356 + emit_insn(ctx, amswapw, src, t1, t3); 357 + emit_zext_32(ctx, src, true); 358 + } 359 + break; 360 + /* r0 = atomic_cmpxchg(dst + off, r0, src); */ 361 + case BPF_CMPXCHG: 362 + u8 r0 = regmap[BPF_REG_0]; 363 + 364 + move_reg(ctx, t2, r0); 365 + if (isdw) { 366 + emit_insn(ctx, lld, r0, t1, 0); 367 + emit_insn(ctx, bne, t2, r0, 4); 368 + move_reg(ctx, t3, src); 369 + emit_insn(ctx, scd, t3, t1, 0); 370 + emit_insn(ctx, beq, t3, LOONGARCH_GPR_ZERO, -4); 371 + } else { 372 + emit_insn(ctx, llw, r0, t1, 0); 373 + emit_zext_32(ctx, t2, true); 374 + emit_zext_32(ctx, r0, true); 375 + emit_insn(ctx, bne, t2, r0, 4); 376 + move_reg(ctx, t3, src); 377 + emit_insn(ctx, scw, t3, t1, 0); 378 + emit_insn(ctx, beq, t3, LOONGARCH_GPR_ZERO, -6); 379 + emit_zext_32(ctx, r0, true); 380 + } 381 + break; 382 + } 383 + } 384 + 385 + static bool is_signed_bpf_cond(u8 cond) 386 + { 387 + return cond == BPF_JSGT || cond == BPF_JSLT || 388 + cond == BPF_JSGE || cond == BPF_JSLE; 389 + } 390 + 391 + static int build_insn(const struct bpf_insn *insn, struct jit_ctx *ctx, bool extra_pass) 392 + { 393 + const bool is32 = BPF_CLASS(insn->code) == BPF_ALU || 394 + BPF_CLASS(insn->code) == BPF_JMP32; 395 + const u8 code = insn->code; 396 + const u8 cond = BPF_OP(code); 397 + const u8 t1 = LOONGARCH_GPR_T1; 398 + const u8 t2 = LOONGARCH_GPR_T2; 399 + const u8 src = regmap[insn->src_reg]; 400 + const u8 dst = regmap[insn->dst_reg]; 401 + const s16 off = insn->off; 402 + const s32 imm = insn->imm; 403 + int jmp_offset; 404 + int i = insn - ctx->prog->insnsi; 405 + 406 + switch (code) { 407 + /* dst = src */ 408 + case BPF_ALU | BPF_MOV | BPF_X: 409 + case BPF_ALU64 | BPF_MOV | BPF_X: 410 + move_reg(ctx, dst, src); 411 + emit_zext_32(ctx, dst, is32); 412 + break; 413 + 414 + /* dst = imm */ 415 + case BPF_ALU | BPF_MOV | BPF_K: 416 + case BPF_ALU64 | BPF_MOV | BPF_K: 417 + move_imm(ctx, dst, imm, is32); 418 + break; 419 + 420 + /* dst = dst + src */ 421 + case BPF_ALU | BPF_ADD | BPF_X: 422 + case BPF_ALU64 | BPF_ADD | BPF_X: 423 + emit_insn(ctx, addd, dst, dst, src); 424 + emit_zext_32(ctx, dst, is32); 425 + break; 426 + 427 + /* dst = dst + imm */ 428 + case BPF_ALU | BPF_ADD | BPF_K: 429 + case BPF_ALU64 | BPF_ADD | BPF_K: 430 + if (is_signed_imm12(imm)) { 431 + emit_insn(ctx, addid, dst, dst, imm); 432 + } else { 433 + move_imm(ctx, t1, imm, is32); 434 + emit_insn(ctx, addd, dst, dst, t1); 435 + } 436 + emit_zext_32(ctx, dst, is32); 437 + break; 438 + 439 + /* dst = dst - src */ 440 + case BPF_ALU | BPF_SUB | BPF_X: 441 + case BPF_ALU64 | BPF_SUB | BPF_X: 442 + emit_insn(ctx, subd, dst, dst, src); 443 + emit_zext_32(ctx, dst, is32); 444 + break; 445 + 446 + /* dst = dst - imm */ 447 + case BPF_ALU | BPF_SUB | BPF_K: 448 + case BPF_ALU64 | BPF_SUB | BPF_K: 449 + if (is_signed_imm12(-imm)) { 450 + emit_insn(ctx, addid, dst, dst, -imm); 451 + } else { 452 + move_imm(ctx, t1, imm, is32); 453 + emit_insn(ctx, subd, dst, dst, t1); 454 + } 455 + emit_zext_32(ctx, dst, is32); 456 + break; 457 + 458 + /* dst = dst * src */ 459 + case BPF_ALU | BPF_MUL | BPF_X: 460 + case BPF_ALU64 | BPF_MUL | BPF_X: 461 + emit_insn(ctx, muld, dst, dst, src); 462 + emit_zext_32(ctx, dst, is32); 463 + break; 464 + 465 + /* dst = dst * imm */ 466 + case BPF_ALU | BPF_MUL | BPF_K: 467 + case BPF_ALU64 | BPF_MUL | BPF_K: 468 + move_imm(ctx, t1, imm, is32); 469 + emit_insn(ctx, muld, dst, dst, t1); 470 + emit_zext_32(ctx, dst, is32); 471 + break; 472 + 473 + /* dst = dst / src */ 474 + case BPF_ALU | BPF_DIV | BPF_X: 475 + case BPF_ALU64 | BPF_DIV | BPF_X: 476 + emit_zext_32(ctx, dst, is32); 477 + move_reg(ctx, t1, src); 478 + emit_zext_32(ctx, t1, is32); 479 + emit_insn(ctx, divdu, dst, dst, t1); 480 + emit_zext_32(ctx, dst, is32); 481 + break; 482 + 483 + /* dst = dst / imm */ 484 + case BPF_ALU | BPF_DIV | BPF_K: 485 + case BPF_ALU64 | BPF_DIV | BPF_K: 486 + move_imm(ctx, t1, imm, is32); 487 + emit_zext_32(ctx, dst, is32); 488 + emit_insn(ctx, divdu, dst, dst, t1); 489 + emit_zext_32(ctx, dst, is32); 490 + break; 491 + 492 + /* dst = dst % src */ 493 + case BPF_ALU | BPF_MOD | BPF_X: 494 + case BPF_ALU64 | BPF_MOD | BPF_X: 495 + emit_zext_32(ctx, dst, is32); 496 + move_reg(ctx, t1, src); 497 + emit_zext_32(ctx, t1, is32); 498 + emit_insn(ctx, moddu, dst, dst, t1); 499 + emit_zext_32(ctx, dst, is32); 500 + break; 501 + 502 + /* dst = dst % imm */ 503 + case BPF_ALU | BPF_MOD | BPF_K: 504 + case BPF_ALU64 | BPF_MOD | BPF_K: 505 + move_imm(ctx, t1, imm, is32); 506 + emit_zext_32(ctx, dst, is32); 507 + emit_insn(ctx, moddu, dst, dst, t1); 508 + emit_zext_32(ctx, dst, is32); 509 + break; 510 + 511 + /* dst = -dst */ 512 + case BPF_ALU | BPF_NEG: 513 + case BPF_ALU64 | BPF_NEG: 514 + move_imm(ctx, t1, imm, is32); 515 + emit_insn(ctx, subd, dst, LOONGARCH_GPR_ZERO, dst); 516 + emit_zext_32(ctx, dst, is32); 517 + break; 518 + 519 + /* dst = dst & src */ 520 + case BPF_ALU | BPF_AND | BPF_X: 521 + case BPF_ALU64 | BPF_AND | BPF_X: 522 + emit_insn(ctx, and, dst, dst, src); 523 + emit_zext_32(ctx, dst, is32); 524 + break; 525 + 526 + /* dst = dst & imm */ 527 + case BPF_ALU | BPF_AND | BPF_K: 528 + case BPF_ALU64 | BPF_AND | BPF_K: 529 + if (is_unsigned_imm12(imm)) { 530 + emit_insn(ctx, andi, dst, dst, imm); 531 + } else { 532 + move_imm(ctx, t1, imm, is32); 533 + emit_insn(ctx, and, dst, dst, t1); 534 + } 535 + emit_zext_32(ctx, dst, is32); 536 + break; 537 + 538 + /* dst = dst | src */ 539 + case BPF_ALU | BPF_OR | BPF_X: 540 + case BPF_ALU64 | BPF_OR | BPF_X: 541 + emit_insn(ctx, or, dst, dst, src); 542 + emit_zext_32(ctx, dst, is32); 543 + break; 544 + 545 + /* dst = dst | imm */ 546 + case BPF_ALU | BPF_OR | BPF_K: 547 + case BPF_ALU64 | BPF_OR | BPF_K: 548 + if (is_unsigned_imm12(imm)) { 549 + emit_insn(ctx, ori, dst, dst, imm); 550 + } else { 551 + move_imm(ctx, t1, imm, is32); 552 + emit_insn(ctx, or, dst, dst, t1); 553 + } 554 + emit_zext_32(ctx, dst, is32); 555 + break; 556 + 557 + /* dst = dst ^ src */ 558 + case BPF_ALU | BPF_XOR | BPF_X: 559 + case BPF_ALU64 | BPF_XOR | BPF_X: 560 + emit_insn(ctx, xor, dst, dst, src); 561 + emit_zext_32(ctx, dst, is32); 562 + break; 563 + 564 + /* dst = dst ^ imm */ 565 + case BPF_ALU | BPF_XOR | BPF_K: 566 + case BPF_ALU64 | BPF_XOR | BPF_K: 567 + if (is_unsigned_imm12(imm)) { 568 + emit_insn(ctx, xori, dst, dst, imm); 569 + } else { 570 + move_imm(ctx, t1, imm, is32); 571 + emit_insn(ctx, xor, dst, dst, t1); 572 + } 573 + emit_zext_32(ctx, dst, is32); 574 + break; 575 + 576 + /* dst = dst << src (logical) */ 577 + case BPF_ALU | BPF_LSH | BPF_X: 578 + emit_insn(ctx, sllw, dst, dst, src); 579 + emit_zext_32(ctx, dst, is32); 580 + break; 581 + 582 + case BPF_ALU64 | BPF_LSH | BPF_X: 583 + emit_insn(ctx, slld, dst, dst, src); 584 + break; 585 + 586 + /* dst = dst << imm (logical) */ 587 + case BPF_ALU | BPF_LSH | BPF_K: 588 + emit_insn(ctx, slliw, dst, dst, imm); 589 + emit_zext_32(ctx, dst, is32); 590 + break; 591 + 592 + case BPF_ALU64 | BPF_LSH | BPF_K: 593 + emit_insn(ctx, sllid, dst, dst, imm); 594 + break; 595 + 596 + /* dst = dst >> src (logical) */ 597 + case BPF_ALU | BPF_RSH | BPF_X: 598 + emit_insn(ctx, srlw, dst, dst, src); 599 + emit_zext_32(ctx, dst, is32); 600 + break; 601 + 602 + case BPF_ALU64 | BPF_RSH | BPF_X: 603 + emit_insn(ctx, srld, dst, dst, src); 604 + break; 605 + 606 + /* dst = dst >> imm (logical) */ 607 + case BPF_ALU | BPF_RSH | BPF_K: 608 + emit_insn(ctx, srliw, dst, dst, imm); 609 + emit_zext_32(ctx, dst, is32); 610 + break; 611 + 612 + case BPF_ALU64 | BPF_RSH | BPF_K: 613 + emit_insn(ctx, srlid, dst, dst, imm); 614 + break; 615 + 616 + /* dst = dst >> src (arithmetic) */ 617 + case BPF_ALU | BPF_ARSH | BPF_X: 618 + emit_insn(ctx, sraw, dst, dst, src); 619 + emit_zext_32(ctx, dst, is32); 620 + break; 621 + 622 + case BPF_ALU64 | BPF_ARSH | BPF_X: 623 + emit_insn(ctx, srad, dst, dst, src); 624 + break; 625 + 626 + /* dst = dst >> imm (arithmetic) */ 627 + case BPF_ALU | BPF_ARSH | BPF_K: 628 + emit_insn(ctx, sraiw, dst, dst, imm); 629 + emit_zext_32(ctx, dst, is32); 630 + break; 631 + 632 + case BPF_ALU64 | BPF_ARSH | BPF_K: 633 + emit_insn(ctx, sraid, dst, dst, imm); 634 + break; 635 + 636 + /* dst = BSWAP##imm(dst) */ 637 + case BPF_ALU | BPF_END | BPF_FROM_LE: 638 + switch (imm) { 639 + case 16: 640 + /* zero-extend 16 bits into 64 bits */ 641 + emit_insn(ctx, bstrpickd, dst, dst, 15, 0); 642 + break; 643 + case 32: 644 + /* zero-extend 32 bits into 64 bits */ 645 + emit_zext_32(ctx, dst, is32); 646 + break; 647 + case 64: 648 + /* do nothing */ 649 + break; 650 + } 651 + break; 652 + 653 + case BPF_ALU | BPF_END | BPF_FROM_BE: 654 + switch (imm) { 655 + case 16: 656 + emit_insn(ctx, revb2h, dst, dst); 657 + /* zero-extend 16 bits into 64 bits */ 658 + emit_insn(ctx, bstrpickd, dst, dst, 15, 0); 659 + break; 660 + case 32: 661 + emit_insn(ctx, revb2w, dst, dst); 662 + /* zero-extend 32 bits into 64 bits */ 663 + emit_zext_32(ctx, dst, is32); 664 + break; 665 + case 64: 666 + emit_insn(ctx, revbd, dst, dst); 667 + break; 668 + } 669 + break; 670 + 671 + /* PC += off if dst cond src */ 672 + case BPF_JMP | BPF_JEQ | BPF_X: 673 + case BPF_JMP | BPF_JNE | BPF_X: 674 + case BPF_JMP | BPF_JGT | BPF_X: 675 + case BPF_JMP | BPF_JGE | BPF_X: 676 + case BPF_JMP | BPF_JLT | BPF_X: 677 + case BPF_JMP | BPF_JLE | BPF_X: 678 + case BPF_JMP | BPF_JSGT | BPF_X: 679 + case BPF_JMP | BPF_JSGE | BPF_X: 680 + case BPF_JMP | BPF_JSLT | BPF_X: 681 + case BPF_JMP | BPF_JSLE | BPF_X: 682 + case BPF_JMP32 | BPF_JEQ | BPF_X: 683 + case BPF_JMP32 | BPF_JNE | BPF_X: 684 + case BPF_JMP32 | BPF_JGT | BPF_X: 685 + case BPF_JMP32 | BPF_JGE | BPF_X: 686 + case BPF_JMP32 | BPF_JLT | BPF_X: 687 + case BPF_JMP32 | BPF_JLE | BPF_X: 688 + case BPF_JMP32 | BPF_JSGT | BPF_X: 689 + case BPF_JMP32 | BPF_JSGE | BPF_X: 690 + case BPF_JMP32 | BPF_JSLT | BPF_X: 691 + case BPF_JMP32 | BPF_JSLE | BPF_X: 692 + jmp_offset = bpf2la_offset(i, off, ctx); 693 + move_reg(ctx, t1, dst); 694 + move_reg(ctx, t2, src); 695 + if (is_signed_bpf_cond(BPF_OP(code))) { 696 + emit_sext_32(ctx, t1, is32); 697 + emit_sext_32(ctx, t2, is32); 698 + } else { 699 + emit_zext_32(ctx, t1, is32); 700 + emit_zext_32(ctx, t2, is32); 701 + } 702 + if (emit_cond_jmp(ctx, cond, t1, t2, jmp_offset) < 0) 703 + goto toofar; 704 + break; 705 + 706 + /* PC += off if dst cond imm */ 707 + case BPF_JMP | BPF_JEQ | BPF_K: 708 + case BPF_JMP | BPF_JNE | BPF_K: 709 + case BPF_JMP | BPF_JGT | BPF_K: 710 + case BPF_JMP | BPF_JGE | BPF_K: 711 + case BPF_JMP | BPF_JLT | BPF_K: 712 + case BPF_JMP | BPF_JLE | BPF_K: 713 + case BPF_JMP | BPF_JSGT | BPF_K: 714 + case BPF_JMP | BPF_JSGE | BPF_K: 715 + case BPF_JMP | BPF_JSLT | BPF_K: 716 + case BPF_JMP | BPF_JSLE | BPF_K: 717 + case BPF_JMP32 | BPF_JEQ | BPF_K: 718 + case BPF_JMP32 | BPF_JNE | BPF_K: 719 + case BPF_JMP32 | BPF_JGT | BPF_K: 720 + case BPF_JMP32 | BPF_JGE | BPF_K: 721 + case BPF_JMP32 | BPF_JLT | BPF_K: 722 + case BPF_JMP32 | BPF_JLE | BPF_K: 723 + case BPF_JMP32 | BPF_JSGT | BPF_K: 724 + case BPF_JMP32 | BPF_JSGE | BPF_K: 725 + case BPF_JMP32 | BPF_JSLT | BPF_K: 726 + case BPF_JMP32 | BPF_JSLE | BPF_K: 727 + u8 t7 = -1; 728 + jmp_offset = bpf2la_offset(i, off, ctx); 729 + if (imm) { 730 + move_imm(ctx, t1, imm, false); 731 + t7 = t1; 732 + } else { 733 + /* If imm is 0, simply use zero register. */ 734 + t7 = LOONGARCH_GPR_ZERO; 735 + } 736 + move_reg(ctx, t2, dst); 737 + if (is_signed_bpf_cond(BPF_OP(code))) { 738 + emit_sext_32(ctx, t7, is32); 739 + emit_sext_32(ctx, t2, is32); 740 + } else { 741 + emit_zext_32(ctx, t7, is32); 742 + emit_zext_32(ctx, t2, is32); 743 + } 744 + if (emit_cond_jmp(ctx, cond, t2, t7, jmp_offset) < 0) 745 + goto toofar; 746 + break; 747 + 748 + /* PC += off if dst & src */ 749 + case BPF_JMP | BPF_JSET | BPF_X: 750 + case BPF_JMP32 | BPF_JSET | BPF_X: 751 + jmp_offset = bpf2la_offset(i, off, ctx); 752 + emit_insn(ctx, and, t1, dst, src); 753 + emit_zext_32(ctx, t1, is32); 754 + if (emit_cond_jmp(ctx, cond, t1, LOONGARCH_GPR_ZERO, jmp_offset) < 0) 755 + goto toofar; 756 + break; 757 + 758 + /* PC += off if dst & imm */ 759 + case BPF_JMP | BPF_JSET | BPF_K: 760 + case BPF_JMP32 | BPF_JSET | BPF_K: 761 + jmp_offset = bpf2la_offset(i, off, ctx); 762 + move_imm(ctx, t1, imm, is32); 763 + emit_insn(ctx, and, t1, dst, t1); 764 + emit_zext_32(ctx, t1, is32); 765 + if (emit_cond_jmp(ctx, cond, t1, LOONGARCH_GPR_ZERO, jmp_offset) < 0) 766 + goto toofar; 767 + break; 768 + 769 + /* PC += off */ 770 + case BPF_JMP | BPF_JA: 771 + jmp_offset = bpf2la_offset(i, off, ctx); 772 + if (emit_uncond_jmp(ctx, jmp_offset) < 0) 773 + goto toofar; 774 + break; 775 + 776 + /* function call */ 777 + case BPF_JMP | BPF_CALL: 778 + int ret; 779 + u64 func_addr; 780 + bool func_addr_fixed; 781 + 782 + mark_call(ctx); 783 + ret = bpf_jit_get_func_addr(ctx->prog, insn, extra_pass, 784 + &func_addr, &func_addr_fixed); 785 + if (ret < 0) 786 + return ret; 787 + 788 + move_imm(ctx, t1, func_addr, is32); 789 + emit_insn(ctx, jirl, t1, LOONGARCH_GPR_RA, 0); 790 + move_reg(ctx, regmap[BPF_REG_0], LOONGARCH_GPR_A0); 791 + break; 792 + 793 + /* tail call */ 794 + case BPF_JMP | BPF_TAIL_CALL: 795 + mark_tail_call(ctx); 796 + if (emit_bpf_tail_call(ctx) < 0) 797 + return -EINVAL; 798 + break; 799 + 800 + /* function return */ 801 + case BPF_JMP | BPF_EXIT: 802 + emit_sext_32(ctx, regmap[BPF_REG_0], true); 803 + 804 + if (i == ctx->prog->len - 1) 805 + break; 806 + 807 + jmp_offset = epilogue_offset(ctx); 808 + if (emit_uncond_jmp(ctx, jmp_offset) < 0) 809 + goto toofar; 810 + break; 811 + 812 + /* dst = imm64 */ 813 + case BPF_LD | BPF_IMM | BPF_DW: 814 + u64 imm64 = (u64)(insn + 1)->imm << 32 | (u32)insn->imm; 815 + 816 + move_imm(ctx, dst, imm64, is32); 817 + return 1; 818 + 819 + /* dst = *(size *)(src + off) */ 820 + case BPF_LDX | BPF_MEM | BPF_B: 821 + case BPF_LDX | BPF_MEM | BPF_H: 822 + case BPF_LDX | BPF_MEM | BPF_W: 823 + case BPF_LDX | BPF_MEM | BPF_DW: 824 + switch (BPF_SIZE(code)) { 825 + case BPF_B: 826 + if (is_signed_imm12(off)) { 827 + emit_insn(ctx, ldbu, dst, src, off); 828 + } else { 829 + move_imm(ctx, t1, off, is32); 830 + emit_insn(ctx, ldxbu, dst, src, t1); 831 + } 832 + break; 833 + case BPF_H: 834 + if (is_signed_imm12(off)) { 835 + emit_insn(ctx, ldhu, dst, src, off); 836 + } else { 837 + move_imm(ctx, t1, off, is32); 838 + emit_insn(ctx, ldxhu, dst, src, t1); 839 + } 840 + break; 841 + case BPF_W: 842 + if (is_signed_imm12(off)) { 843 + emit_insn(ctx, ldwu, dst, src, off); 844 + } else if (is_signed_imm14(off)) { 845 + emit_insn(ctx, ldptrw, dst, src, off); 846 + } else { 847 + move_imm(ctx, t1, off, is32); 848 + emit_insn(ctx, ldxwu, dst, src, t1); 849 + } 850 + break; 851 + case BPF_DW: 852 + if (is_signed_imm12(off)) { 853 + emit_insn(ctx, ldd, dst, src, off); 854 + } else if (is_signed_imm14(off)) { 855 + emit_insn(ctx, ldptrd, dst, src, off); 856 + } else { 857 + move_imm(ctx, t1, off, is32); 858 + emit_insn(ctx, ldxd, dst, src, t1); 859 + } 860 + break; 861 + } 862 + break; 863 + 864 + /* *(size *)(dst + off) = imm */ 865 + case BPF_ST | BPF_MEM | BPF_B: 866 + case BPF_ST | BPF_MEM | BPF_H: 867 + case BPF_ST | BPF_MEM | BPF_W: 868 + case BPF_ST | BPF_MEM | BPF_DW: 869 + switch (BPF_SIZE(code)) { 870 + case BPF_B: 871 + move_imm(ctx, t1, imm, is32); 872 + if (is_signed_imm12(off)) { 873 + emit_insn(ctx, stb, t1, dst, off); 874 + } else { 875 + move_imm(ctx, t2, off, is32); 876 + emit_insn(ctx, stxb, t1, dst, t2); 877 + } 878 + break; 879 + case BPF_H: 880 + move_imm(ctx, t1, imm, is32); 881 + if (is_signed_imm12(off)) { 882 + emit_insn(ctx, sth, t1, dst, off); 883 + } else { 884 + move_imm(ctx, t2, off, is32); 885 + emit_insn(ctx, stxh, t1, dst, t2); 886 + } 887 + break; 888 + case BPF_W: 889 + move_imm(ctx, t1, imm, is32); 890 + if (is_signed_imm12(off)) { 891 + emit_insn(ctx, stw, t1, dst, off); 892 + } else if (is_signed_imm14(off)) { 893 + emit_insn(ctx, stptrw, t1, dst, off); 894 + } else { 895 + move_imm(ctx, t2, off, is32); 896 + emit_insn(ctx, stxw, t1, dst, t2); 897 + } 898 + break; 899 + case BPF_DW: 900 + move_imm(ctx, t1, imm, is32); 901 + if (is_signed_imm12(off)) { 902 + emit_insn(ctx, std, t1, dst, off); 903 + } else if (is_signed_imm14(off)) { 904 + emit_insn(ctx, stptrd, t1, dst, off); 905 + } else { 906 + move_imm(ctx, t2, off, is32); 907 + emit_insn(ctx, stxd, t1, dst, t2); 908 + } 909 + break; 910 + } 911 + break; 912 + 913 + /* *(size *)(dst + off) = src */ 914 + case BPF_STX | BPF_MEM | BPF_B: 915 + case BPF_STX | BPF_MEM | BPF_H: 916 + case BPF_STX | BPF_MEM | BPF_W: 917 + case BPF_STX | BPF_MEM | BPF_DW: 918 + switch (BPF_SIZE(code)) { 919 + case BPF_B: 920 + if (is_signed_imm12(off)) { 921 + emit_insn(ctx, stb, src, dst, off); 922 + } else { 923 + move_imm(ctx, t1, off, is32); 924 + emit_insn(ctx, stxb, src, dst, t1); 925 + } 926 + break; 927 + case BPF_H: 928 + if (is_signed_imm12(off)) { 929 + emit_insn(ctx, sth, src, dst, off); 930 + } else { 931 + move_imm(ctx, t1, off, is32); 932 + emit_insn(ctx, stxh, src, dst, t1); 933 + } 934 + break; 935 + case BPF_W: 936 + if (is_signed_imm12(off)) { 937 + emit_insn(ctx, stw, src, dst, off); 938 + } else if (is_signed_imm14(off)) { 939 + emit_insn(ctx, stptrw, src, dst, off); 940 + } else { 941 + move_imm(ctx, t1, off, is32); 942 + emit_insn(ctx, stxw, src, dst, t1); 943 + } 944 + break; 945 + case BPF_DW: 946 + if (is_signed_imm12(off)) { 947 + emit_insn(ctx, std, src, dst, off); 948 + } else if (is_signed_imm14(off)) { 949 + emit_insn(ctx, stptrd, src, dst, off); 950 + } else { 951 + move_imm(ctx, t1, off, is32); 952 + emit_insn(ctx, stxd, src, dst, t1); 953 + } 954 + break; 955 + } 956 + break; 957 + 958 + case BPF_STX | BPF_ATOMIC | BPF_W: 959 + case BPF_STX | BPF_ATOMIC | BPF_DW: 960 + emit_atomic(insn, ctx); 961 + break; 962 + 963 + default: 964 + pr_err("bpf_jit: unknown opcode %02x\n", code); 965 + return -EINVAL; 966 + } 967 + 968 + return 0; 969 + 970 + toofar: 971 + pr_info_once("bpf_jit: opcode %02x, jump too far\n", code); 972 + return -E2BIG; 973 + } 974 + 975 + static int build_body(struct jit_ctx *ctx, bool extra_pass) 976 + { 977 + int i; 978 + const struct bpf_prog *prog = ctx->prog; 979 + 980 + for (i = 0; i < prog->len; i++) { 981 + const struct bpf_insn *insn = &prog->insnsi[i]; 982 + int ret; 983 + 984 + if (ctx->image == NULL) 985 + ctx->offset[i] = ctx->idx; 986 + 987 + ret = build_insn(insn, ctx, extra_pass); 988 + if (ret > 0) { 989 + i++; 990 + if (ctx->image == NULL) 991 + ctx->offset[i] = ctx->idx; 992 + continue; 993 + } 994 + if (ret) 995 + return ret; 996 + } 997 + 998 + if (ctx->image == NULL) 999 + ctx->offset[i] = ctx->idx; 1000 + 1001 + return 0; 1002 + } 1003 + 1004 + /* Fill space with break instructions */ 1005 + static void jit_fill_hole(void *area, unsigned int size) 1006 + { 1007 + u32 *ptr; 1008 + 1009 + /* We are guaranteed to have aligned memory */ 1010 + for (ptr = area; size >= sizeof(u32); size -= sizeof(u32)) 1011 + *ptr++ = INSN_BREAK; 1012 + } 1013 + 1014 + static int validate_code(struct jit_ctx *ctx) 1015 + { 1016 + int i; 1017 + union loongarch_instruction insn; 1018 + 1019 + for (i = 0; i < ctx->idx; i++) { 1020 + insn = ctx->image[i]; 1021 + /* Check INSN_BREAK */ 1022 + if (insn.word == INSN_BREAK) 1023 + return -1; 1024 + } 1025 + 1026 + return 0; 1027 + } 1028 + 1029 + struct bpf_prog *bpf_int_jit_compile(struct bpf_prog *prog) 1030 + { 1031 + bool tmp_blinded = false, extra_pass = false; 1032 + u8 *image_ptr; 1033 + int image_size; 1034 + struct jit_ctx ctx; 1035 + struct jit_data *jit_data; 1036 + struct bpf_binary_header *header; 1037 + struct bpf_prog *tmp, *orig_prog = prog; 1038 + 1039 + /* 1040 + * If BPF JIT was not enabled then we must fall back to 1041 + * the interpreter. 1042 + */ 1043 + if (!prog->jit_requested) 1044 + return orig_prog; 1045 + 1046 + tmp = bpf_jit_blind_constants(prog); 1047 + /* 1048 + * If blinding was requested and we failed during blinding, 1049 + * we must fall back to the interpreter. Otherwise, we save 1050 + * the new JITed code. 1051 + */ 1052 + if (IS_ERR(tmp)) 1053 + return orig_prog; 1054 + 1055 + if (tmp != prog) { 1056 + tmp_blinded = true; 1057 + prog = tmp; 1058 + } 1059 + 1060 + jit_data = prog->aux->jit_data; 1061 + if (!jit_data) { 1062 + jit_data = kzalloc(sizeof(*jit_data), GFP_KERNEL); 1063 + if (!jit_data) { 1064 + prog = orig_prog; 1065 + goto out; 1066 + } 1067 + prog->aux->jit_data = jit_data; 1068 + } 1069 + if (jit_data->ctx.offset) { 1070 + ctx = jit_data->ctx; 1071 + image_ptr = jit_data->image; 1072 + header = jit_data->header; 1073 + extra_pass = true; 1074 + image_size = sizeof(u32) * ctx.idx; 1075 + goto skip_init_ctx; 1076 + } 1077 + 1078 + memset(&ctx, 0, sizeof(ctx)); 1079 + ctx.prog = prog; 1080 + 1081 + ctx.offset = kvcalloc(prog->len + 1, sizeof(u32), GFP_KERNEL); 1082 + if (ctx.offset == NULL) { 1083 + prog = orig_prog; 1084 + goto out_offset; 1085 + } 1086 + 1087 + /* 1. Initial fake pass to compute ctx->idx and set ctx->flags */ 1088 + build_prologue(&ctx); 1089 + if (build_body(&ctx, extra_pass)) { 1090 + prog = orig_prog; 1091 + goto out_offset; 1092 + } 1093 + ctx.epilogue_offset = ctx.idx; 1094 + build_epilogue(&ctx); 1095 + 1096 + /* Now we know the actual image size. 1097 + * As each LoongArch instruction is of length 32bit, 1098 + * we are translating number of JITed intructions into 1099 + * the size required to store these JITed code. 1100 + */ 1101 + image_size = sizeof(u32) * ctx.idx; 1102 + /* Now we know the size of the structure to make */ 1103 + header = bpf_jit_binary_alloc(image_size, &image_ptr, 1104 + sizeof(u32), jit_fill_hole); 1105 + if (header == NULL) { 1106 + prog = orig_prog; 1107 + goto out_offset; 1108 + } 1109 + 1110 + /* 2. Now, the actual pass to generate final JIT code */ 1111 + ctx.image = (union loongarch_instruction *)image_ptr; 1112 + 1113 + skip_init_ctx: 1114 + ctx.idx = 0; 1115 + 1116 + build_prologue(&ctx); 1117 + if (build_body(&ctx, extra_pass)) { 1118 + bpf_jit_binary_free(header); 1119 + prog = orig_prog; 1120 + goto out_offset; 1121 + } 1122 + build_epilogue(&ctx); 1123 + 1124 + /* 3. Extra pass to validate JITed code */ 1125 + if (validate_code(&ctx)) { 1126 + bpf_jit_binary_free(header); 1127 + prog = orig_prog; 1128 + goto out_offset; 1129 + } 1130 + 1131 + /* And we're done */ 1132 + if (bpf_jit_enable > 1) 1133 + bpf_jit_dump(prog->len, image_size, 2, ctx.image); 1134 + 1135 + /* Update the icache */ 1136 + flush_icache_range((unsigned long)header, (unsigned long)(ctx.image + ctx.idx)); 1137 + 1138 + if (!prog->is_func || extra_pass) { 1139 + if (extra_pass && ctx.idx != jit_data->ctx.idx) { 1140 + pr_err_once("multi-func JIT bug %d != %d\n", 1141 + ctx.idx, jit_data->ctx.idx); 1142 + bpf_jit_binary_free(header); 1143 + prog->bpf_func = NULL; 1144 + prog->jited = 0; 1145 + prog->jited_len = 0; 1146 + goto out_offset; 1147 + } 1148 + bpf_jit_binary_lock_ro(header); 1149 + } else { 1150 + jit_data->ctx = ctx; 1151 + jit_data->image = image_ptr; 1152 + jit_data->header = header; 1153 + } 1154 + prog->jited = 1; 1155 + prog->jited_len = image_size; 1156 + prog->bpf_func = (void *)ctx.image; 1157 + 1158 + if (!prog->is_func || extra_pass) { 1159 + int i; 1160 + 1161 + /* offset[prog->len] is the size of program */ 1162 + for (i = 0; i <= prog->len; i++) 1163 + ctx.offset[i] *= LOONGARCH_INSN_SIZE; 1164 + bpf_prog_fill_jited_linfo(prog, ctx.offset + 1); 1165 + 1166 + out_offset: 1167 + kvfree(ctx.offset); 1168 + kfree(jit_data); 1169 + prog->aux->jit_data = NULL; 1170 + } 1171 + 1172 + out: 1173 + if (tmp_blinded) 1174 + bpf_jit_prog_release_other(prog, prog == orig_prog ? tmp : orig_prog); 1175 + 1176 + out_offset = -1; 1177 + 1178 + return prog; 1179 + }
+282
arch/loongarch/net/bpf_jit.h
··· 1 + /* SPDX-License-Identifier: GPL-2.0-only */ 2 + /* 3 + * BPF JIT compiler for LoongArch 4 + * 5 + * Copyright (C) 2022 Loongson Technology Corporation Limited 6 + */ 7 + #include <linux/bpf.h> 8 + #include <linux/filter.h> 9 + #include <asm/cacheflush.h> 10 + #include <asm/inst.h> 11 + 12 + struct jit_ctx { 13 + const struct bpf_prog *prog; 14 + unsigned int idx; 15 + unsigned int flags; 16 + unsigned int epilogue_offset; 17 + u32 *offset; 18 + union loongarch_instruction *image; 19 + u32 stack_size; 20 + }; 21 + 22 + struct jit_data { 23 + struct bpf_binary_header *header; 24 + u8 *image; 25 + struct jit_ctx ctx; 26 + }; 27 + 28 + #define emit_insn(ctx, func, ...) \ 29 + do { \ 30 + if (ctx->image != NULL) { \ 31 + union loongarch_instruction *insn = &ctx->image[ctx->idx]; \ 32 + emit_##func(insn, ##__VA_ARGS__); \ 33 + } \ 34 + ctx->idx++; \ 35 + } while (0) 36 + 37 + #define is_signed_imm12(val) signed_imm_check(val, 12) 38 + #define is_signed_imm14(val) signed_imm_check(val, 14) 39 + #define is_signed_imm16(val) signed_imm_check(val, 16) 40 + #define is_signed_imm26(val) signed_imm_check(val, 26) 41 + #define is_signed_imm32(val) signed_imm_check(val, 32) 42 + #define is_signed_imm52(val) signed_imm_check(val, 52) 43 + #define is_unsigned_imm12(val) unsigned_imm_check(val, 12) 44 + 45 + static inline int bpf2la_offset(int bpf_insn, int off, const struct jit_ctx *ctx) 46 + { 47 + /* BPF JMP offset is relative to the next instruction */ 48 + bpf_insn++; 49 + /* 50 + * Whereas LoongArch branch instructions encode the offset 51 + * from the branch itself, so we must subtract 1 from the 52 + * instruction offset. 53 + */ 54 + return (ctx->offset[bpf_insn + off] - (ctx->offset[bpf_insn] - 1)); 55 + } 56 + 57 + static inline int epilogue_offset(const struct jit_ctx *ctx) 58 + { 59 + int from = ctx->idx; 60 + int to = ctx->epilogue_offset; 61 + 62 + return (to - from); 63 + } 64 + 65 + /* Zero-extend 32 bits into 64 bits */ 66 + static inline void emit_zext_32(struct jit_ctx *ctx, enum loongarch_gpr reg, bool is32) 67 + { 68 + if (!is32) 69 + return; 70 + 71 + emit_insn(ctx, lu32id, reg, 0); 72 + } 73 + 74 + /* Signed-extend 32 bits into 64 bits */ 75 + static inline void emit_sext_32(struct jit_ctx *ctx, enum loongarch_gpr reg, bool is32) 76 + { 77 + if (!is32) 78 + return; 79 + 80 + emit_insn(ctx, addiw, reg, reg, 0); 81 + } 82 + 83 + static inline void move_imm(struct jit_ctx *ctx, enum loongarch_gpr rd, long imm, bool is32) 84 + { 85 + long imm_11_0, imm_31_12, imm_51_32, imm_63_52, imm_51_0, imm_51_31; 86 + 87 + /* or rd, $zero, $zero */ 88 + if (imm == 0) { 89 + emit_insn(ctx, or, rd, LOONGARCH_GPR_ZERO, LOONGARCH_GPR_ZERO); 90 + return; 91 + } 92 + 93 + /* addiw rd, $zero, imm_11_0 */ 94 + if (is_signed_imm12(imm)) { 95 + emit_insn(ctx, addiw, rd, LOONGARCH_GPR_ZERO, imm); 96 + goto zext; 97 + } 98 + 99 + /* ori rd, $zero, imm_11_0 */ 100 + if (is_unsigned_imm12(imm)) { 101 + emit_insn(ctx, ori, rd, LOONGARCH_GPR_ZERO, imm); 102 + goto zext; 103 + } 104 + 105 + /* lu52id rd, $zero, imm_63_52 */ 106 + imm_63_52 = (imm >> 52) & 0xfff; 107 + imm_51_0 = imm & 0xfffffffffffff; 108 + if (imm_63_52 != 0 && imm_51_0 == 0) { 109 + emit_insn(ctx, lu52id, rd, LOONGARCH_GPR_ZERO, imm_63_52); 110 + return; 111 + } 112 + 113 + /* lu12iw rd, imm_31_12 */ 114 + imm_31_12 = (imm >> 12) & 0xfffff; 115 + emit_insn(ctx, lu12iw, rd, imm_31_12); 116 + 117 + /* ori rd, rd, imm_11_0 */ 118 + imm_11_0 = imm & 0xfff; 119 + if (imm_11_0 != 0) 120 + emit_insn(ctx, ori, rd, rd, imm_11_0); 121 + 122 + if (!is_signed_imm32(imm)) { 123 + if (imm_51_0 != 0) { 124 + /* 125 + * If bit[51:31] is all 0 or all 1, 126 + * it means bit[51:32] is sign extended by lu12iw, 127 + * no need to call lu32id to do a new filled operation. 128 + */ 129 + imm_51_31 = (imm >> 31) & 0x1fffff; 130 + if (imm_51_31 != 0 || imm_51_31 != 0x1fffff) { 131 + /* lu32id rd, imm_51_32 */ 132 + imm_51_32 = (imm >> 32) & 0xfffff; 133 + emit_insn(ctx, lu32id, rd, imm_51_32); 134 + } 135 + } 136 + 137 + /* lu52id rd, rd, imm_63_52 */ 138 + if (!is_signed_imm52(imm)) 139 + emit_insn(ctx, lu52id, rd, rd, imm_63_52); 140 + } 141 + 142 + zext: 143 + emit_zext_32(ctx, rd, is32); 144 + } 145 + 146 + static inline void move_reg(struct jit_ctx *ctx, enum loongarch_gpr rd, 147 + enum loongarch_gpr rj) 148 + { 149 + emit_insn(ctx, or, rd, rj, LOONGARCH_GPR_ZERO); 150 + } 151 + 152 + static inline int invert_jmp_cond(u8 cond) 153 + { 154 + switch (cond) { 155 + case BPF_JEQ: 156 + return BPF_JNE; 157 + case BPF_JNE: 158 + case BPF_JSET: 159 + return BPF_JEQ; 160 + case BPF_JGT: 161 + return BPF_JLE; 162 + case BPF_JGE: 163 + return BPF_JLT; 164 + case BPF_JLT: 165 + return BPF_JGE; 166 + case BPF_JLE: 167 + return BPF_JGT; 168 + case BPF_JSGT: 169 + return BPF_JSLE; 170 + case BPF_JSGE: 171 + return BPF_JSLT; 172 + case BPF_JSLT: 173 + return BPF_JSGE; 174 + case BPF_JSLE: 175 + return BPF_JSGT; 176 + } 177 + return -1; 178 + } 179 + 180 + static inline void cond_jmp_offset(struct jit_ctx *ctx, u8 cond, enum loongarch_gpr rj, 181 + enum loongarch_gpr rd, int jmp_offset) 182 + { 183 + switch (cond) { 184 + case BPF_JEQ: 185 + /* PC += jmp_offset if rj == rd */ 186 + emit_insn(ctx, beq, rj, rd, jmp_offset); 187 + return; 188 + case BPF_JNE: 189 + case BPF_JSET: 190 + /* PC += jmp_offset if rj != rd */ 191 + emit_insn(ctx, bne, rj, rd, jmp_offset); 192 + return; 193 + case BPF_JGT: 194 + /* PC += jmp_offset if rj > rd (unsigned) */ 195 + emit_insn(ctx, bltu, rd, rj, jmp_offset); 196 + return; 197 + case BPF_JLT: 198 + /* PC += jmp_offset if rj < rd (unsigned) */ 199 + emit_insn(ctx, bltu, rj, rd, jmp_offset); 200 + return; 201 + case BPF_JGE: 202 + /* PC += jmp_offset if rj >= rd (unsigned) */ 203 + emit_insn(ctx, bgeu, rj, rd, jmp_offset); 204 + return; 205 + case BPF_JLE: 206 + /* PC += jmp_offset if rj <= rd (unsigned) */ 207 + emit_insn(ctx, bgeu, rd, rj, jmp_offset); 208 + return; 209 + case BPF_JSGT: 210 + /* PC += jmp_offset if rj > rd (signed) */ 211 + emit_insn(ctx, blt, rd, rj, jmp_offset); 212 + return; 213 + case BPF_JSLT: 214 + /* PC += jmp_offset if rj < rd (signed) */ 215 + emit_insn(ctx, blt, rj, rd, jmp_offset); 216 + return; 217 + case BPF_JSGE: 218 + /* PC += jmp_offset if rj >= rd (signed) */ 219 + emit_insn(ctx, bge, rj, rd, jmp_offset); 220 + return; 221 + case BPF_JSLE: 222 + /* PC += jmp_offset if rj <= rd (signed) */ 223 + emit_insn(ctx, bge, rd, rj, jmp_offset); 224 + return; 225 + } 226 + } 227 + 228 + static inline void cond_jmp_offs26(struct jit_ctx *ctx, u8 cond, enum loongarch_gpr rj, 229 + enum loongarch_gpr rd, int jmp_offset) 230 + { 231 + cond = invert_jmp_cond(cond); 232 + cond_jmp_offset(ctx, cond, rj, rd, 2); 233 + emit_insn(ctx, b, jmp_offset); 234 + } 235 + 236 + static inline void uncond_jmp_offs26(struct jit_ctx *ctx, int jmp_offset) 237 + { 238 + emit_insn(ctx, b, jmp_offset); 239 + } 240 + 241 + static inline int emit_cond_jmp(struct jit_ctx *ctx, u8 cond, enum loongarch_gpr rj, 242 + enum loongarch_gpr rd, int jmp_offset) 243 + { 244 + /* 245 + * A large PC-relative jump offset may overflow the immediate field of 246 + * the native conditional branch instruction, triggering a conversion 247 + * to use an absolute jump instead, this jump sequence is particularly 248 + * nasty. For now, use cond_jmp_offs26() directly to keep it simple. 249 + * In the future, maybe we can add support for far branching, the branch 250 + * relaxation requires more than two passes to converge, the code seems 251 + * too complex to understand, not quite sure whether it is necessary and 252 + * worth the extra pain. Anyway, just leave it as it is to enhance code 253 + * readability now. 254 + */ 255 + if (is_signed_imm26(jmp_offset)) { 256 + cond_jmp_offs26(ctx, cond, rj, rd, jmp_offset); 257 + return 0; 258 + } 259 + 260 + return -EINVAL; 261 + } 262 + 263 + static inline int emit_uncond_jmp(struct jit_ctx *ctx, int jmp_offset) 264 + { 265 + if (is_signed_imm26(jmp_offset)) { 266 + uncond_jmp_offs26(ctx, jmp_offset); 267 + return 0; 268 + } 269 + 270 + return -EINVAL; 271 + } 272 + 273 + static inline int emit_tailcall_jmp(struct jit_ctx *ctx, u8 cond, enum loongarch_gpr rj, 274 + enum loongarch_gpr rd, int jmp_offset) 275 + { 276 + if (is_signed_imm16(jmp_offset)) { 277 + cond_jmp_offset(ctx, cond, rj, rd, jmp_offset); 278 + return 0; 279 + } 280 + 281 + return -EINVAL; 282 + }
+72 -4
arch/loongarch/pci/acpi.c
··· 83 83 } 84 84 85 85 /* 86 + * Create a PCI config space window 87 + * - reserve mem region 88 + * - alloc struct pci_config_window with space for all mappings 89 + * - ioremap the config space 90 + */ 91 + static struct pci_config_window *arch_pci_ecam_create(struct device *dev, 92 + struct resource *cfgres, struct resource *busr, const struct pci_ecam_ops *ops) 93 + { 94 + int bsz, bus_range, err; 95 + struct resource *conflict; 96 + struct pci_config_window *cfg; 97 + 98 + if (busr->start > busr->end) 99 + return ERR_PTR(-EINVAL); 100 + 101 + cfg = kzalloc(sizeof(*cfg), GFP_KERNEL); 102 + if (!cfg) 103 + return ERR_PTR(-ENOMEM); 104 + 105 + cfg->parent = dev; 106 + cfg->ops = ops; 107 + cfg->busr.start = busr->start; 108 + cfg->busr.end = busr->end; 109 + cfg->busr.flags = IORESOURCE_BUS; 110 + bus_range = resource_size(cfgres) >> ops->bus_shift; 111 + 112 + bsz = 1 << ops->bus_shift; 113 + 114 + cfg->res.start = cfgres->start; 115 + cfg->res.end = cfgres->end; 116 + cfg->res.flags = IORESOURCE_MEM | IORESOURCE_BUSY; 117 + cfg->res.name = "PCI ECAM"; 118 + 119 + conflict = request_resource_conflict(&iomem_resource, &cfg->res); 120 + if (conflict) { 121 + err = -EBUSY; 122 + dev_err(dev, "can't claim ECAM area %pR: address conflict with %s %pR\n", 123 + &cfg->res, conflict->name, conflict); 124 + goto err_exit; 125 + } 126 + 127 + cfg->win = pci_remap_cfgspace(cfgres->start, bus_range * bsz); 128 + if (!cfg->win) 129 + goto err_exit_iomap; 130 + 131 + if (ops->init) { 132 + err = ops->init(cfg); 133 + if (err) 134 + goto err_exit; 135 + } 136 + dev_info(dev, "ECAM at %pR for %pR\n", &cfg->res, &cfg->busr); 137 + 138 + return cfg; 139 + 140 + err_exit_iomap: 141 + err = -ENOMEM; 142 + dev_err(dev, "ECAM ioremap failed\n"); 143 + err_exit: 144 + pci_ecam_free(cfg); 145 + return ERR_PTR(err); 146 + } 147 + 148 + /* 86 149 * Lookup the bus range for the domain in MCFG, and set up config space 87 150 * mapping. 88 151 */ ··· 169 106 170 107 bus_shift = ecam_ops->bus_shift ? : 20; 171 108 172 - cfgres.start = root->mcfg_addr + (bus_res->start << bus_shift); 173 - cfgres.end = cfgres.start + (resource_size(bus_res) << bus_shift) - 1; 174 - cfgres.flags = IORESOURCE_MEM; 109 + if (bus_shift == 20) 110 + cfg = pci_ecam_create(dev, &cfgres, bus_res, ecam_ops); 111 + else { 112 + cfgres.start = root->mcfg_addr + (bus_res->start << bus_shift); 113 + cfgres.end = cfgres.start + (resource_size(bus_res) << bus_shift) - 1; 114 + cfgres.end |= BIT(28) + (((PCI_CFG_SPACE_EXP_SIZE - 1) & 0xf00) << 16); 115 + cfgres.flags = IORESOURCE_MEM; 116 + cfg = arch_pci_ecam_create(dev, &cfgres, bus_res, ecam_ops); 117 + } 175 118 176 - cfg = pci_ecam_create(dev, &cfgres, bus_res, ecam_ops); 177 119 if (IS_ERR(cfg)) { 178 120 dev_err(dev, "%04x:%pR error %ld mapping ECAM\n", seg, bus_res, PTR_ERR(cfg)); 179 121 return NULL;
+3 -4
arch/loongarch/pci/pci.c
··· 9 9 #include <linux/types.h> 10 10 #include <linux/pci.h> 11 11 #include <linux/vgaarb.h> 12 + #include <asm/cacheflush.h> 12 13 #include <asm/loongson.h> 13 14 14 15 #define PCI_DEVICE_ID_LOONGSON_HOST 0x7a00 ··· 46 45 unsigned int lsize; 47 46 48 47 /* 49 - * Set PCI cacheline size to that of the highest level in the 48 + * Set PCI cacheline size to that of the last level in the 50 49 * cache hierarchy. 51 50 */ 52 - lsize = cpu_dcache_line_size(); 53 - lsize = cpu_vcache_line_size() ? : lsize; 54 - lsize = cpu_scache_line_size() ? : lsize; 51 + lsize = cpu_last_level_cache_line_size(); 55 52 56 53 BUG_ON(!lsize); 57 54
+2
drivers/platform/Kconfig
··· 3 3 source "drivers/platform/mips/Kconfig" 4 4 endif 5 5 6 + source "drivers/platform/loongarch/Kconfig" 7 + 6 8 source "drivers/platform/goldfish/Kconfig" 7 9 8 10 source "drivers/platform/chrome/Kconfig"
+1
drivers/platform/Makefile
··· 4 4 # 5 5 6 6 obj-$(CONFIG_X86) += x86/ 7 + obj-$(CONFIG_LOONGARCH) += loongarch/ 7 8 obj-$(CONFIG_MELLANOX_PLATFORM) += mellanox/ 8 9 obj-$(CONFIG_MIPS) += mips/ 9 10 obj-$(CONFIG_OLPC_EC) += olpc/
+31
drivers/platform/loongarch/Kconfig
··· 1 + # 2 + # LoongArch Platform Specific Drivers 3 + # 4 + 5 + menuconfig LOONGARCH_PLATFORM_DEVICES 6 + bool "LoongArch Platform Specific Device Drivers" 7 + default y 8 + depends on LOONGARCH 9 + help 10 + Say Y here to get to see options for device drivers of various 11 + LoongArch platforms, including vendor-specific laptop/desktop 12 + extension and hardware monitor drivers. This option itself does 13 + not add any kernel code. 14 + 15 + If you say N, all options in this submenu will be skipped and disabled. 16 + 17 + if LOONGARCH_PLATFORM_DEVICES 18 + 19 + config LOONGSON_LAPTOP 20 + tristate "Generic Loongson-3 Laptop Driver" 21 + depends on ACPI 22 + depends on BACKLIGHT_CLASS_DEVICE 23 + depends on INPUT 24 + depends on MACH_LOONGSON64 25 + select ACPI_VIDEO 26 + select INPUT_SPARSEKMAP 27 + default y 28 + help 29 + ACPI-based Loongson-3 family laptops generic driver. 30 + 31 + endif # LOONGARCH_PLATFORM_DEVICES
+1
drivers/platform/loongarch/Makefile
··· 1 + obj-$(CONFIG_LOONGSON_LAPTOP) += loongson-laptop.o
+624
drivers/platform/loongarch/loongson-laptop.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + /* 3 + * Generic Loongson processor based LAPTOP/ALL-IN-ONE driver 4 + * 5 + * Jianmin Lv <lvjianmin@loongson.cn> 6 + * Huacai Chen <chenhuacai@loongson.cn> 7 + * 8 + * Copyright (C) 2022 Loongson Technology Corporation Limited 9 + */ 10 + 11 + #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 + 13 + #include <linux/init.h> 14 + #include <linux/kernel.h> 15 + #include <linux/module.h> 16 + #include <linux/acpi.h> 17 + #include <linux/backlight.h> 18 + #include <linux/device.h> 19 + #include <linux/input.h> 20 + #include <linux/input/sparse-keymap.h> 21 + #include <linux/platform_device.h> 22 + #include <linux/string.h> 23 + #include <linux/types.h> 24 + #include <acpi/video.h> 25 + 26 + /* 1. Driver-wide structs and misc. variables */ 27 + 28 + /* ACPI HIDs */ 29 + #define LOONGSON_ACPI_EC_HID "PNP0C09" 30 + #define LOONGSON_ACPI_HKEY_HID "LOON0000" 31 + 32 + #define ACPI_LAPTOP_NAME "loongson-laptop" 33 + #define ACPI_LAPTOP_ACPI_EVENT_PREFIX "loongson" 34 + 35 + #define MAX_ACPI_ARGS 3 36 + #define GENERIC_HOTKEY_MAP_MAX 64 37 + 38 + #define GENERIC_EVENT_TYPE_OFF 12 39 + #define GENERIC_EVENT_TYPE_MASK 0xF000 40 + #define GENERIC_EVENT_CODE_MASK 0x0FFF 41 + 42 + struct generic_sub_driver { 43 + u32 type; 44 + char *name; 45 + acpi_handle *handle; 46 + struct acpi_device *device; 47 + struct platform_driver *driver; 48 + int (*init)(struct generic_sub_driver *sub_driver); 49 + void (*notify)(struct generic_sub_driver *sub_driver, u32 event); 50 + u8 acpi_notify_installed; 51 + }; 52 + 53 + static u32 input_device_registered; 54 + static struct input_dev *generic_inputdev; 55 + 56 + static acpi_handle hotkey_handle; 57 + static struct key_entry hotkey_keycode_map[GENERIC_HOTKEY_MAP_MAX]; 58 + 59 + int loongson_laptop_turn_on_backlight(void); 60 + int loongson_laptop_turn_off_backlight(void); 61 + static int loongson_laptop_backlight_update(struct backlight_device *bd); 62 + 63 + /* 2. ACPI Helpers and device model */ 64 + 65 + static int acpi_evalf(acpi_handle handle, int *res, char *method, char *fmt, ...) 66 + { 67 + char res_type; 68 + char *fmt0 = fmt; 69 + va_list ap; 70 + int success, quiet; 71 + acpi_status status; 72 + struct acpi_object_list params; 73 + struct acpi_buffer result, *resultp; 74 + union acpi_object in_objs[MAX_ACPI_ARGS], out_obj; 75 + 76 + if (!*fmt) { 77 + pr_err("acpi_evalf() called with empty format\n"); 78 + return 0; 79 + } 80 + 81 + if (*fmt == 'q') { 82 + quiet = 1; 83 + fmt++; 84 + } else 85 + quiet = 0; 86 + 87 + res_type = *(fmt++); 88 + 89 + params.count = 0; 90 + params.pointer = &in_objs[0]; 91 + 92 + va_start(ap, fmt); 93 + while (*fmt) { 94 + char c = *(fmt++); 95 + switch (c) { 96 + case 'd': /* int */ 97 + in_objs[params.count].integer.value = va_arg(ap, int); 98 + in_objs[params.count++].type = ACPI_TYPE_INTEGER; 99 + break; 100 + /* add more types as needed */ 101 + default: 102 + pr_err("acpi_evalf() called with invalid format character '%c'\n", c); 103 + va_end(ap); 104 + return 0; 105 + } 106 + } 107 + va_end(ap); 108 + 109 + if (res_type != 'v') { 110 + result.length = sizeof(out_obj); 111 + result.pointer = &out_obj; 112 + resultp = &result; 113 + } else 114 + resultp = NULL; 115 + 116 + status = acpi_evaluate_object(handle, method, &params, resultp); 117 + 118 + switch (res_type) { 119 + case 'd': /* int */ 120 + success = (status == AE_OK && out_obj.type == ACPI_TYPE_INTEGER); 121 + if (success && res) 122 + *res = out_obj.integer.value; 123 + break; 124 + case 'v': /* void */ 125 + success = status == AE_OK; 126 + break; 127 + /* add more types as needed */ 128 + default: 129 + pr_err("acpi_evalf() called with invalid format character '%c'\n", res_type); 130 + return 0; 131 + } 132 + 133 + if (!success && !quiet) 134 + pr_err("acpi_evalf(%s, %s, ...) failed: %s\n", 135 + method, fmt0, acpi_format_exception(status)); 136 + 137 + return success; 138 + } 139 + 140 + static int hotkey_status_get(int *status) 141 + { 142 + if (!acpi_evalf(hotkey_handle, status, "GSWS", "d")) 143 + return -EIO; 144 + 145 + return 0; 146 + } 147 + 148 + static void dispatch_acpi_notify(acpi_handle handle, u32 event, void *data) 149 + { 150 + struct generic_sub_driver *sub_driver = data; 151 + 152 + if (!sub_driver || !sub_driver->notify) 153 + return; 154 + sub_driver->notify(sub_driver, event); 155 + } 156 + 157 + static int __init setup_acpi_notify(struct generic_sub_driver *sub_driver) 158 + { 159 + acpi_status status; 160 + 161 + if (!*sub_driver->handle) 162 + return 0; 163 + 164 + sub_driver->device = acpi_fetch_acpi_dev(*sub_driver->handle); 165 + if (!sub_driver->device) { 166 + pr_err("acpi_fetch_acpi_dev(%s) failed\n", sub_driver->name); 167 + return -ENODEV; 168 + } 169 + 170 + sub_driver->device->driver_data = sub_driver; 171 + sprintf(acpi_device_class(sub_driver->device), "%s/%s", 172 + ACPI_LAPTOP_ACPI_EVENT_PREFIX, sub_driver->name); 173 + 174 + status = acpi_install_notify_handler(*sub_driver->handle, 175 + sub_driver->type, dispatch_acpi_notify, sub_driver); 176 + if (ACPI_FAILURE(status)) { 177 + if (status == AE_ALREADY_EXISTS) { 178 + pr_notice("Another device driver is already " 179 + "handling %s events\n", sub_driver->name); 180 + } else { 181 + pr_err("acpi_install_notify_handler(%s) failed: %s\n", 182 + sub_driver->name, acpi_format_exception(status)); 183 + } 184 + return -ENODEV; 185 + } 186 + sub_driver->acpi_notify_installed = 1; 187 + 188 + return 0; 189 + } 190 + 191 + static int loongson_hotkey_suspend(struct device *dev) 192 + { 193 + return 0; 194 + } 195 + 196 + static int loongson_hotkey_resume(struct device *dev) 197 + { 198 + int status = 0; 199 + struct key_entry ke; 200 + struct backlight_device *bd; 201 + 202 + /* 203 + * Only if the firmware supports SW_LID event model, we can handle the 204 + * event. This is for the consideration of development board without EC. 205 + */ 206 + if (test_bit(SW_LID, generic_inputdev->swbit)) { 207 + if (hotkey_status_get(&status) < 0) 208 + return -EIO; 209 + /* 210 + * The input device sw element records the last lid status. 211 + * When the system is awakened by other wake-up sources, 212 + * the lid event will also be reported. The judgment of 213 + * adding SW_LID bit which in sw element can avoid this 214 + * case. 215 + * 216 + * Input system will drop lid event when current lid event 217 + * value and last lid status in the same. So laptop driver 218 + * doesn't report repeated events. 219 + * 220 + * Lid status is generally 0, but hardware exception is 221 + * considered. So add lid status confirmation. 222 + */ 223 + if (test_bit(SW_LID, generic_inputdev->sw) && !(status & (1 << SW_LID))) { 224 + ke.type = KE_SW; 225 + ke.sw.value = (u8)status; 226 + ke.sw.code = SW_LID; 227 + sparse_keymap_report_entry(generic_inputdev, &ke, 1, true); 228 + } 229 + } 230 + 231 + bd = backlight_device_get_by_type(BACKLIGHT_PLATFORM); 232 + if (bd) { 233 + loongson_laptop_backlight_update(bd) ? 234 + pr_warn("Loongson_backlight: resume brightness failed") : 235 + pr_info("Loongson_backlight: resume brightness %d\n", bd->props.brightness); 236 + } 237 + 238 + return 0; 239 + } 240 + 241 + static DEFINE_SIMPLE_DEV_PM_OPS(loongson_hotkey_pm, 242 + loongson_hotkey_suspend, loongson_hotkey_resume); 243 + 244 + static int loongson_hotkey_probe(struct platform_device *pdev) 245 + { 246 + hotkey_handle = ACPI_HANDLE(&pdev->dev); 247 + 248 + if (!hotkey_handle) 249 + return -ENODEV; 250 + 251 + return 0; 252 + } 253 + 254 + static const struct acpi_device_id loongson_device_ids[] = { 255 + {LOONGSON_ACPI_HKEY_HID, 0}, 256 + {"", 0}, 257 + }; 258 + MODULE_DEVICE_TABLE(acpi, loongson_device_ids); 259 + 260 + static struct platform_driver loongson_hotkey_driver = { 261 + .probe = loongson_hotkey_probe, 262 + .driver = { 263 + .name = "loongson-hotkey", 264 + .owner = THIS_MODULE, 265 + .pm = pm_ptr(&loongson_hotkey_pm), 266 + .acpi_match_table = loongson_device_ids, 267 + }, 268 + }; 269 + 270 + static int hotkey_map(void) 271 + { 272 + u32 index; 273 + acpi_status status; 274 + struct acpi_buffer buf; 275 + union acpi_object *pack; 276 + 277 + buf.length = ACPI_ALLOCATE_BUFFER; 278 + status = acpi_evaluate_object_typed(hotkey_handle, "KMAP", NULL, &buf, ACPI_TYPE_PACKAGE); 279 + if (status != AE_OK) { 280 + pr_err("ACPI exception: %s\n", acpi_format_exception(status)); 281 + return -1; 282 + } 283 + pack = buf.pointer; 284 + for (index = 0; index < pack->package.count; index++) { 285 + union acpi_object *element, *sub_pack; 286 + 287 + sub_pack = &pack->package.elements[index]; 288 + 289 + element = &sub_pack->package.elements[0]; 290 + hotkey_keycode_map[index].type = element->integer.value; 291 + element = &sub_pack->package.elements[1]; 292 + hotkey_keycode_map[index].code = element->integer.value; 293 + element = &sub_pack->package.elements[2]; 294 + hotkey_keycode_map[index].keycode = element->integer.value; 295 + } 296 + 297 + return 0; 298 + } 299 + 300 + static int hotkey_backlight_set(bool enable) 301 + { 302 + if (!acpi_evalf(hotkey_handle, NULL, "VCBL", "vd", enable ? 1 : 0)) 303 + return -EIO; 304 + 305 + return 0; 306 + } 307 + 308 + static int ec_get_brightness(void) 309 + { 310 + int status = 0; 311 + 312 + if (!hotkey_handle) 313 + return -ENXIO; 314 + 315 + if (!acpi_evalf(hotkey_handle, &status, "ECBG", "d")) 316 + return -EIO; 317 + 318 + return status; 319 + } 320 + 321 + static int ec_set_brightness(int level) 322 + { 323 + 324 + int ret = 0; 325 + 326 + if (!hotkey_handle) 327 + return -ENXIO; 328 + 329 + if (!acpi_evalf(hotkey_handle, NULL, "ECBS", "vd", level)) 330 + ret = -EIO; 331 + 332 + return ret; 333 + } 334 + 335 + static int ec_backlight_level(u8 level) 336 + { 337 + int status = 0; 338 + 339 + if (!hotkey_handle) 340 + return -ENXIO; 341 + 342 + if (!acpi_evalf(hotkey_handle, &status, "ECLL", "d")) 343 + return -EIO; 344 + 345 + if ((status < 0) || (level > status)) 346 + return status; 347 + 348 + if (!acpi_evalf(hotkey_handle, &status, "ECSL", "d")) 349 + return -EIO; 350 + 351 + if ((status < 0) || (level < status)) 352 + return status; 353 + 354 + return level; 355 + } 356 + 357 + static int loongson_laptop_backlight_update(struct backlight_device *bd) 358 + { 359 + int lvl = ec_backlight_level(bd->props.brightness); 360 + 361 + if (lvl < 0) 362 + return -EIO; 363 + if (ec_set_brightness(lvl)) 364 + return -EIO; 365 + 366 + return 0; 367 + } 368 + 369 + static int loongson_laptop_get_brightness(struct backlight_device *bd) 370 + { 371 + int level; 372 + 373 + level = ec_get_brightness(); 374 + if (level < 0) 375 + return -EIO; 376 + 377 + return level; 378 + } 379 + 380 + static const struct backlight_ops backlight_laptop_ops = { 381 + .update_status = loongson_laptop_backlight_update, 382 + .get_brightness = loongson_laptop_get_brightness, 383 + }; 384 + 385 + static int laptop_backlight_register(void) 386 + { 387 + int status = 0; 388 + struct backlight_properties props; 389 + 390 + memset(&props, 0, sizeof(props)); 391 + 392 + if (!acpi_evalf(hotkey_handle, &status, "ECLL", "d")) 393 + return -EIO; 394 + 395 + props.brightness = 1; 396 + props.max_brightness = status; 397 + props.type = BACKLIGHT_PLATFORM; 398 + 399 + backlight_device_register("loongson_laptop", 400 + NULL, NULL, &backlight_laptop_ops, &props); 401 + 402 + return 0; 403 + } 404 + 405 + int loongson_laptop_turn_on_backlight(void) 406 + { 407 + int status; 408 + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; 409 + struct acpi_object_list args = { 1, &arg0 }; 410 + 411 + arg0.integer.value = 1; 412 + status = acpi_evaluate_object(NULL, "\\BLSW", &args, NULL); 413 + if (ACPI_FAILURE(status)) { 414 + pr_info("Loongson lvds error: 0x%x\n", status); 415 + return -ENODEV; 416 + } 417 + 418 + return 0; 419 + } 420 + 421 + int loongson_laptop_turn_off_backlight(void) 422 + { 423 + int status; 424 + union acpi_object arg0 = { ACPI_TYPE_INTEGER }; 425 + struct acpi_object_list args = { 1, &arg0 }; 426 + 427 + arg0.integer.value = 0; 428 + status = acpi_evaluate_object(NULL, "\\BLSW", &args, NULL); 429 + if (ACPI_FAILURE(status)) { 430 + pr_info("Loongson lvds error: 0x%x\n", status); 431 + return -ENODEV; 432 + } 433 + 434 + return 0; 435 + } 436 + 437 + static int __init event_init(struct generic_sub_driver *sub_driver) 438 + { 439 + int ret; 440 + 441 + ret = hotkey_map(); 442 + if (ret < 0) { 443 + pr_err("Failed to parse keymap from DSDT\n"); 444 + return ret; 445 + } 446 + 447 + ret = sparse_keymap_setup(generic_inputdev, hotkey_keycode_map, NULL); 448 + if (ret < 0) { 449 + pr_err("Failed to setup input device keymap\n"); 450 + input_free_device(generic_inputdev); 451 + 452 + return ret; 453 + } 454 + 455 + /* 456 + * This hotkey driver handle backlight event when 457 + * acpi_video_get_backlight_type() gets acpi_backlight_vendor 458 + */ 459 + if (acpi_video_get_backlight_type() == acpi_backlight_vendor) 460 + hotkey_backlight_set(true); 461 + else 462 + hotkey_backlight_set(false); 463 + 464 + pr_info("ACPI: enabling firmware HKEY event interface...\n"); 465 + 466 + return ret; 467 + } 468 + 469 + static void event_notify(struct generic_sub_driver *sub_driver, u32 event) 470 + { 471 + int type, scan_code; 472 + struct key_entry *ke = NULL; 473 + 474 + scan_code = event & GENERIC_EVENT_CODE_MASK; 475 + type = (event & GENERIC_EVENT_TYPE_MASK) >> GENERIC_EVENT_TYPE_OFF; 476 + ke = sparse_keymap_entry_from_scancode(generic_inputdev, scan_code); 477 + if (ke) { 478 + if (type == KE_SW) { 479 + int status = 0; 480 + 481 + if (hotkey_status_get(&status) < 0) 482 + return; 483 + 484 + ke->sw.value = !!(status & (1 << ke->sw.code)); 485 + } 486 + sparse_keymap_report_entry(generic_inputdev, ke, 1, true); 487 + } 488 + } 489 + 490 + /* 3. Infrastructure */ 491 + 492 + static void generic_subdriver_exit(struct generic_sub_driver *sub_driver); 493 + 494 + static int __init generic_subdriver_init(struct generic_sub_driver *sub_driver) 495 + { 496 + int ret; 497 + 498 + if (!sub_driver || !sub_driver->driver) 499 + return -EINVAL; 500 + 501 + ret = platform_driver_register(sub_driver->driver); 502 + if (ret) 503 + return -EINVAL; 504 + 505 + if (sub_driver->init) 506 + sub_driver->init(sub_driver); 507 + 508 + if (sub_driver->notify) { 509 + ret = setup_acpi_notify(sub_driver); 510 + if (ret == -ENODEV) { 511 + ret = 0; 512 + goto err_out; 513 + } 514 + if (ret < 0) 515 + goto err_out; 516 + } 517 + 518 + return 0; 519 + 520 + err_out: 521 + generic_subdriver_exit(sub_driver); 522 + return (ret < 0) ? ret : 0; 523 + } 524 + 525 + static void generic_subdriver_exit(struct generic_sub_driver *sub_driver) 526 + { 527 + 528 + if (sub_driver->acpi_notify_installed) { 529 + acpi_remove_notify_handler(*sub_driver->handle, 530 + sub_driver->type, dispatch_acpi_notify); 531 + sub_driver->acpi_notify_installed = 0; 532 + } 533 + platform_driver_unregister(sub_driver->driver); 534 + } 535 + 536 + static struct generic_sub_driver generic_sub_drivers[] __refdata = { 537 + { 538 + .name = "hotkey", 539 + .init = event_init, 540 + .notify = event_notify, 541 + .handle = &hotkey_handle, 542 + .type = ACPI_DEVICE_NOTIFY, 543 + .driver = &loongson_hotkey_driver, 544 + }, 545 + }; 546 + 547 + static int __init generic_acpi_laptop_init(void) 548 + { 549 + bool ec_found; 550 + int i, ret, status; 551 + 552 + if (acpi_disabled) 553 + return -ENODEV; 554 + 555 + /* The EC device is required */ 556 + ec_found = acpi_dev_found(LOONGSON_ACPI_EC_HID); 557 + if (!ec_found) 558 + return -ENODEV; 559 + 560 + /* Enable SCI for EC */ 561 + acpi_write_bit_register(ACPI_BITREG_SCI_ENABLE, 1); 562 + 563 + generic_inputdev = input_allocate_device(); 564 + if (!generic_inputdev) { 565 + pr_err("Unable to allocate input device\n"); 566 + return -ENOMEM; 567 + } 568 + 569 + /* Prepare input device, but don't register */ 570 + generic_inputdev->name = 571 + "Loongson Generic Laptop/All-in-One Extra Buttons"; 572 + generic_inputdev->phys = ACPI_LAPTOP_NAME "/input0"; 573 + generic_inputdev->id.bustype = BUS_HOST; 574 + generic_inputdev->dev.parent = NULL; 575 + 576 + /* Init subdrivers */ 577 + for (i = 0; i < ARRAY_SIZE(generic_sub_drivers); i++) { 578 + ret = generic_subdriver_init(&generic_sub_drivers[i]); 579 + if (ret < 0) { 580 + input_free_device(generic_inputdev); 581 + while (--i >= 0) 582 + generic_subdriver_exit(&generic_sub_drivers[i]); 583 + return ret; 584 + } 585 + } 586 + 587 + ret = input_register_device(generic_inputdev); 588 + if (ret < 0) { 589 + input_free_device(generic_inputdev); 590 + while (--i >= 0) 591 + generic_subdriver_exit(&generic_sub_drivers[i]); 592 + pr_err("Unable to register input device\n"); 593 + return ret; 594 + } 595 + 596 + input_device_registered = 1; 597 + 598 + if (acpi_evalf(hotkey_handle, &status, "ECBG", "d")) { 599 + pr_info("Loongson Laptop used, init brightness is 0x%x\n", status); 600 + ret = laptop_backlight_register(); 601 + if (ret < 0) 602 + pr_err("Loongson Laptop: laptop-backlight device register failed\n"); 603 + } 604 + 605 + return 0; 606 + } 607 + 608 + static void __exit generic_acpi_laptop_exit(void) 609 + { 610 + if (generic_inputdev) { 611 + if (input_device_registered) 612 + input_unregister_device(generic_inputdev); 613 + else 614 + input_free_device(generic_inputdev); 615 + } 616 + } 617 + 618 + module_init(generic_acpi_laptop_init); 619 + module_exit(generic_acpi_laptop_exit); 620 + 621 + MODULE_AUTHOR("Jianmin Lv <lvjianmin@loongson.cn>"); 622 + MODULE_AUTHOR("Huacai Chen <chenhuacai@loongson.cn>"); 623 + MODULE_DESCRIPTION("Loongson Laptop/All-in-One ACPI Driver"); 624 + MODULE_LICENSE("GPL");