Resguardo inicial

This commit is contained in:
lastdragon
2026-09-06 12:53:21 -06:00
parent 662efe3c2a
commit ad2088d178
94853 changed files with 43200404 additions and 0 deletions
@@ -0,0 +1,15 @@
# SPDX-License-Identifier: GPL-2.0
syscall-y += syscall_table_32.h
syscall-y += syscall_table_64.h
generated-y += orc_hash.h
generic-y += mcs_spinlock.h
generic-y += parport.h
generic-y += early_ioremap.h
generic-y += qrwlock.h
generic-y += user.h
generic-y += ioctl.h
generic-y += mmzone.h
generic-y += ring_buffer.h
generic-y += statfs.h
generic-y += text-patching.h
@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* LoongArch specific ACPICA environments and implementation
*
* Author: Jianmin Lv <lvjianmin@loongson.cn>
* Huacai Chen <chenhuacai@loongson.cn>
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_LOONGARCH_ACENV_H
#define _ASM_LOONGARCH_ACENV_H
#ifdef CONFIG_ARCH_STRICT_ALIGN
#define ACPI_MISALIGNMENT_NOT_SUPPORTED
#endif /* CONFIG_ARCH_STRICT_ALIGN */
#endif /* _ASM_LOONGARCH_ACENV_H */
@@ -0,0 +1,60 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Author: Jianmin Lv <lvjianmin@loongson.cn>
* Huacai Chen <chenhuacai@loongson.cn>
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_LOONGARCH_ACPI_H
#define _ASM_LOONGARCH_ACPI_H
#include <asm/smp.h>
#include <asm/suspend.h>
#ifdef CONFIG_ACPI
extern int acpi_strict;
extern int acpi_disabled;
extern int acpi_pci_disabled;
extern int acpi_noirq;
extern int pptt_enabled;
#define acpi_os_ioremap acpi_os_ioremap
void __iomem *acpi_os_ioremap(acpi_physical_address phys, acpi_size size);
static inline void disable_acpi(void)
{
acpi_disabled = 1;
acpi_pci_disabled = 1;
acpi_noirq = 1;
}
static inline bool acpi_has_cpu_in_madt(void)
{
return true;
}
#define MAX_CORE_PIC 2048
extern struct list_head acpi_wakeup_device_list;
extern struct acpi_madt_core_pic acpi_core_pic[MAX_CORE_PIC];
extern void acpi_add_early_pio(void);
extern void acpi_remove_early_pio(void);
extern int __init parse_acpi_topology(void);
#endif /* !CONFIG_ACPI */
#define ACPI_TABLE_UPGRADE_MAX_PHYS ARCH_LOW_ADDRESS_LIMIT
extern int loongarch_acpi_suspend(void);
extern int (*acpi_suspend_lowlevel)(void);
static inline unsigned long acpi_get_wakeup_address(void)
{
#ifdef CONFIG_SUSPEND
return (unsigned long)loongarch_wakeup_start;
#endif
return 0UL;
}
#endif /* _ASM_LOONGARCH_ACPI_H */
@@ -0,0 +1,148 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*
* Derived from MIPS:
* Copyright (C) 1996, 99 Ralf Baechle
* Copyright (C) 2000, 2002 Maciej W. Rozycki
* Copyright (C) 1990, 1999 by Silicon Graphics, Inc.
*/
#ifndef _ASM_ADDRSPACE_H
#define _ASM_ADDRSPACE_H
#include <linux/const.h>
#include <linux/sizes.h>
#include <asm/loongarch.h>
/*
* This gives the physical RAM offset.
*/
#ifndef __ASSEMBLER__
#ifndef PHYS_OFFSET
#define PHYS_OFFSET _UL(0)
#endif
extern unsigned long vm_map_base;
#endif /* __ASSEMBLER__ */
#ifndef IO_BASE
#define IO_BASE CSR_DMW0_BASE
#endif
#ifndef CACHE_BASE
#define CACHE_BASE CSR_DMW1_BASE
#endif
#ifndef UNCACHE_BASE
#define UNCACHE_BASE CSR_DMW0_BASE
#endif
#ifndef WRITECOMBINE_BASE
#ifdef CONFIG_32BIT
#define WRITECOMBINE_BASE CSR_DMW0_BASE
#else
#define WRITECOMBINE_BASE CSR_DMW2_BASE
#endif
#endif
#ifdef CONFIG_32BIT
#define DMW_PABITS 29
#define TO_PHYS_MASK ((_UL(1) << _UL(DMW_PABITS)) - 1)
#else
#define DMW_PABITS 48
#define TO_PHYS_MASK ((_ULL(1) << _ULL(DMW_PABITS)) - 1)
#endif
/*
* Memory above this physical address will be considered highmem.
*/
#ifndef HIGHMEM_START
#define HIGHMEM_START (_UL(1) << _UL(DMW_PABITS))
#endif
#define TO_PHYS(x) ( ((x) & TO_PHYS_MASK))
#define TO_CACHE(x) (CACHE_BASE | ((x) & TO_PHYS_MASK))
#define TO_UNCACHE(x) (UNCACHE_BASE | ((x) & TO_PHYS_MASK))
/*
* This handles the memory map.
*/
#ifndef PAGE_OFFSET
#define PAGE_OFFSET (CACHE_BASE + PHYS_OFFSET)
#endif
#ifndef FIXADDR_TOP
#define FIXADDR_TOP ((unsigned long)(long)(int)0xfffe0000)
#endif
#ifdef __ASSEMBLER__
#define _ATYPE_
#define _ATYPE32_
#define _ATYPE64_
#else
#define _ATYPE_ __PTRDIFF_TYPE__
#define _ATYPE32_ int
#define _ATYPE64_ __s64
#endif
#ifdef CONFIG_64BIT
#define _CONST64_(x) _UL(x)
#else
#define _CONST64_(x) _ULL(x)
#endif
/*
* 32/64-bit LoongArch address spaces
*/
#ifdef __ASSEMBLER__
#define _ACAST32_
#define _ACAST64_
#else
#define _ACAST32_ (_ATYPE_)(_ATYPE32_) /* widen if necessary */
#define _ACAST64_ (_ATYPE64_) /* do _not_ narrow */
#endif
#ifdef CONFIG_32BIT
#define UVRANGE 0x00000000
#define KPRANGE0 0x80000000
#define KPRANGE1 0xa0000000
#define KVRANGE 0xc0000000
#else
#define XUVRANGE _CONST64_(0x0000000000000000)
#define XSPRANGE _CONST64_(0x4000000000000000)
#define XKPRANGE _CONST64_(0x8000000000000000)
#define XKVRANGE _CONST64_(0xc000000000000000)
#endif
/*
* Returns the physical address of a KPRANGEx / XKPRANGE address
*/
#ifdef CONFIG_32BIT
#define PHYSADDR(a) ((_ACAST32_(a)) & TO_PHYS_MASK)
#else
#define PHYSADDR(a) ((_ACAST64_(a)) & TO_PHYS_MASK)
#endif
/*
* On LoongArch, I/O ports mappring is following:
*
* | .... |
* |-----------------------|
* | pci io ports(16K~32M) |
* |-----------------------|
* | isa io ports(0 ~16K) |
* PCI_IOBASE ->|-----------------------|
* | .... |
*/
#define PCI_IOBASE ((void __iomem *)(vm_map_base + (2 * PAGE_SIZE)))
#define PCI_IOSIZE SZ_32M
#define ISA_IOSIZE SZ_16K
#define IO_SPACE_LIMIT (PCI_IOSIZE - 1)
#define PHYS_LINK_KADDR PHYSADDR(VMLINUX_LOAD_ADDRESS)
#endif /* _ASM_ADDRSPACE_H */
@@ -0,0 +1,82 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_ALTERNATIVE_ASM_H
#define _ASM_ALTERNATIVE_ASM_H
#ifdef __ASSEMBLER__
#include <asm/asm.h>
/*
* Issue one struct alt_instr descriptor entry (need to put it into
* the section .altinstructions, see below). This entry contains
* enough information for the alternatives patching code to patch an
* instruction. See apply_alternatives().
*/
.macro altinstruction_entry orig alt feature orig_len alt_len
.long \orig - .
.long \alt - .
.short \feature
.byte \orig_len
.byte \alt_len
.endm
/*
* Define an alternative between two instructions. If @feature is
* present, early code in apply_alternatives() replaces @oldinstr with
* @newinstr. ".fill" directive takes care of proper instruction padding
* in case @newinstr is longer than @oldinstr.
*/
.macro ALTERNATIVE oldinstr, newinstr, feature
140 :
\oldinstr
141 :
.fill - (((144f-143f)-(141b-140b)) > 0) * ((144f-143f)-(141b-140b)) / 4, 4, 0x03400000
142 :
.pushsection .altinstructions, "a"
altinstruction_entry 140b, 143f, \feature, 142b-140b, 144f-143f
.popsection
.subsection 1
143 :
\newinstr
144 :
.previous
.endm
#define old_len (141b-140b)
#define new_len1 (144f-143f)
#define new_len2 (145f-144f)
#define alt_max_short(a, b) ((a) ^ (((a) ^ (b)) & -(-((a) < (b)))))
/*
* Same as ALTERNATIVE macro above but for two alternatives. If CPU
* has @feature1, it replaces @oldinstr with @newinstr1. If CPU has
* @feature2, it replaces @oldinstr with @feature2.
*/
.macro ALTERNATIVE_2 oldinstr, newinstr1, feature1, newinstr2, feature2
140 :
\oldinstr
141 :
.fill - ((alt_max_short(new_len1, new_len2) - (old_len)) > 0) * \
(alt_max_short(new_len1, new_len2) - (old_len)) / 4, 4, 0x03400000
142 :
.pushsection .altinstructions, "a"
altinstruction_entry 140b, 143f, \feature1, 142b-140b, 144f-143f, 142b-141b
altinstruction_entry 140b, 144f, \feature2, 142b-140b, 145f-144f, 142b-141b
.popsection
.subsection 1
143 :
\newinstr1
144 :
\newinstr2
145 :
.previous
.endm
#endif /* __ASSEMBLER__ */
#endif /* _ASM_ALTERNATIVE_ASM_H */
@@ -0,0 +1,111 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_ALTERNATIVE_H
#define _ASM_ALTERNATIVE_H
#ifndef __ASSEMBLER__
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/stringify.h>
#include <asm/asm.h>
struct alt_instr {
s32 instr_offset; /* offset to original instruction */
s32 replace_offset; /* offset to replacement instruction */
u16 feature; /* feature bit set for replacement */
u8 instrlen; /* length of original instruction */
u8 replacementlen; /* length of new instruction */
} __packed;
/*
* Debug flag that can be tested to see whether alternative
* instructions were patched in already:
*/
extern int alternatives_patched;
extern struct alt_instr __alt_instructions[], __alt_instructions_end[];
extern void alternative_instructions(void);
extern void apply_alternatives(struct alt_instr *start, struct alt_instr *end);
#define b_replacement(num) "664"#num
#define e_replacement(num) "665"#num
#define alt_end_marker "663"
#define alt_slen "662b-661b"
#define alt_total_slen alt_end_marker"b-661b"
#define alt_rlen(num) e_replacement(num)"f-"b_replacement(num)"f"
#define __OLDINSTR(oldinstr, num) \
"661:\n\t" oldinstr "\n662:\n" \
".fill -(((" alt_rlen(num) ")-(" alt_slen ")) > 0) * " \
"((" alt_rlen(num) ")-(" alt_slen ")) / 4, 4, 0x03400000\n"
#define OLDINSTR(oldinstr, num) \
__OLDINSTR(oldinstr, num) \
alt_end_marker ":\n"
#define alt_max_short(a, b) "((" a ") ^ (((" a ") ^ (" b ")) & -(-((" a ") < (" b ")))))"
/*
* Pad the second replacement alternative with additional NOPs if it is
* additionally longer than the first replacement alternative.
*/
#define OLDINSTR_2(oldinstr, num1, num2) \
"661:\n\t" oldinstr "\n662:\n" \
".fill -((" alt_max_short(alt_rlen(num1), alt_rlen(num2)) " - (" alt_slen ")) > 0) * " \
"(" alt_max_short(alt_rlen(num1), alt_rlen(num2)) " - (" alt_slen ")) / 4, " \
"4, 0x03400000\n" \
alt_end_marker ":\n"
#define ALTINSTR_ENTRY(feature, num) \
" .long 661b - .\n" /* label */ \
" .long " b_replacement(num)"f - .\n" /* new instruction */ \
" .short " __stringify(feature) "\n" /* feature bit */ \
" .byte " alt_total_slen "\n" /* source len */ \
" .byte " alt_rlen(num) "\n" /* replacement len */
#define ALTINSTR_REPLACEMENT(newinstr, feature, num) /* replacement */ \
b_replacement(num)":\n\t" newinstr "\n" e_replacement(num) ":\n\t"
/* alternative assembly primitive: */
#define ALTERNATIVE(oldinstr, newinstr, feature) \
OLDINSTR(oldinstr, 1) \
".pushsection .altinstructions,\"a\"\n" \
ALTINSTR_ENTRY(feature, 1) \
".popsection\n" \
".subsection 1\n" \
ALTINSTR_REPLACEMENT(newinstr, feature, 1) \
".previous\n"
#define ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2)\
OLDINSTR_2(oldinstr, 1, 2) \
".pushsection .altinstructions,\"a\"\n" \
ALTINSTR_ENTRY(feature1, 1) \
ALTINSTR_ENTRY(feature2, 2) \
".popsection\n" \
".subsection 1\n" \
ALTINSTR_REPLACEMENT(newinstr1, feature1, 1) \
ALTINSTR_REPLACEMENT(newinstr2, feature2, 2) \
".previous\n"
/*
* Alternative instructions for different CPU types or capabilities.
*
* This allows to use optimized instructions even on generic binary
* kernels.
*
* length of oldinstr must be longer or equal the length of newinstr
* It can be padded with nops as needed.
*
* For non barrier like inlines please define new variants
* without volatile and memory clobber.
*/
#define alternative(oldinstr, newinstr, feature) \
(asm volatile (ALTERNATIVE(oldinstr, newinstr, feature) : : : "memory"))
#define alternative_2(oldinstr, newinstr1, feature1, newinstr2, feature2) \
(asm volatile(ALTERNATIVE_2(oldinstr, newinstr1, feature1, newinstr2, feature2) ::: "memory"))
#endif /* __ASSEMBLER__ */
#endif /* _ASM_ALTERNATIVE_H */
@@ -0,0 +1,65 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __ASM_ASM_EXTABLE_H
#define __ASM_ASM_EXTABLE_H
#define EX_TYPE_NONE 0
#define EX_TYPE_FIXUP 1
#define EX_TYPE_UACCESS_ERR_ZERO 2
#define EX_TYPE_BPF 3
#ifdef __ASSEMBLER__
#define __ASM_EXTABLE_RAW(insn, fixup, type, data) \
.pushsection __ex_table, "a"; \
.balign 4; \
.long ((insn) - .); \
.long ((fixup) - .); \
.short (type); \
.short (data); \
.popsection;
.macro _asm_extable, insn, fixup
__ASM_EXTABLE_RAW(\insn, \fixup, EX_TYPE_FIXUP, 0)
.endm
#else /* __ASSEMBLER__ */
#include <linux/bits.h>
#include <linux/stringify.h>
#include <asm/gpr-num.h>
#define __ASM_EXTABLE_RAW(insn, fixup, type, data) \
".pushsection __ex_table, \"a\"\n" \
".balign 4\n" \
".long ((" insn ") - .)\n" \
".long ((" fixup ") - .)\n" \
".short (" type ")\n" \
".short (" data ")\n" \
".popsection\n"
#define _ASM_EXTABLE(insn, fixup) \
__ASM_EXTABLE_RAW(#insn, #fixup, __stringify(EX_TYPE_FIXUP), "0")
#define EX_DATA_REG_ERR_SHIFT 0
#define EX_DATA_REG_ERR GENMASK(4, 0)
#define EX_DATA_REG_ZERO_SHIFT 5
#define EX_DATA_REG_ZERO GENMASK(9, 5)
#define EX_DATA_REG(reg, gpr) \
"((.L__gpr_num_" #gpr ") << " __stringify(EX_DATA_REG_##reg##_SHIFT) ")"
#define _ASM_EXTABLE_UACCESS_ERR_ZERO(insn, fixup, err, zero) \
__DEFINE_ASM_GPR_NUMS \
__ASM_EXTABLE_RAW(#insn, #fixup, \
__stringify(EX_TYPE_UACCESS_ERR_ZERO), \
"(" \
EX_DATA_REG(ERR, err) " | " \
EX_DATA_REG(ZERO, zero) \
")")
#define _ASM_EXTABLE_UACCESS_ERR(insn, fixup, err) \
_ASM_EXTABLE_UACCESS_ERR_ZERO(insn, fixup, err, zero)
#endif /* __ASSEMBLER__ */
#endif /* __ASM_ASM_EXTABLE_H */
@@ -0,0 +1,5 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#include <generated/asm-offsets.h>
@@ -0,0 +1,42 @@
/* SPDX-License-Identifier: GPL-2.0 */
#include <linux/uaccess.h>
#include <asm/fpu.h>
#include <asm/lbt.h>
#include <asm/mmu_context.h>
#include <asm/page.h>
#include <asm/ftrace.h>
#include <asm-generic/asm-prototypes.h>
#ifdef CONFIG_ARCH_SUPPORTS_INT128
__int128_t __ashlti3(__int128_t a, int b);
__int128_t __ashrti3(__int128_t a, int b);
__int128_t __lshrti3(__int128_t a, int b);
#endif
asmlinkage void noinstr __no_stack_protector ret_from_fork(struct task_struct *prev,
struct pt_regs *regs);
asmlinkage void noinstr __no_stack_protector ret_from_kernel_thread(struct task_struct *prev,
struct pt_regs *regs,
int (*fn)(void *),
void *fn_arg);
struct kvm_run;
struct kvm_vcpu;
struct loongarch_fpu;
void kvm_exc_entry(void);
int kvm_enter_guest(struct kvm_run *run, struct kvm_vcpu *vcpu);
void kvm_save_fpu(struct loongarch_fpu *fpu);
void kvm_restore_fpu(struct loongarch_fpu *fpu);
#ifdef CONFIG_CPU_HAS_LSX
void kvm_save_lsx(struct loongarch_fpu *fpu);
void kvm_restore_lsx(struct loongarch_fpu *fpu);
#endif
#ifdef CONFIG_CPU_HAS_LASX
void kvm_save_lasx(struct loongarch_fpu *fpu);
void kvm_restore_lasx(struct loongarch_fpu *fpu);
#endif
@@ -0,0 +1,238 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Some useful macros for LoongArch assembler code
*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*
* Derived from MIPS:
* Copyright (C) 1995, 1996, 1997, 1999, 2001 by Ralf Baechle
* Copyright (C) 1999 by Silicon Graphics, Inc.
* Copyright (C) 2001 MIPS Technologies, Inc.
* Copyright (C) 2002 Maciej W. Rozycki
*/
#ifndef __ASM_ASM_H
#define __ASM_ASM_H
/* LoongArch pref instruction. */
#ifdef CONFIG_CPU_HAS_PREFETCH
#define PREF(hint, addr, offs) \
preld hint, addr, offs; \
#define PREFX(hint, addr, index) \
preldx hint, addr, index; \
#else /* !CONFIG_CPU_HAS_PREFETCH */
#define PREF(hint, addr, offs)
#define PREFX(hint, addr, index)
#endif /* !CONFIG_CPU_HAS_PREFETCH */
/*
* Stack alignment
*/
#define STACK_ALIGN ~(0xf)
/*
* Macros to handle different pointer/register sizes for 32/64-bit code
*/
/*
* Size of a register
*/
#ifndef __loongarch64
#define SZREG 4
#else
#define SZREG 8
#endif
/*
* Use the following macros in assemblercode to load/store registers,
* pointers etc.
*/
#if (SZREG == 4)
#define REG_L ld.w
#define REG_S st.w
#define REG_ADD add.w
#define REG_SUB sub.w
#else /* SZREG == 8 */
#define REG_L ld.d
#define REG_S st.d
#define REG_ADD add.d
#define REG_SUB sub.d
#endif
/*
* How to add/sub/load/store/shift C int variables.
*/
#if (__SIZEOF_INT__ == 4)
#define INT_ADD add.w
#define INT_ADDI addi.w
#define INT_SUB sub.w
#define INT_L ld.w
#define INT_S st.w
#define INT_SLLI slli.w
#define INT_SLLV sll.w
#define INT_SRLI srli.w
#define INT_SRLV srl.w
#define INT_SRAI srai.w
#define INT_SRAV sra.w
#endif
#if (__SIZEOF_INT__ == 8)
#define INT_ADD add.d
#define INT_ADDI addi.d
#define INT_SUB sub.d
#define INT_L ld.d
#define INT_S st.d
#define INT_SLLI slli.d
#define INT_SLLV sll.d
#define INT_SRLI srli.d
#define INT_SRLV srl.d
#define INT_SRAI srai.d
#define INT_SRAV sra.d
#endif
/*
* How to add/sub/load/store/shift C long variables.
*/
#if (__SIZEOF_LONG__ == 4)
#define LONG_ADD add.w
#define LONG_ADDI addi.w
#define LONG_ALSL alsl.w
#define LONG_BSTRINS bstrins.w
#define LONG_BSTRPICK bstrpick.w
#define LONG_SUB sub.w
#define LONG_L ld.w
#define LONG_LI li.w
#define LONG_LPTR ld.w
#define LONG_S st.w
#define LONG_SPTR st.w
#define LONG_SLLI slli.w
#define LONG_SLLV sll.w
#define LONG_SRLI srli.w
#define LONG_SRLV srl.w
#define LONG_SRAI srai.w
#define LONG_SRAV sra.w
#define LONG_ROTR rotr.w
#define LONG_ROTRI rotri.w
#ifdef __ASSEMBLER__
#define LONG .word
#endif
#define LONGSIZE 4
#define LONGMASK 3
#define LONGLOG 2
#endif
#if (__SIZEOF_LONG__ == 8)
#define LONG_ADD add.d
#define LONG_ADDI addi.d
#define LONG_ALSL alsl.d
#define LONG_BSTRINS bstrins.d
#define LONG_BSTRPICK bstrpick.d
#define LONG_SUB sub.d
#define LONG_L ld.d
#define LONG_LI li.d
#define LONG_LPTR ldptr.d
#define LONG_S st.d
#define LONG_SPTR stptr.d
#define LONG_SLLI slli.d
#define LONG_SLLV sll.d
#define LONG_SRLI srli.d
#define LONG_SRLV srl.d
#define LONG_SRAI srai.d
#define LONG_SRAV sra.d
#define LONG_ROTR rotr.d
#define LONG_ROTRI rotri.d
#ifdef __ASSEMBLER__
#define LONG .dword
#endif
#define LONGSIZE 8
#define LONGMASK 7
#define LONGLOG 3
#endif
/*
* How to add/sub/load/store/shift pointers.
*/
#if (__SIZEOF_POINTER__ == 4)
#define PTR_ADD add.w
#define PTR_ADDI addi.w
#define PTR_ALSL alsl.w
#define PTR_BSTRINS bstrins.w
#define PTR_BSTRPICK bstrpick.w
#define PTR_SUB sub.w
#define PTR_L ld.w
#define PTR_LI li.w
#define PTR_LPTR ld.w
#define PTR_S st.w
#define PTR_SPTR st.w
#define PTR_SLLI slli.w
#define PTR_SLLV sll.w
#define PTR_SRLI srli.w
#define PTR_SRLV srl.w
#define PTR_SRAI srai.w
#define PTR_SRAV sra.w
#define PTR_ROTR rotr.w
#define PTR_ROTRI rotri.w
#define PTR_SCALESHIFT 2
#ifdef __ASSEMBLER__
#define PTR .word
#endif
#define PTRSIZE 4
#define PTRLOG 2
#endif
#if (__SIZEOF_POINTER__ == 8)
#define PTR_ADD add.d
#define PTR_ADDI addi.d
#define PTR_ALSL alsl.d
#define PTR_BSTRINS bstrins.d
#define PTR_BSTRPICK bstrpick.d
#define PTR_SUB sub.d
#define PTR_L ld.d
#define PTR_LI li.d
#define PTR_LPTR ldptr.d
#define PTR_S st.d
#define PTR_SPTR stptr.d
#define PTR_SLLI slli.d
#define PTR_SLLV sll.d
#define PTR_SRLI srli.d
#define PTR_SRLV srl.d
#define PTR_SRAI srai.d
#define PTR_SRAV sra.d
#define PTR_ROTR rotr.d
#define PTR_ROTRI rotri.d
#define PTR_SCALESHIFT 3
#ifdef __ASSEMBLER__
#define PTR .dword
#endif
#define PTRSIZE 8
#define PTRLOG 3
#endif
/* Annotate a function as being unsuitable for kprobes. */
#ifdef CONFIG_KPROBES
#ifdef CONFIG_32BIT
#define _ASM_NOKPROBE(name) \
.pushsection "_kprobe_blacklist", "aw"; \
.long name; \
.popsection
#else
#define _ASM_NOKPROBE(name) \
.pushsection "_kprobe_blacklist", "aw"; \
.quad name; \
.popsection
#endif
#else
#define _ASM_NOKPROBE(name)
#endif
#endif /* __ASM_ASM_H */
@@ -0,0 +1,681 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_ASMMACRO_H
#define _ASM_ASMMACRO_H
#include <linux/sizes.h>
#include <asm/asm-offsets.h>
#include <asm/regdef.h>
#include <asm/fpregdef.h>
#include <asm/loongarch.h>
#ifdef CONFIG_64BIT
#define TASK_STRUCT_OFFSET 0
#else
#define TASK_STRUCT_OFFSET 2040
#endif
.macro cpu_save_nonscratch thread
LONG_SPTR s0, \thread, (THREAD_REG23 - TASK_STRUCT_OFFSET)
LONG_SPTR s1, \thread, (THREAD_REG24 - TASK_STRUCT_OFFSET)
LONG_SPTR s2, \thread, (THREAD_REG25 - TASK_STRUCT_OFFSET)
LONG_SPTR s3, \thread, (THREAD_REG26 - TASK_STRUCT_OFFSET)
LONG_SPTR s4, \thread, (THREAD_REG27 - TASK_STRUCT_OFFSET)
LONG_SPTR s5, \thread, (THREAD_REG28 - TASK_STRUCT_OFFSET)
LONG_SPTR s6, \thread, (THREAD_REG29 - TASK_STRUCT_OFFSET)
LONG_SPTR s7, \thread, (THREAD_REG30 - TASK_STRUCT_OFFSET)
LONG_SPTR s8, \thread, (THREAD_REG31 - TASK_STRUCT_OFFSET)
LONG_SPTR ra, \thread, (THREAD_REG01 - TASK_STRUCT_OFFSET)
LONG_SPTR sp, \thread, (THREAD_REG03 - TASK_STRUCT_OFFSET)
LONG_SPTR fp, \thread, (THREAD_REG22 - TASK_STRUCT_OFFSET)
.endm
.macro cpu_restore_nonscratch thread
LONG_LPTR s0, \thread, (THREAD_REG23 - TASK_STRUCT_OFFSET)
LONG_LPTR s1, \thread, (THREAD_REG24 - TASK_STRUCT_OFFSET)
LONG_LPTR s2, \thread, (THREAD_REG25 - TASK_STRUCT_OFFSET)
LONG_LPTR s3, \thread, (THREAD_REG26 - TASK_STRUCT_OFFSET)
LONG_LPTR s4, \thread, (THREAD_REG27 - TASK_STRUCT_OFFSET)
LONG_LPTR s5, \thread, (THREAD_REG28 - TASK_STRUCT_OFFSET)
LONG_LPTR s6, \thread, (THREAD_REG29 - TASK_STRUCT_OFFSET)
LONG_LPTR s7, \thread, (THREAD_REG30 - TASK_STRUCT_OFFSET)
LONG_LPTR s8, \thread, (THREAD_REG31 - TASK_STRUCT_OFFSET)
LONG_LPTR ra, \thread, (THREAD_REG01 - TASK_STRUCT_OFFSET)
LONG_LPTR sp, \thread, (THREAD_REG03 - TASK_STRUCT_OFFSET)
LONG_LPTR fp, \thread, (THREAD_REG22 - TASK_STRUCT_OFFSET)
.endm
.macro fpu_save_csr thread tmp
movfcsr2gr \tmp, fcsr0
#ifdef CONFIG_32BIT
st.w \tmp, \thread, THREAD_FCSR
#else
stptr.w \tmp, \thread, THREAD_FCSR
#endif
#ifdef CONFIG_CPU_HAS_LBT
/* TM bit is always 0 if LBT not supported */
andi \tmp, \tmp, FPU_CSR_TM
beqz \tmp, 1f
/* Save FTOP */
x86mftop \tmp
stptr.w \tmp, \thread, THREAD_FTOP
/* Turn off TM to ensure the order of FPR in memory independent of TM */
x86clrtm
1:
#endif
.endm
.macro fpu_restore_csr thread tmp0 tmp1
#ifdef CONFIG_32BIT
ld.w \tmp0, \thread, THREAD_FCSR
#else
ldptr.w \tmp0, \thread, THREAD_FCSR
#endif
movgr2fcsr fcsr0, \tmp0
#ifdef CONFIG_CPU_HAS_LBT
/* TM bit is always 0 if LBT not supported */
andi \tmp0, \tmp0, FPU_CSR_TM
beqz \tmp0, 2f
/* Restore FTOP */
ldptr.w \tmp0, \thread, THREAD_FTOP
andi \tmp0, \tmp0, 0x7
la.pcrel \tmp1, 1f
alsl.d \tmp1, \tmp0, \tmp1, 3
jr \tmp1
1:
x86mttop 0
b 2f
x86mttop 1
b 2f
x86mttop 2
b 2f
x86mttop 3
b 2f
x86mttop 4
b 2f
x86mttop 5
b 2f
x86mttop 6
b 2f
x86mttop 7
2:
#endif
.endm
#ifdef CONFIG_32BIT
.macro fpu_save_cc thread tmp0 tmp1
movcf2gr \tmp0, $fcc0
move \tmp1, \tmp0
movcf2gr \tmp0, $fcc1
bstrins.w \tmp1, \tmp0, 15, 8
movcf2gr \tmp0, $fcc2
bstrins.w \tmp1, \tmp0, 23, 16
movcf2gr \tmp0, $fcc3
bstrins.w \tmp1, \tmp0, 31, 24
st.w \tmp1, \thread, THREAD_FCC
movcf2gr \tmp0, $fcc4
move \tmp1, \tmp0
movcf2gr \tmp0, $fcc5
bstrins.w \tmp1, \tmp0, 15, 8
movcf2gr \tmp0, $fcc6
bstrins.w \tmp1, \tmp0, 23, 16
movcf2gr \tmp0, $fcc7
bstrins.w \tmp1, \tmp0, 31, 24
st.w \tmp1, \thread, (THREAD_FCC + 4)
.endm
.macro fpu_restore_cc thread tmp0 tmp1
ld.w \tmp0, \thread, THREAD_FCC
bstrpick.w \tmp1, \tmp0, 7, 0
movgr2cf $fcc0, \tmp1
bstrpick.w \tmp1, \tmp0, 15, 8
movgr2cf $fcc1, \tmp1
bstrpick.w \tmp1, \tmp0, 23, 16
movgr2cf $fcc2, \tmp1
bstrpick.w \tmp1, \tmp0, 31, 24
movgr2cf $fcc3, \tmp1
ld.w \tmp0, \thread, (THREAD_FCC + 4)
bstrpick.w \tmp1, \tmp0, 7, 0
movgr2cf $fcc4, \tmp1
bstrpick.w \tmp1, \tmp0, 15, 8
movgr2cf $fcc5, \tmp1
bstrpick.w \tmp1, \tmp0, 23, 16
movgr2cf $fcc6, \tmp1
bstrpick.w \tmp1, \tmp0, 31, 24
movgr2cf $fcc7, \tmp1
.endm
#else
.macro fpu_save_cc thread tmp0 tmp1
movcf2gr \tmp0, $fcc0
move \tmp1, \tmp0
movcf2gr \tmp0, $fcc1
bstrins.d \tmp1, \tmp0, 15, 8
movcf2gr \tmp0, $fcc2
bstrins.d \tmp1, \tmp0, 23, 16
movcf2gr \tmp0, $fcc3
bstrins.d \tmp1, \tmp0, 31, 24
movcf2gr \tmp0, $fcc4
bstrins.d \tmp1, \tmp0, 39, 32
movcf2gr \tmp0, $fcc5
bstrins.d \tmp1, \tmp0, 47, 40
movcf2gr \tmp0, $fcc6
bstrins.d \tmp1, \tmp0, 55, 48
movcf2gr \tmp0, $fcc7
bstrins.d \tmp1, \tmp0, 63, 56
stptr.d \tmp1, \thread, THREAD_FCC
.endm
.macro fpu_restore_cc thread tmp0 tmp1
ldptr.d \tmp0, \thread, THREAD_FCC
bstrpick.d \tmp1, \tmp0, 7, 0
movgr2cf $fcc0, \tmp1
bstrpick.d \tmp1, \tmp0, 15, 8
movgr2cf $fcc1, \tmp1
bstrpick.d \tmp1, \tmp0, 23, 16
movgr2cf $fcc2, \tmp1
bstrpick.d \tmp1, \tmp0, 31, 24
movgr2cf $fcc3, \tmp1
bstrpick.d \tmp1, \tmp0, 39, 32
movgr2cf $fcc4, \tmp1
bstrpick.d \tmp1, \tmp0, 47, 40
movgr2cf $fcc5, \tmp1
bstrpick.d \tmp1, \tmp0, 55, 48
movgr2cf $fcc6, \tmp1
bstrpick.d \tmp1, \tmp0, 63, 56
movgr2cf $fcc7, \tmp1
.endm
#endif
.macro fpu_save_double thread tmp
li.w \tmp, THREAD_FPR0
PTR_ADD \tmp, \tmp, \thread
fst.d $f0, \tmp, THREAD_FPR0 - THREAD_FPR0
fst.d $f1, \tmp, THREAD_FPR1 - THREAD_FPR0
fst.d $f2, \tmp, THREAD_FPR2 - THREAD_FPR0
fst.d $f3, \tmp, THREAD_FPR3 - THREAD_FPR0
fst.d $f4, \tmp, THREAD_FPR4 - THREAD_FPR0
fst.d $f5, \tmp, THREAD_FPR5 - THREAD_FPR0
fst.d $f6, \tmp, THREAD_FPR6 - THREAD_FPR0
fst.d $f7, \tmp, THREAD_FPR7 - THREAD_FPR0
fst.d $f8, \tmp, THREAD_FPR8 - THREAD_FPR0
fst.d $f9, \tmp, THREAD_FPR9 - THREAD_FPR0
fst.d $f10, \tmp, THREAD_FPR10 - THREAD_FPR0
fst.d $f11, \tmp, THREAD_FPR11 - THREAD_FPR0
fst.d $f12, \tmp, THREAD_FPR12 - THREAD_FPR0
fst.d $f13, \tmp, THREAD_FPR13 - THREAD_FPR0
fst.d $f14, \tmp, THREAD_FPR14 - THREAD_FPR0
fst.d $f15, \tmp, THREAD_FPR15 - THREAD_FPR0
fst.d $f16, \tmp, THREAD_FPR16 - THREAD_FPR0
fst.d $f17, \tmp, THREAD_FPR17 - THREAD_FPR0
fst.d $f18, \tmp, THREAD_FPR18 - THREAD_FPR0
fst.d $f19, \tmp, THREAD_FPR19 - THREAD_FPR0
fst.d $f20, \tmp, THREAD_FPR20 - THREAD_FPR0
fst.d $f21, \tmp, THREAD_FPR21 - THREAD_FPR0
fst.d $f22, \tmp, THREAD_FPR22 - THREAD_FPR0
fst.d $f23, \tmp, THREAD_FPR23 - THREAD_FPR0
fst.d $f24, \tmp, THREAD_FPR24 - THREAD_FPR0
fst.d $f25, \tmp, THREAD_FPR25 - THREAD_FPR0
fst.d $f26, \tmp, THREAD_FPR26 - THREAD_FPR0
fst.d $f27, \tmp, THREAD_FPR27 - THREAD_FPR0
fst.d $f28, \tmp, THREAD_FPR28 - THREAD_FPR0
fst.d $f29, \tmp, THREAD_FPR29 - THREAD_FPR0
fst.d $f30, \tmp, THREAD_FPR30 - THREAD_FPR0
fst.d $f31, \tmp, THREAD_FPR31 - THREAD_FPR0
.endm
.macro fpu_restore_double thread tmp
li.w \tmp, THREAD_FPR0
PTR_ADD \tmp, \tmp, \thread
fld.d $f0, \tmp, THREAD_FPR0 - THREAD_FPR0
fld.d $f1, \tmp, THREAD_FPR1 - THREAD_FPR0
fld.d $f2, \tmp, THREAD_FPR2 - THREAD_FPR0
fld.d $f3, \tmp, THREAD_FPR3 - THREAD_FPR0
fld.d $f4, \tmp, THREAD_FPR4 - THREAD_FPR0
fld.d $f5, \tmp, THREAD_FPR5 - THREAD_FPR0
fld.d $f6, \tmp, THREAD_FPR6 - THREAD_FPR0
fld.d $f7, \tmp, THREAD_FPR7 - THREAD_FPR0
fld.d $f8, \tmp, THREAD_FPR8 - THREAD_FPR0
fld.d $f9, \tmp, THREAD_FPR9 - THREAD_FPR0
fld.d $f10, \tmp, THREAD_FPR10 - THREAD_FPR0
fld.d $f11, \tmp, THREAD_FPR11 - THREAD_FPR0
fld.d $f12, \tmp, THREAD_FPR12 - THREAD_FPR0
fld.d $f13, \tmp, THREAD_FPR13 - THREAD_FPR0
fld.d $f14, \tmp, THREAD_FPR14 - THREAD_FPR0
fld.d $f15, \tmp, THREAD_FPR15 - THREAD_FPR0
fld.d $f16, \tmp, THREAD_FPR16 - THREAD_FPR0
fld.d $f17, \tmp, THREAD_FPR17 - THREAD_FPR0
fld.d $f18, \tmp, THREAD_FPR18 - THREAD_FPR0
fld.d $f19, \tmp, THREAD_FPR19 - THREAD_FPR0
fld.d $f20, \tmp, THREAD_FPR20 - THREAD_FPR0
fld.d $f21, \tmp, THREAD_FPR21 - THREAD_FPR0
fld.d $f22, \tmp, THREAD_FPR22 - THREAD_FPR0
fld.d $f23, \tmp, THREAD_FPR23 - THREAD_FPR0
fld.d $f24, \tmp, THREAD_FPR24 - THREAD_FPR0
fld.d $f25, \tmp, THREAD_FPR25 - THREAD_FPR0
fld.d $f26, \tmp, THREAD_FPR26 - THREAD_FPR0
fld.d $f27, \tmp, THREAD_FPR27 - THREAD_FPR0
fld.d $f28, \tmp, THREAD_FPR28 - THREAD_FPR0
fld.d $f29, \tmp, THREAD_FPR29 - THREAD_FPR0
fld.d $f30, \tmp, THREAD_FPR30 - THREAD_FPR0
fld.d $f31, \tmp, THREAD_FPR31 - THREAD_FPR0
.endm
.macro lsx_save_data thread tmp
li.w \tmp, THREAD_FPR0
PTR_ADD \tmp, \thread, \tmp
vst $vr0, \tmp, THREAD_FPR0 - THREAD_FPR0
vst $vr1, \tmp, THREAD_FPR1 - THREAD_FPR0
vst $vr2, \tmp, THREAD_FPR2 - THREAD_FPR0
vst $vr3, \tmp, THREAD_FPR3 - THREAD_FPR0
vst $vr4, \tmp, THREAD_FPR4 - THREAD_FPR0
vst $vr5, \tmp, THREAD_FPR5 - THREAD_FPR0
vst $vr6, \tmp, THREAD_FPR6 - THREAD_FPR0
vst $vr7, \tmp, THREAD_FPR7 - THREAD_FPR0
vst $vr8, \tmp, THREAD_FPR8 - THREAD_FPR0
vst $vr9, \tmp, THREAD_FPR9 - THREAD_FPR0
vst $vr10, \tmp, THREAD_FPR10 - THREAD_FPR0
vst $vr11, \tmp, THREAD_FPR11 - THREAD_FPR0
vst $vr12, \tmp, THREAD_FPR12 - THREAD_FPR0
vst $vr13, \tmp, THREAD_FPR13 - THREAD_FPR0
vst $vr14, \tmp, THREAD_FPR14 - THREAD_FPR0
vst $vr15, \tmp, THREAD_FPR15 - THREAD_FPR0
vst $vr16, \tmp, THREAD_FPR16 - THREAD_FPR0
vst $vr17, \tmp, THREAD_FPR17 - THREAD_FPR0
vst $vr18, \tmp, THREAD_FPR18 - THREAD_FPR0
vst $vr19, \tmp, THREAD_FPR19 - THREAD_FPR0
vst $vr20, \tmp, THREAD_FPR20 - THREAD_FPR0
vst $vr21, \tmp, THREAD_FPR21 - THREAD_FPR0
vst $vr22, \tmp, THREAD_FPR22 - THREAD_FPR0
vst $vr23, \tmp, THREAD_FPR23 - THREAD_FPR0
vst $vr24, \tmp, THREAD_FPR24 - THREAD_FPR0
vst $vr25, \tmp, THREAD_FPR25 - THREAD_FPR0
vst $vr26, \tmp, THREAD_FPR26 - THREAD_FPR0
vst $vr27, \tmp, THREAD_FPR27 - THREAD_FPR0
vst $vr28, \tmp, THREAD_FPR28 - THREAD_FPR0
vst $vr29, \tmp, THREAD_FPR29 - THREAD_FPR0
vst $vr30, \tmp, THREAD_FPR30 - THREAD_FPR0
vst $vr31, \tmp, THREAD_FPR31 - THREAD_FPR0
.endm
.macro lsx_restore_data thread tmp
li.w \tmp, THREAD_FPR0
PTR_ADD \tmp, \thread, \tmp
vld $vr0, \tmp, THREAD_FPR0 - THREAD_FPR0
vld $vr1, \tmp, THREAD_FPR1 - THREAD_FPR0
vld $vr2, \tmp, THREAD_FPR2 - THREAD_FPR0
vld $vr3, \tmp, THREAD_FPR3 - THREAD_FPR0
vld $vr4, \tmp, THREAD_FPR4 - THREAD_FPR0
vld $vr5, \tmp, THREAD_FPR5 - THREAD_FPR0
vld $vr6, \tmp, THREAD_FPR6 - THREAD_FPR0
vld $vr7, \tmp, THREAD_FPR7 - THREAD_FPR0
vld $vr8, \tmp, THREAD_FPR8 - THREAD_FPR0
vld $vr9, \tmp, THREAD_FPR9 - THREAD_FPR0
vld $vr10, \tmp, THREAD_FPR10 - THREAD_FPR0
vld $vr11, \tmp, THREAD_FPR11 - THREAD_FPR0
vld $vr12, \tmp, THREAD_FPR12 - THREAD_FPR0
vld $vr13, \tmp, THREAD_FPR13 - THREAD_FPR0
vld $vr14, \tmp, THREAD_FPR14 - THREAD_FPR0
vld $vr15, \tmp, THREAD_FPR15 - THREAD_FPR0
vld $vr16, \tmp, THREAD_FPR16 - THREAD_FPR0
vld $vr17, \tmp, THREAD_FPR17 - THREAD_FPR0
vld $vr18, \tmp, THREAD_FPR18 - THREAD_FPR0
vld $vr19, \tmp, THREAD_FPR19 - THREAD_FPR0
vld $vr20, \tmp, THREAD_FPR20 - THREAD_FPR0
vld $vr21, \tmp, THREAD_FPR21 - THREAD_FPR0
vld $vr22, \tmp, THREAD_FPR22 - THREAD_FPR0
vld $vr23, \tmp, THREAD_FPR23 - THREAD_FPR0
vld $vr24, \tmp, THREAD_FPR24 - THREAD_FPR0
vld $vr25, \tmp, THREAD_FPR25 - THREAD_FPR0
vld $vr26, \tmp, THREAD_FPR26 - THREAD_FPR0
vld $vr27, \tmp, THREAD_FPR27 - THREAD_FPR0
vld $vr28, \tmp, THREAD_FPR28 - THREAD_FPR0
vld $vr29, \tmp, THREAD_FPR29 - THREAD_FPR0
vld $vr30, \tmp, THREAD_FPR30 - THREAD_FPR0
vld $vr31, \tmp, THREAD_FPR31 - THREAD_FPR0
.endm
.macro lsx_save_all thread tmp0 tmp1
fpu_save_cc \thread, \tmp0, \tmp1
fpu_save_csr \thread, \tmp0
lsx_save_data \thread, \tmp0
.endm
.macro lsx_restore_all thread tmp0 tmp1
lsx_restore_data \thread, \tmp0
fpu_restore_cc \thread, \tmp0, \tmp1
fpu_restore_csr \thread, \tmp0, \tmp1
.endm
.macro lsx_save_upper vd base tmp off
vpickve2gr.d \tmp, \vd, 1
st.d \tmp, \base, (\off+8)
.endm
.macro lsx_save_all_upper thread base tmp
li.w \tmp, THREAD_FPR0
PTR_ADD \base, \thread, \tmp
lsx_save_upper $vr0, \base, \tmp, (THREAD_FPR0-THREAD_FPR0)
lsx_save_upper $vr1, \base, \tmp, (THREAD_FPR1-THREAD_FPR0)
lsx_save_upper $vr2, \base, \tmp, (THREAD_FPR2-THREAD_FPR0)
lsx_save_upper $vr3, \base, \tmp, (THREAD_FPR3-THREAD_FPR0)
lsx_save_upper $vr4, \base, \tmp, (THREAD_FPR4-THREAD_FPR0)
lsx_save_upper $vr5, \base, \tmp, (THREAD_FPR5-THREAD_FPR0)
lsx_save_upper $vr6, \base, \tmp, (THREAD_FPR6-THREAD_FPR0)
lsx_save_upper $vr7, \base, \tmp, (THREAD_FPR7-THREAD_FPR0)
lsx_save_upper $vr8, \base, \tmp, (THREAD_FPR8-THREAD_FPR0)
lsx_save_upper $vr9, \base, \tmp, (THREAD_FPR9-THREAD_FPR0)
lsx_save_upper $vr10, \base, \tmp, (THREAD_FPR10-THREAD_FPR0)
lsx_save_upper $vr11, \base, \tmp, (THREAD_FPR11-THREAD_FPR0)
lsx_save_upper $vr12, \base, \tmp, (THREAD_FPR12-THREAD_FPR0)
lsx_save_upper $vr13, \base, \tmp, (THREAD_FPR13-THREAD_FPR0)
lsx_save_upper $vr14, \base, \tmp, (THREAD_FPR14-THREAD_FPR0)
lsx_save_upper $vr15, \base, \tmp, (THREAD_FPR15-THREAD_FPR0)
lsx_save_upper $vr16, \base, \tmp, (THREAD_FPR16-THREAD_FPR0)
lsx_save_upper $vr17, \base, \tmp, (THREAD_FPR17-THREAD_FPR0)
lsx_save_upper $vr18, \base, \tmp, (THREAD_FPR18-THREAD_FPR0)
lsx_save_upper $vr19, \base, \tmp, (THREAD_FPR19-THREAD_FPR0)
lsx_save_upper $vr20, \base, \tmp, (THREAD_FPR20-THREAD_FPR0)
lsx_save_upper $vr21, \base, \tmp, (THREAD_FPR21-THREAD_FPR0)
lsx_save_upper $vr22, \base, \tmp, (THREAD_FPR22-THREAD_FPR0)
lsx_save_upper $vr23, \base, \tmp, (THREAD_FPR23-THREAD_FPR0)
lsx_save_upper $vr24, \base, \tmp, (THREAD_FPR24-THREAD_FPR0)
lsx_save_upper $vr25, \base, \tmp, (THREAD_FPR25-THREAD_FPR0)
lsx_save_upper $vr26, \base, \tmp, (THREAD_FPR26-THREAD_FPR0)
lsx_save_upper $vr27, \base, \tmp, (THREAD_FPR27-THREAD_FPR0)
lsx_save_upper $vr28, \base, \tmp, (THREAD_FPR28-THREAD_FPR0)
lsx_save_upper $vr29, \base, \tmp, (THREAD_FPR29-THREAD_FPR0)
lsx_save_upper $vr30, \base, \tmp, (THREAD_FPR30-THREAD_FPR0)
lsx_save_upper $vr31, \base, \tmp, (THREAD_FPR31-THREAD_FPR0)
.endm
.macro lsx_restore_upper vd base tmp off
ld.d \tmp, \base, (\off+8)
vinsgr2vr.d \vd, \tmp, 1
.endm
.macro lsx_restore_all_upper thread base tmp
li.w \tmp, THREAD_FPR0
PTR_ADD \base, \thread, \tmp
lsx_restore_upper $vr0, \base, \tmp, (THREAD_FPR0-THREAD_FPR0)
lsx_restore_upper $vr1, \base, \tmp, (THREAD_FPR1-THREAD_FPR0)
lsx_restore_upper $vr2, \base, \tmp, (THREAD_FPR2-THREAD_FPR0)
lsx_restore_upper $vr3, \base, \tmp, (THREAD_FPR3-THREAD_FPR0)
lsx_restore_upper $vr4, \base, \tmp, (THREAD_FPR4-THREAD_FPR0)
lsx_restore_upper $vr5, \base, \tmp, (THREAD_FPR5-THREAD_FPR0)
lsx_restore_upper $vr6, \base, \tmp, (THREAD_FPR6-THREAD_FPR0)
lsx_restore_upper $vr7, \base, \tmp, (THREAD_FPR7-THREAD_FPR0)
lsx_restore_upper $vr8, \base, \tmp, (THREAD_FPR8-THREAD_FPR0)
lsx_restore_upper $vr9, \base, \tmp, (THREAD_FPR9-THREAD_FPR0)
lsx_restore_upper $vr10, \base, \tmp, (THREAD_FPR10-THREAD_FPR0)
lsx_restore_upper $vr11, \base, \tmp, (THREAD_FPR11-THREAD_FPR0)
lsx_restore_upper $vr12, \base, \tmp, (THREAD_FPR12-THREAD_FPR0)
lsx_restore_upper $vr13, \base, \tmp, (THREAD_FPR13-THREAD_FPR0)
lsx_restore_upper $vr14, \base, \tmp, (THREAD_FPR14-THREAD_FPR0)
lsx_restore_upper $vr15, \base, \tmp, (THREAD_FPR15-THREAD_FPR0)
lsx_restore_upper $vr16, \base, \tmp, (THREAD_FPR16-THREAD_FPR0)
lsx_restore_upper $vr17, \base, \tmp, (THREAD_FPR17-THREAD_FPR0)
lsx_restore_upper $vr18, \base, \tmp, (THREAD_FPR18-THREAD_FPR0)
lsx_restore_upper $vr19, \base, \tmp, (THREAD_FPR19-THREAD_FPR0)
lsx_restore_upper $vr20, \base, \tmp, (THREAD_FPR20-THREAD_FPR0)
lsx_restore_upper $vr21, \base, \tmp, (THREAD_FPR21-THREAD_FPR0)
lsx_restore_upper $vr22, \base, \tmp, (THREAD_FPR22-THREAD_FPR0)
lsx_restore_upper $vr23, \base, \tmp, (THREAD_FPR23-THREAD_FPR0)
lsx_restore_upper $vr24, \base, \tmp, (THREAD_FPR24-THREAD_FPR0)
lsx_restore_upper $vr25, \base, \tmp, (THREAD_FPR25-THREAD_FPR0)
lsx_restore_upper $vr26, \base, \tmp, (THREAD_FPR26-THREAD_FPR0)
lsx_restore_upper $vr27, \base, \tmp, (THREAD_FPR27-THREAD_FPR0)
lsx_restore_upper $vr28, \base, \tmp, (THREAD_FPR28-THREAD_FPR0)
lsx_restore_upper $vr29, \base, \tmp, (THREAD_FPR29-THREAD_FPR0)
lsx_restore_upper $vr30, \base, \tmp, (THREAD_FPR30-THREAD_FPR0)
lsx_restore_upper $vr31, \base, \tmp, (THREAD_FPR31-THREAD_FPR0)
.endm
.macro lsx_init_upper vd tmp
vinsgr2vr.d \vd, \tmp, 1
.endm
.macro lsx_init_all_upper tmp
not \tmp, zero
lsx_init_upper $vr0 \tmp
lsx_init_upper $vr1 \tmp
lsx_init_upper $vr2 \tmp
lsx_init_upper $vr3 \tmp
lsx_init_upper $vr4 \tmp
lsx_init_upper $vr5 \tmp
lsx_init_upper $vr6 \tmp
lsx_init_upper $vr7 \tmp
lsx_init_upper $vr8 \tmp
lsx_init_upper $vr9 \tmp
lsx_init_upper $vr10 \tmp
lsx_init_upper $vr11 \tmp
lsx_init_upper $vr12 \tmp
lsx_init_upper $vr13 \tmp
lsx_init_upper $vr14 \tmp
lsx_init_upper $vr15 \tmp
lsx_init_upper $vr16 \tmp
lsx_init_upper $vr17 \tmp
lsx_init_upper $vr18 \tmp
lsx_init_upper $vr19 \tmp
lsx_init_upper $vr20 \tmp
lsx_init_upper $vr21 \tmp
lsx_init_upper $vr22 \tmp
lsx_init_upper $vr23 \tmp
lsx_init_upper $vr24 \tmp
lsx_init_upper $vr25 \tmp
lsx_init_upper $vr26 \tmp
lsx_init_upper $vr27 \tmp
lsx_init_upper $vr28 \tmp
lsx_init_upper $vr29 \tmp
lsx_init_upper $vr30 \tmp
lsx_init_upper $vr31 \tmp
.endm
.macro lasx_save_data thread tmp
li.w \tmp, THREAD_FPR0
PTR_ADD \tmp, \thread, \tmp
xvst $xr0, \tmp, THREAD_FPR0 - THREAD_FPR0
xvst $xr1, \tmp, THREAD_FPR1 - THREAD_FPR0
xvst $xr2, \tmp, THREAD_FPR2 - THREAD_FPR0
xvst $xr3, \tmp, THREAD_FPR3 - THREAD_FPR0
xvst $xr4, \tmp, THREAD_FPR4 - THREAD_FPR0
xvst $xr5, \tmp, THREAD_FPR5 - THREAD_FPR0
xvst $xr6, \tmp, THREAD_FPR6 - THREAD_FPR0
xvst $xr7, \tmp, THREAD_FPR7 - THREAD_FPR0
xvst $xr8, \tmp, THREAD_FPR8 - THREAD_FPR0
xvst $xr9, \tmp, THREAD_FPR9 - THREAD_FPR0
xvst $xr10, \tmp, THREAD_FPR10 - THREAD_FPR0
xvst $xr11, \tmp, THREAD_FPR11 - THREAD_FPR0
xvst $xr12, \tmp, THREAD_FPR12 - THREAD_FPR0
xvst $xr13, \tmp, THREAD_FPR13 - THREAD_FPR0
xvst $xr14, \tmp, THREAD_FPR14 - THREAD_FPR0
xvst $xr15, \tmp, THREAD_FPR15 - THREAD_FPR0
xvst $xr16, \tmp, THREAD_FPR16 - THREAD_FPR0
xvst $xr17, \tmp, THREAD_FPR17 - THREAD_FPR0
xvst $xr18, \tmp, THREAD_FPR18 - THREAD_FPR0
xvst $xr19, \tmp, THREAD_FPR19 - THREAD_FPR0
xvst $xr20, \tmp, THREAD_FPR20 - THREAD_FPR0
xvst $xr21, \tmp, THREAD_FPR21 - THREAD_FPR0
xvst $xr22, \tmp, THREAD_FPR22 - THREAD_FPR0
xvst $xr23, \tmp, THREAD_FPR23 - THREAD_FPR0
xvst $xr24, \tmp, THREAD_FPR24 - THREAD_FPR0
xvst $xr25, \tmp, THREAD_FPR25 - THREAD_FPR0
xvst $xr26, \tmp, THREAD_FPR26 - THREAD_FPR0
xvst $xr27, \tmp, THREAD_FPR27 - THREAD_FPR0
xvst $xr28, \tmp, THREAD_FPR28 - THREAD_FPR0
xvst $xr29, \tmp, THREAD_FPR29 - THREAD_FPR0
xvst $xr30, \tmp, THREAD_FPR30 - THREAD_FPR0
xvst $xr31, \tmp, THREAD_FPR31 - THREAD_FPR0
.endm
.macro lasx_restore_data thread tmp
li.w \tmp, THREAD_FPR0
PTR_ADD \tmp, \thread, \tmp
xvld $xr0, \tmp, THREAD_FPR0 - THREAD_FPR0
xvld $xr1, \tmp, THREAD_FPR1 - THREAD_FPR0
xvld $xr2, \tmp, THREAD_FPR2 - THREAD_FPR0
xvld $xr3, \tmp, THREAD_FPR3 - THREAD_FPR0
xvld $xr4, \tmp, THREAD_FPR4 - THREAD_FPR0
xvld $xr5, \tmp, THREAD_FPR5 - THREAD_FPR0
xvld $xr6, \tmp, THREAD_FPR6 - THREAD_FPR0
xvld $xr7, \tmp, THREAD_FPR7 - THREAD_FPR0
xvld $xr8, \tmp, THREAD_FPR8 - THREAD_FPR0
xvld $xr9, \tmp, THREAD_FPR9 - THREAD_FPR0
xvld $xr10, \tmp, THREAD_FPR10 - THREAD_FPR0
xvld $xr11, \tmp, THREAD_FPR11 - THREAD_FPR0
xvld $xr12, \tmp, THREAD_FPR12 - THREAD_FPR0
xvld $xr13, \tmp, THREAD_FPR13 - THREAD_FPR0
xvld $xr14, \tmp, THREAD_FPR14 - THREAD_FPR0
xvld $xr15, \tmp, THREAD_FPR15 - THREAD_FPR0
xvld $xr16, \tmp, THREAD_FPR16 - THREAD_FPR0
xvld $xr17, \tmp, THREAD_FPR17 - THREAD_FPR0
xvld $xr18, \tmp, THREAD_FPR18 - THREAD_FPR0
xvld $xr19, \tmp, THREAD_FPR19 - THREAD_FPR0
xvld $xr20, \tmp, THREAD_FPR20 - THREAD_FPR0
xvld $xr21, \tmp, THREAD_FPR21 - THREAD_FPR0
xvld $xr22, \tmp, THREAD_FPR22 - THREAD_FPR0
xvld $xr23, \tmp, THREAD_FPR23 - THREAD_FPR0
xvld $xr24, \tmp, THREAD_FPR24 - THREAD_FPR0
xvld $xr25, \tmp, THREAD_FPR25 - THREAD_FPR0
xvld $xr26, \tmp, THREAD_FPR26 - THREAD_FPR0
xvld $xr27, \tmp, THREAD_FPR27 - THREAD_FPR0
xvld $xr28, \tmp, THREAD_FPR28 - THREAD_FPR0
xvld $xr29, \tmp, THREAD_FPR29 - THREAD_FPR0
xvld $xr30, \tmp, THREAD_FPR30 - THREAD_FPR0
xvld $xr31, \tmp, THREAD_FPR31 - THREAD_FPR0
.endm
.macro lasx_save_all thread tmp0 tmp1
fpu_save_cc \thread, \tmp0, \tmp1
fpu_save_csr \thread, \tmp0
lasx_save_data \thread, \tmp0
.endm
.macro lasx_restore_all thread tmp0 tmp1
lasx_restore_data \thread, \tmp0
fpu_restore_cc \thread, \tmp0, \tmp1
fpu_restore_csr \thread, \tmp0, \tmp1
.endm
.macro lasx_save_upper xd base tmp off
/* Nothing */
.endm
.macro lasx_save_all_upper thread base tmp
/* Nothing */
.endm
.macro lasx_restore_upper xd base tmp0 tmp1 off
vld \tmp0, \base, (\off+16)
xvpermi.q \xd, \tmp1, 0x2
.endm
.macro lasx_restore_all_upper thread base tmp
li.w \tmp, THREAD_FPR0
PTR_ADD \base, \thread, \tmp
/* Save $vr31 ($xr31 lower bits) with xvpickve2gr */
xvpickve2gr.d $r17, $xr31, 0
xvpickve2gr.d $r18, $xr31, 1
lasx_restore_upper $xr0, \base, $vr31, $xr31, (THREAD_FPR0-THREAD_FPR0)
lasx_restore_upper $xr1, \base, $vr31, $xr31, (THREAD_FPR1-THREAD_FPR0)
lasx_restore_upper $xr2, \base, $vr31, $xr31, (THREAD_FPR2-THREAD_FPR0)
lasx_restore_upper $xr3, \base, $vr31, $xr31, (THREAD_FPR3-THREAD_FPR0)
lasx_restore_upper $xr4, \base, $vr31, $xr31, (THREAD_FPR4-THREAD_FPR0)
lasx_restore_upper $xr5, \base, $vr31, $xr31, (THREAD_FPR5-THREAD_FPR0)
lasx_restore_upper $xr6, \base, $vr31, $xr31, (THREAD_FPR6-THREAD_FPR0)
lasx_restore_upper $xr7, \base, $vr31, $xr31, (THREAD_FPR7-THREAD_FPR0)
lasx_restore_upper $xr8, \base, $vr31, $xr31, (THREAD_FPR8-THREAD_FPR0)
lasx_restore_upper $xr9, \base, $vr31, $xr31, (THREAD_FPR9-THREAD_FPR0)
lasx_restore_upper $xr10, \base, $vr31, $xr31, (THREAD_FPR10-THREAD_FPR0)
lasx_restore_upper $xr11, \base, $vr31, $xr31, (THREAD_FPR11-THREAD_FPR0)
lasx_restore_upper $xr12, \base, $vr31, $xr31, (THREAD_FPR12-THREAD_FPR0)
lasx_restore_upper $xr13, \base, $vr31, $xr31, (THREAD_FPR13-THREAD_FPR0)
lasx_restore_upper $xr14, \base, $vr31, $xr31, (THREAD_FPR14-THREAD_FPR0)
lasx_restore_upper $xr15, \base, $vr31, $xr31, (THREAD_FPR15-THREAD_FPR0)
lasx_restore_upper $xr16, \base, $vr31, $xr31, (THREAD_FPR16-THREAD_FPR0)
lasx_restore_upper $xr17, \base, $vr31, $xr31, (THREAD_FPR17-THREAD_FPR0)
lasx_restore_upper $xr18, \base, $vr31, $xr31, (THREAD_FPR18-THREAD_FPR0)
lasx_restore_upper $xr19, \base, $vr31, $xr31, (THREAD_FPR19-THREAD_FPR0)
lasx_restore_upper $xr20, \base, $vr31, $xr31, (THREAD_FPR20-THREAD_FPR0)
lasx_restore_upper $xr21, \base, $vr31, $xr31, (THREAD_FPR21-THREAD_FPR0)
lasx_restore_upper $xr22, \base, $vr31, $xr31, (THREAD_FPR22-THREAD_FPR0)
lasx_restore_upper $xr23, \base, $vr31, $xr31, (THREAD_FPR23-THREAD_FPR0)
lasx_restore_upper $xr24, \base, $vr31, $xr31, (THREAD_FPR24-THREAD_FPR0)
lasx_restore_upper $xr25, \base, $vr31, $xr31, (THREAD_FPR25-THREAD_FPR0)
lasx_restore_upper $xr26, \base, $vr31, $xr31, (THREAD_FPR26-THREAD_FPR0)
lasx_restore_upper $xr27, \base, $vr31, $xr31, (THREAD_FPR27-THREAD_FPR0)
lasx_restore_upper $xr28, \base, $vr31, $xr31, (THREAD_FPR28-THREAD_FPR0)
lasx_restore_upper $xr29, \base, $vr31, $xr31, (THREAD_FPR29-THREAD_FPR0)
lasx_restore_upper $xr30, \base, $vr31, $xr31, (THREAD_FPR30-THREAD_FPR0)
lasx_restore_upper $xr31, \base, $vr31, $xr31, (THREAD_FPR31-THREAD_FPR0)
/* Restore $vr31 ($xr31 lower bits) with xvinsgr2vr */
xvinsgr2vr.d $xr31, $r17, 0
xvinsgr2vr.d $xr31, $r18, 1
.endm
.macro lasx_init_upper xd tmp
xvinsgr2vr.d \xd, \tmp, 2
xvinsgr2vr.d \xd, \tmp, 3
.endm
.macro lasx_init_all_upper tmp
not \tmp, zero
lasx_init_upper $xr0 \tmp
lasx_init_upper $xr1 \tmp
lasx_init_upper $xr2 \tmp
lasx_init_upper $xr3 \tmp
lasx_init_upper $xr4 \tmp
lasx_init_upper $xr5 \tmp
lasx_init_upper $xr6 \tmp
lasx_init_upper $xr7 \tmp
lasx_init_upper $xr8 \tmp
lasx_init_upper $xr9 \tmp
lasx_init_upper $xr10 \tmp
lasx_init_upper $xr11 \tmp
lasx_init_upper $xr12 \tmp
lasx_init_upper $xr13 \tmp
lasx_init_upper $xr14 \tmp
lasx_init_upper $xr15 \tmp
lasx_init_upper $xr16 \tmp
lasx_init_upper $xr17 \tmp
lasx_init_upper $xr18 \tmp
lasx_init_upper $xr19 \tmp
lasx_init_upper $xr20 \tmp
lasx_init_upper $xr21 \tmp
lasx_init_upper $xr22 \tmp
lasx_init_upper $xr23 \tmp
lasx_init_upper $xr24 \tmp
lasx_init_upper $xr25 \tmp
lasx_init_upper $xr26 \tmp
lasx_init_upper $xr27 \tmp
lasx_init_upper $xr28 \tmp
lasx_init_upper $xr29 \tmp
lasx_init_upper $xr30 \tmp
lasx_init_upper $xr31 \tmp
.endm
.macro not dst src
nor \dst, \src, zero
.endm
.macro la_abs reg, sym
#ifndef CONFIG_RELOCATABLE
la.abs \reg, \sym
#else
766:
lu12i.w \reg, 0
ori \reg, \reg, 0
#ifdef CONFIG_64BIT
lu32i.d \reg, 0
lu52i.d \reg, \reg, 0
#endif
.pushsection ".la_abs", "aw", %progbits
.p2align PTRLOG
PTR 766b
PTR \sym
.popsection
#endif
.endm
#endif /* _ASM_ASMMACRO_H */
@@ -0,0 +1,206 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Atomic operations (AMO).
*
* Copyright (C) 2020-2025 Loongson Technology Corporation Limited
*/
#ifndef _ASM_ATOMIC_AMO_H
#define _ASM_ATOMIC_AMO_H
#include <linux/types.h>
#include <asm/barrier.h>
#include <asm/cmpxchg.h>
#define ATOMIC_OP(op, I, asm_op) \
static inline void arch_atomic_##op(int i, atomic_t *v) \
{ \
__asm__ __volatile__( \
"am"#asm_op".w" " $zero, %1, %0 \n" \
: "+ZB" (v->counter) \
: "r" (I) \
: "memory"); \
}
#define ATOMIC_OP_RETURN(op, I, asm_op, c_op, mb, suffix) \
static inline int arch_atomic_##op##_return##suffix(int i, atomic_t *v) \
{ \
int result; \
\
__asm__ __volatile__( \
"am"#asm_op#mb".w" " %1, %2, %0 \n" \
: "+ZB" (v->counter), "=&r" (result) \
: "r" (I) \
: "memory"); \
\
return result c_op I; \
}
#define ATOMIC_FETCH_OP(op, I, asm_op, mb, suffix) \
static inline int arch_atomic_fetch_##op##suffix(int i, atomic_t *v) \
{ \
int result; \
\
__asm__ __volatile__( \
"am"#asm_op#mb".w" " %1, %2, %0 \n" \
: "+ZB" (v->counter), "=&r" (result) \
: "r" (I) \
: "memory"); \
\
return result; \
}
#define ATOMIC_OPS(op, I, asm_op, c_op) \
ATOMIC_OP(op, I, asm_op) \
ATOMIC_OP_RETURN(op, I, asm_op, c_op, _db, ) \
ATOMIC_OP_RETURN(op, I, asm_op, c_op, , _relaxed) \
ATOMIC_FETCH_OP(op, I, asm_op, _db, ) \
ATOMIC_FETCH_OP(op, I, asm_op, , _relaxed)
ATOMIC_OPS(add, i, add, +)
ATOMIC_OPS(sub, -i, add, +)
#define arch_atomic_add_return arch_atomic_add_return
#define arch_atomic_add_return_acquire arch_atomic_add_return
#define arch_atomic_add_return_release arch_atomic_add_return
#define arch_atomic_add_return_relaxed arch_atomic_add_return_relaxed
#define arch_atomic_sub_return arch_atomic_sub_return
#define arch_atomic_sub_return_acquire arch_atomic_sub_return
#define arch_atomic_sub_return_release arch_atomic_sub_return
#define arch_atomic_sub_return_relaxed arch_atomic_sub_return_relaxed
#define arch_atomic_fetch_add arch_atomic_fetch_add
#define arch_atomic_fetch_add_acquire arch_atomic_fetch_add
#define arch_atomic_fetch_add_release arch_atomic_fetch_add
#define arch_atomic_fetch_add_relaxed arch_atomic_fetch_add_relaxed
#define arch_atomic_fetch_sub arch_atomic_fetch_sub
#define arch_atomic_fetch_sub_acquire arch_atomic_fetch_sub
#define arch_atomic_fetch_sub_release arch_atomic_fetch_sub
#define arch_atomic_fetch_sub_relaxed arch_atomic_fetch_sub_relaxed
#undef ATOMIC_OPS
#define ATOMIC_OPS(op, I, asm_op) \
ATOMIC_OP(op, I, asm_op) \
ATOMIC_FETCH_OP(op, I, asm_op, _db, ) \
ATOMIC_FETCH_OP(op, I, asm_op, , _relaxed)
ATOMIC_OPS(and, i, and)
ATOMIC_OPS(or, i, or)
ATOMIC_OPS(xor, i, xor)
#define arch_atomic_fetch_and arch_atomic_fetch_and
#define arch_atomic_fetch_and_acquire arch_atomic_fetch_and
#define arch_atomic_fetch_and_release arch_atomic_fetch_and
#define arch_atomic_fetch_and_relaxed arch_atomic_fetch_and_relaxed
#define arch_atomic_fetch_or arch_atomic_fetch_or
#define arch_atomic_fetch_or_acquire arch_atomic_fetch_or
#define arch_atomic_fetch_or_release arch_atomic_fetch_or
#define arch_atomic_fetch_or_relaxed arch_atomic_fetch_or_relaxed
#define arch_atomic_fetch_xor arch_atomic_fetch_xor
#define arch_atomic_fetch_xor_acquire arch_atomic_fetch_xor
#define arch_atomic_fetch_xor_release arch_atomic_fetch_xor
#define arch_atomic_fetch_xor_relaxed arch_atomic_fetch_xor_relaxed
#undef ATOMIC_OPS
#undef ATOMIC_FETCH_OP
#undef ATOMIC_OP_RETURN
#undef ATOMIC_OP
#ifdef CONFIG_64BIT
#define ATOMIC64_OP(op, I, asm_op) \
static inline void arch_atomic64_##op(long i, atomic64_t *v) \
{ \
__asm__ __volatile__( \
"am"#asm_op".d " " $zero, %1, %0 \n" \
: "+ZB" (v->counter) \
: "r" (I) \
: "memory"); \
}
#define ATOMIC64_OP_RETURN(op, I, asm_op, c_op, mb, suffix) \
static inline long arch_atomic64_##op##_return##suffix(long i, atomic64_t *v) \
{ \
long result; \
__asm__ __volatile__( \
"am"#asm_op#mb".d " " %1, %2, %0 \n" \
: "+ZB" (v->counter), "=&r" (result) \
: "r" (I) \
: "memory"); \
\
return result c_op I; \
}
#define ATOMIC64_FETCH_OP(op, I, asm_op, mb, suffix) \
static inline long arch_atomic64_fetch_##op##suffix(long i, atomic64_t *v) \
{ \
long result; \
\
__asm__ __volatile__( \
"am"#asm_op#mb".d " " %1, %2, %0 \n" \
: "+ZB" (v->counter), "=&r" (result) \
: "r" (I) \
: "memory"); \
\
return result; \
}
#define ATOMIC64_OPS(op, I, asm_op, c_op) \
ATOMIC64_OP(op, I, asm_op) \
ATOMIC64_OP_RETURN(op, I, asm_op, c_op, _db, ) \
ATOMIC64_OP_RETURN(op, I, asm_op, c_op, , _relaxed) \
ATOMIC64_FETCH_OP(op, I, asm_op, _db, ) \
ATOMIC64_FETCH_OP(op, I, asm_op, , _relaxed)
ATOMIC64_OPS(add, i, add, +)
ATOMIC64_OPS(sub, -i, add, +)
#define arch_atomic64_add_return arch_atomic64_add_return
#define arch_atomic64_add_return_acquire arch_atomic64_add_return
#define arch_atomic64_add_return_release arch_atomic64_add_return
#define arch_atomic64_add_return_relaxed arch_atomic64_add_return_relaxed
#define arch_atomic64_sub_return arch_atomic64_sub_return
#define arch_atomic64_sub_return_acquire arch_atomic64_sub_return
#define arch_atomic64_sub_return_release arch_atomic64_sub_return
#define arch_atomic64_sub_return_relaxed arch_atomic64_sub_return_relaxed
#define arch_atomic64_fetch_add arch_atomic64_fetch_add
#define arch_atomic64_fetch_add_acquire arch_atomic64_fetch_add
#define arch_atomic64_fetch_add_release arch_atomic64_fetch_add
#define arch_atomic64_fetch_add_relaxed arch_atomic64_fetch_add_relaxed
#define arch_atomic64_fetch_sub arch_atomic64_fetch_sub
#define arch_atomic64_fetch_sub_acquire arch_atomic64_fetch_sub
#define arch_atomic64_fetch_sub_release arch_atomic64_fetch_sub
#define arch_atomic64_fetch_sub_relaxed arch_atomic64_fetch_sub_relaxed
#undef ATOMIC64_OPS
#define ATOMIC64_OPS(op, I, asm_op) \
ATOMIC64_OP(op, I, asm_op) \
ATOMIC64_FETCH_OP(op, I, asm_op, _db, ) \
ATOMIC64_FETCH_OP(op, I, asm_op, , _relaxed)
ATOMIC64_OPS(and, i, and)
ATOMIC64_OPS(or, i, or)
ATOMIC64_OPS(xor, i, xor)
#define arch_atomic64_fetch_and arch_atomic64_fetch_and
#define arch_atomic64_fetch_and_acquire arch_atomic64_fetch_and
#define arch_atomic64_fetch_and_release arch_atomic64_fetch_and
#define arch_atomic64_fetch_and_relaxed arch_atomic64_fetch_and_relaxed
#define arch_atomic64_fetch_or arch_atomic64_fetch_or
#define arch_atomic64_fetch_or_acquire arch_atomic64_fetch_or
#define arch_atomic64_fetch_or_release arch_atomic64_fetch_or
#define arch_atomic64_fetch_or_relaxed arch_atomic64_fetch_or_relaxed
#define arch_atomic64_fetch_xor arch_atomic64_fetch_xor
#define arch_atomic64_fetch_xor_acquire arch_atomic64_fetch_xor
#define arch_atomic64_fetch_xor_release arch_atomic64_fetch_xor
#define arch_atomic64_fetch_xor_relaxed arch_atomic64_fetch_xor_relaxed
#undef ATOMIC64_OPS
#undef ATOMIC64_FETCH_OP
#undef ATOMIC64_OP_RETURN
#undef ATOMIC64_OP
#endif
#endif /* _ASM_ATOMIC_AMO_H */
@@ -0,0 +1,100 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Atomic operations (LLSC).
*
* Copyright (C) 2024-2025 Loongson Technology Corporation Limited
*/
#ifndef _ASM_ATOMIC_LLSC_H
#define _ASM_ATOMIC_LLSC_H
#include <linux/types.h>
#include <asm/barrier.h>
#include <asm/cmpxchg.h>
#define ATOMIC_OP(op, I, asm_op) \
static inline void arch_atomic_##op(int i, atomic_t *v) \
{ \
int temp; \
\
__asm__ __volatile__( \
"1: ll.w %0, %1 #atomic_" #op " \n" \
" " #asm_op " %0, %0, %2 \n" \
" sc.w %0, %1 \n" \
" beq %0, $r0, 1b \n" \
:"=&r" (temp) , "+ZC"(v->counter) \
:"r" (I) \
); \
}
#define ATOMIC_OP_RETURN(op, I, asm_op) \
static inline int arch_atomic_##op##_return_relaxed(int i, atomic_t *v) \
{ \
int result, temp; \
\
__asm__ __volatile__( \
"1: ll.w %1, %2 # atomic_" #op "_return \n" \
" " #asm_op " %0, %1, %3 \n" \
" sc.w %0, %2 \n" \
" beq %0, $r0 ,1b \n" \
" " #asm_op " %0, %1, %3 \n" \
: "=&r" (result), "=&r" (temp), "+ZC"(v->counter) \
: "r" (I)); \
\
return result; \
}
#define ATOMIC_FETCH_OP(op, I, asm_op) \
static inline int arch_atomic_fetch_##op##_relaxed(int i, atomic_t *v) \
{ \
int result, temp; \
\
__asm__ __volatile__( \
"1: ll.w %1, %2 # atomic_fetch_" #op " \n" \
" " #asm_op " %0, %1, %3 \n" \
" sc.w %0, %2 \n" \
" beq %0, $r0 ,1b \n" \
" add.w %0, %1 ,$r0 \n" \
: "=&r" (result), "=&r" (temp), "+ZC" (v->counter) \
: "r" (I)); \
\
return result; \
}
#define ATOMIC_OPS(op,I ,asm_op, c_op) \
ATOMIC_OP(op, I, asm_op) \
ATOMIC_OP_RETURN(op, I , asm_op) \
ATOMIC_FETCH_OP(op, I, asm_op)
ATOMIC_OPS(add, i , add.w ,+=)
ATOMIC_OPS(sub, -i , add.w ,+=)
#define arch_atomic_add_return_relaxed arch_atomic_add_return_relaxed
#define arch_atomic_sub_return_relaxed arch_atomic_sub_return_relaxed
#define arch_atomic_fetch_add_relaxed arch_atomic_fetch_add_relaxed
#define arch_atomic_fetch_sub_relaxed arch_atomic_fetch_sub_relaxed
#undef ATOMIC_OPS
#define ATOMIC_OPS(op, I, asm_op) \
ATOMIC_OP(op, I, asm_op) \
ATOMIC_FETCH_OP(op, I, asm_op)
ATOMIC_OPS(and, i, and)
ATOMIC_OPS(or, i, or)
ATOMIC_OPS(xor, i, xor)
#define arch_atomic_fetch_and_relaxed arch_atomic_fetch_and_relaxed
#define arch_atomic_fetch_or_relaxed arch_atomic_fetch_or_relaxed
#define arch_atomic_fetch_xor_relaxed arch_atomic_fetch_xor_relaxed
#undef ATOMIC_OPS
#undef ATOMIC_FETCH_OP
#undef ATOMIC_OP_RETURN
#undef ATOMIC_OP
#ifdef CONFIG_64BIT
#error "64-bit LLSC atomic operations are not supported"
#endif
#endif /* _ASM_ATOMIC_LLSC_H */
@@ -0,0 +1,174 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Atomic operations.
*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_ATOMIC_H
#define _ASM_ATOMIC_H
#include <linux/types.h>
#include <asm/barrier.h>
#include <asm/cmpxchg.h>
#ifdef CONFIG_CPU_HAS_AMO
#include <asm/atomic-amo.h>
#else
#include <asm/atomic-llsc.h>
#endif
#ifdef CONFIG_GENERIC_ATOMIC64
#include <asm-generic/atomic64.h>
#endif
#if __SIZEOF_LONG__ == 4
#define __LL "ll.w "
#define __SC "sc.w "
#define __AMADD "amadd.w "
#define __AMOR "amor.w "
#define __AMAND_DB "amand_db.w "
#define __AMOR_DB "amor_db.w "
#define __AMXOR_DB "amxor_db.w "
#elif __SIZEOF_LONG__ == 8
#define __LL "ll.d "
#define __SC "sc.d "
#define __AMADD "amadd.d "
#define __AMOR "amor.d "
#define __AMAND_DB "amand_db.d "
#define __AMOR_DB "amor_db.d "
#define __AMXOR_DB "amxor_db.d "
#endif
#define ATOMIC_INIT(i) { (i) }
#define arch_atomic_read(v) READ_ONCE((v)->counter)
#define arch_atomic_set(v, i) WRITE_ONCE((v)->counter, (i))
static inline int arch_atomic_fetch_add_unless(atomic_t *v, int a, int u)
{
int prev, rc;
__asm__ __volatile__ (
"0: ll.w %[p], %[c]\n"
" beq %[p], %[u], 1f\n"
" add.w %[rc], %[p], %[a]\n"
" sc.w %[rc], %[c]\n"
" beqz %[rc], 0b\n"
" b 2f\n"
"1:\n"
__WEAK_LLSC_MB
"2:\n"
: [p]"=&r" (prev), [rc]"=&r" (rc),
[c]"=ZB" (v->counter)
: [a]"r" (a), [u]"r" (u)
: "memory");
return prev;
}
#define arch_atomic_fetch_add_unless arch_atomic_fetch_add_unless
static inline int arch_atomic_sub_if_positive(int i, atomic_t *v)
{
int result;
int temp;
if (__builtin_constant_p(i)) {
__asm__ __volatile__(
"1: ll.w %1, %2 # atomic_sub_if_positive\n"
" addi.w %0, %1, %3 \n"
" move %1, %0 \n"
" bltz %0, 2f \n"
" sc.w %1, %2 \n"
" beqz %1, 1b \n"
"2: \n"
__WEAK_LLSC_MB
: "=&r" (result), "=&r" (temp), "+ZC" (v->counter)
: "I" (-i));
} else {
__asm__ __volatile__(
"1: ll.w %1, %2 # atomic_sub_if_positive\n"
" sub.w %0, %1, %3 \n"
" move %1, %0 \n"
" bltz %0, 2f \n"
" sc.w %1, %2 \n"
" beqz %1, 1b \n"
"2: \n"
__WEAK_LLSC_MB
: "=&r" (result), "=&r" (temp), "+ZC" (v->counter)
: "r" (i));
}
return result;
}
#define arch_atomic_dec_if_positive(v) arch_atomic_sub_if_positive(1, v)
#ifdef CONFIG_64BIT
#define ATOMIC64_INIT(i) { (i) }
#define arch_atomic64_read(v) READ_ONCE((v)->counter)
#define arch_atomic64_set(v, i) WRITE_ONCE((v)->counter, (i))
static inline long arch_atomic64_fetch_add_unless(atomic64_t *v, long a, long u)
{
long prev, rc;
__asm__ __volatile__ (
"0: ll.d %[p], %[c]\n"
" beq %[p], %[u], 1f\n"
" add.d %[rc], %[p], %[a]\n"
" sc.d %[rc], %[c]\n"
" beqz %[rc], 0b\n"
" b 2f\n"
"1:\n"
__WEAK_LLSC_MB
"2:\n"
: [p]"=&r" (prev), [rc]"=&r" (rc),
[c] "=ZB" (v->counter)
: [a]"r" (a), [u]"r" (u)
: "memory");
return prev;
}
#define arch_atomic64_fetch_add_unless arch_atomic64_fetch_add_unless
static inline long arch_atomic64_sub_if_positive(long i, atomic64_t *v)
{
long result;
long temp;
if (__builtin_constant_p(i)) {
__asm__ __volatile__(
"1: ll.d %1, %2 # atomic64_sub_if_positive \n"
" addi.d %0, %1, %3 \n"
" move %1, %0 \n"
" bltz %0, 2f \n"
" sc.d %1, %2 \n"
" beqz %1, 1b \n"
"2: \n"
__WEAK_LLSC_MB
: "=&r" (result), "=&r" (temp), "+ZC" (v->counter)
: "I" (-i));
} else {
__asm__ __volatile__(
"1: ll.d %1, %2 # atomic64_sub_if_positive \n"
" sub.d %0, %1, %3 \n"
" move %1, %0 \n"
" bltz %0, 2f \n"
" sc.d %1, %2 \n"
" beqz %1, 1b \n"
"2: \n"
__WEAK_LLSC_MB
: "=&r" (result), "=&r" (temp), "+ZC" (v->counter)
: "r" (i));
}
return result;
}
#define arch_atomic64_dec_if_positive(v) arch_atomic64_sub_if_positive(1, v)
#endif /* CONFIG_64BIT */
#endif /* _ASM_ATOMIC_H */
@@ -0,0 +1,139 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __ASM_BARRIER_H
#define __ASM_BARRIER_H
/*
* Hint encoding:
*
* Bit4: ordering or completion (0: completion, 1: ordering)
* Bit3: barrier for previous read (0: true, 1: false)
* Bit2: barrier for previous write (0: true, 1: false)
* Bit1: barrier for succeeding read (0: true, 1: false)
* Bit0: barrier for succeeding write (0: true, 1: false)
*
* Hint 0x700: barrier for "read after read" from the same address
*/
#define DBAR(hint) __asm__ __volatile__("dbar %0 " : : "I"(hint) : "memory")
#define crwrw 0b00000
#define cr_r_ 0b00101
#define c_w_w 0b01010
#define orwrw 0b10000
#define or_r_ 0b10101
#define o_w_w 0b11010
#define orw_w 0b10010
#define or_rw 0b10100
#define c_sync() DBAR(crwrw)
#define c_rsync() DBAR(cr_r_)
#define c_wsync() DBAR(c_w_w)
#define o_sync() DBAR(orwrw)
#define o_rsync() DBAR(or_r_)
#define o_wsync() DBAR(o_w_w)
#define ldacq_mb() DBAR(or_rw)
#define strel_mb() DBAR(orw_w)
#define mb() c_sync()
#define rmb() c_rsync()
#define wmb() c_wsync()
#define iob() c_sync()
#define wbflush() c_sync()
#define __smp_mb() o_sync()
#define __smp_rmb() o_rsync()
#define __smp_wmb() o_wsync()
#ifdef CONFIG_SMP
#define __WEAK_LLSC_MB " dbar 0x700 \n"
#else
#define __WEAK_LLSC_MB " \n"
#endif
#define __smp_mb__before_atomic() barrier()
#define __smp_mb__after_atomic() barrier()
/**
* array_index_mask_nospec() - generate a ~0 mask when index < size, 0 otherwise
* @index: array element index
* @size: number of elements in array
*
* Returns:
* 0 - (@index < @size)
*/
#define array_index_mask_nospec array_index_mask_nospec
static inline unsigned long array_index_mask_nospec(unsigned long index,
unsigned long size)
{
unsigned long mask;
__asm__ __volatile__(
"sltu %0, %1, %2\n\t"
#if (__SIZEOF_LONG__ == 4)
"sub.w %0, $zero, %0\n\t"
#elif (__SIZEOF_LONG__ == 8)
"sub.d %0, $zero, %0\n\t"
#endif
: "=r" (mask)
: "r" (index), "r" (size)
:);
return mask;
}
#define __smp_load_acquire(p) \
({ \
typeof(*p) ___p1 = READ_ONCE(*p); \
compiletime_assert_atomic_type(*p); \
ldacq_mb(); \
___p1; \
})
#define __smp_store_release(p, v) \
do { \
compiletime_assert_atomic_type(*p); \
strel_mb(); \
WRITE_ONCE(*p, v); \
} while (0)
#define __smp_store_mb(p, v) \
do { \
union { typeof(p) __val; char __c[1]; } __u = \
{ .__val = (__force typeof(p)) (v) }; \
unsigned long __tmp; \
switch (sizeof(p)) { \
case 1: \
*(volatile __u8 *)&p = *(__u8 *)__u.__c; \
__smp_mb(); \
break; \
case 2: \
*(volatile __u16 *)&p = *(__u16 *)__u.__c; \
__smp_mb(); \
break; \
case 4: \
__asm__ __volatile__( \
"amswap_db.w %[tmp], %[val], %[mem] \n" \
: [mem] "+ZB" (*(u32 *)&p), [tmp] "=&r" (__tmp) \
: [val] "r" (*(__u32 *)__u.__c) \
: ); \
break; \
case 8: \
__asm__ __volatile__( \
"amswap_db.d %[tmp], %[val], %[mem] \n" \
: [mem] "+ZB" (*(u64 *)&p), [tmp] "=&r" (__tmp) \
: [val] "r" (*(__u64 *)__u.__c) \
: ); \
break; \
} \
} while (0)
#include <asm-generic/barrier.h>
#endif /* __ASM_BARRIER_H */
@@ -0,0 +1,44 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_BITOPS_H
#define _ASM_BITOPS_H
#include <linux/compiler.h>
#ifndef _LINUX_BITOPS_H
#error only <linux/bitops.h> can be included directly
#endif
#include <asm/barrier.h>
#ifdef CONFIG_32BIT_REDUCED
#include <asm-generic/bitops/ffs.h>
#include <asm-generic/bitops/fls.h>
#include <asm-generic/bitops/__ffs.h>
#include <asm-generic/bitops/__fls.h>
#else /* CONFIG_32BIT_STANDARD || CONFIG_64BIT */
#include <asm-generic/bitops/builtin-ffs.h>
#include <asm-generic/bitops/builtin-fls.h>
#include <asm-generic/bitops/builtin-__ffs.h>
#include <asm-generic/bitops/builtin-__fls.h>
#endif
#include <asm-generic/bitops/ffz.h>
#include <asm-generic/bitops/fls64.h>
#include <asm-generic/bitops/sched.h>
#include <asm-generic/bitops/hweight.h>
#include <asm-generic/bitops/atomic.h>
#include <asm-generic/bitops/non-atomic.h>
#include <asm-generic/bitops/lock.h>
#include <asm-generic/bitops/le.h>
#include <asm-generic/bitops/ext2-atomic.h>
#endif /* _ASM_BITOPS_H */
@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __LOONGARCH_ASM_BITREV_H__
#define __LOONGARCH_ASM_BITREV_H__
#include <linux/swab.h>
static __always_inline __attribute_const__ u32 __arch_bitrev32(u32 x)
{
u32 ret;
asm("bitrev.w %0, %1" : "=r"(ret) : "r"(x));
return ret;
}
static __always_inline __attribute_const__ u16 __arch_bitrev16(u16 x)
{
u16 ret;
asm("bitrev.4b %0, %1" : "=r"(ret) : "r"(__swab16(x)));
return ret;
}
static __always_inline __attribute_const__ u8 __arch_bitrev8(u8 x)
{
u8 ret;
asm("bitrev.4b %0, %1" : "=r"(ret) : "r"(x));
return ret;
}
#endif /* __LOONGARCH_ASM_BITREV_H__ */
@@ -0,0 +1,54 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_BOOTINFO_H
#define _ASM_BOOTINFO_H
#include <linux/types.h>
#include <asm/setup.h>
const char *get_system_type(void);
extern void init_environ(void);
extern void memblock_init(void);
extern void platform_init(void);
extern int __init init_numa_memory(void);
struct loongson_board_info {
int bios_size;
const char *bios_vendor;
const char *bios_version;
const char *bios_release_date;
const char *board_name;
const char *board_vendor;
};
#define NR_WORDS DIV_ROUND_UP(NR_CPUS, BITS_PER_LONG)
/*
* The "core" of cores_per_node and cores_per_package stands for a
* logical core, which means in a SMT system it stands for a thread.
*/
struct loongson_system_configuration {
int nr_cpus;
int nr_nodes;
int boot_cpu_id;
int cores_per_node;
int cores_per_package;
unsigned long cores_io_master[NR_WORDS];
unsigned long suspend_addr;
const char *cpuname;
};
extern u64 efi_system_table;
extern unsigned long fw_arg0, fw_arg1, fw_arg2;
extern struct loongson_board_info b_info;
extern struct loongson_system_configuration loongson_sysconf;
static inline bool io_master(int cpu)
{
return test_bit(cpu, loongson_sysconf.cores_io_master);
}
#endif /* _ASM_BOOTINFO_H */
@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_BRANCH_H
#define _ASM_BRANCH_H
#include <asm/ptrace.h>
static inline unsigned long exception_era(struct pt_regs *regs)
{
return regs->csr_era;
}
static inline void compute_return_era(struct pt_regs *regs)
{
regs->csr_era += 4;
}
#endif /* _ASM_BRANCH_H */
@@ -0,0 +1,62 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_BUG_H
#define __ASM_BUG_H
#include <asm/break.h>
#include <linux/stringify.h>
#include <linux/objtool.h>
#ifndef CONFIG_DEBUG_BUGVERBOSE
#define _BUGVERBOSE_LOCATION(file, line)
#else
#define __BUGVERBOSE_LOCATION(file, line) \
.pushsection .rodata.str, "aMS", @progbits, 1; \
10002: .ascii file "\0"; \
.popsection; \
\
.long 10002b - .; \
.short line;
#define _BUGVERBOSE_LOCATION(file, line) __BUGVERBOSE_LOCATION(file, line)
#endif
#ifndef CONFIG_GENERIC_BUG
#define __BUG_ENTRY(cond_str, flags)
#else
#define __BUG_ENTRY(cond_str, flags) \
.pushsection __bug_table, "aw"; \
.align 2; \
10000: .long 10001f - .; \
_BUGVERBOSE_LOCATION(WARN_CONDITION_STR(cond_str) __FILE__, __LINE__) \
.short flags; \
.popsection; \
10001:
#endif
#define ASM_BUG_FLAGS(cond_str, flags) \
__BUG_ENTRY(cond_str, flags) \
break BRK_BUG;
#define ASM_BUG() ASM_BUG_FLAGS("", 0)
#define __BUG_FLAGS(cond_str, flags, extra) \
asm_inline volatile (__stringify(ASM_BUG_FLAGS(cond_str, flags)) extra);
#define __WARN_FLAGS(cond_str, flags) \
do { \
instrumentation_begin(); \
__BUG_FLAGS(cond_str, BUGFLAG_WARNING|(flags), ANNOTATE_REACHABLE(10001b));\
instrumentation_end(); \
} while (0)
#define BUG() \
do { \
instrumentation_begin(); \
__BUG_FLAGS("", 0, ""); \
unreachable(); \
} while (0)
#define HAVE_ARCH_BUG
#include <asm-generic/bug.h>
#endif /* __ASM_BUG_H */
@@ -0,0 +1,15 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_CACHE_H
#define _ASM_CACHE_H
#define L1_CACHE_SHIFT CONFIG_L1_CACHE_SHIFT
#define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT)
#define ARCH_DMA_MINALIGN (16)
#define __read_mostly __section(".data..read_mostly")
#endif /* _ASM_CACHE_H */
@@ -0,0 +1,99 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_CACHEFLUSH_H
#define _ASM_CACHEFLUSH_H
#include <linux/mm.h>
#include <asm/cpu-info.h>
#include <asm/cacheops.h>
static inline bool cache_present(struct cache_desc *cdesc)
{
return cdesc->flags & CACHE_PRESENT;
}
static inline bool cache_private(struct cache_desc *cdesc)
{
return cdesc->flags & CACHE_PRIVATE;
}
static inline bool cache_inclusive(struct cache_desc *cdesc)
{
return cdesc->flags & CACHE_INCLUSIVE;
}
static inline unsigned int cpu_last_level_cache_line_size(void)
{
int cache_present = boot_cpu_data.cache_leaves_present;
return boot_cpu_data.cache_leaves[cache_present - 1].linesz;
}
asmlinkage void __flush_cache_all(void);
/*
* LoongArch maintains ICache/DCache coherency by hardware,
* we just need "ibar" to avoid instruction hazard here.
*/
static inline void local_flush_icache_all(void)
{
asm volatile ("ibar\t0\n"::);
}
static inline void local_flush_icache_range(unsigned long start, unsigned long end)
{
asm volatile ("ibar\t0\n"::);
}
#define flush_icache_all local_flush_icache_all
#define flush_icache_range local_flush_icache_range
#define flush_icache_user_range local_flush_icache_range
#define flush_cache_all() do { } while (0)
#define flush_cache_mm(mm) do { } while (0)
#define flush_cache_dup_mm(mm) do { } while (0)
#define flush_cache_range(vma, start, end) do { } while (0)
#define flush_cache_page(vma, vmaddr, pfn) do { } while (0)
#define flush_cache_vmap(start, end) do { } while (0)
#define flush_cache_vunmap(start, end) do { } while (0)
#define flush_icache_user_page(vma, page, addr, len) do { } while (0)
#define flush_dcache_mmap_lock(mapping) do { } while (0)
#define flush_dcache_mmap_unlock(mapping) do { } while (0)
#define cache_op(op, addr) \
__asm__ __volatile__( \
" cacop %0, %1 \n" \
: \
: "i" (op), "ZC" (*(unsigned char *)(addr)))
static inline void flush_cache_line(int leaf, unsigned long addr)
{
switch (leaf) {
case Cache_LEAF0:
cache_op(Index_Writeback_Inv_LEAF0, addr);
break;
case Cache_LEAF1:
cache_op(Index_Writeback_Inv_LEAF1, addr);
break;
case Cache_LEAF2:
cache_op(Index_Writeback_Inv_LEAF2, addr);
break;
case Cache_LEAF3:
cache_op(Index_Writeback_Inv_LEAF3, addr);
break;
case Cache_LEAF4:
cache_op(Index_Writeback_Inv_LEAF4, addr);
break;
case Cache_LEAF5:
cache_op(Index_Writeback_Inv_LEAF5, addr);
break;
default:
break;
}
}
#include <asm-generic/cacheflush.h>
#endif /* _ASM_CACHEFLUSH_H */
@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Cache operations for the cache instruction.
*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __ASM_CACHEOPS_H
#define __ASM_CACHEOPS_H
/*
* Most cache ops are split into a 3 bit field identifying the cache, and a 2
* bit field identifying the cache operation.
*/
#define CacheOp_Cache 0x07
#define CacheOp_Op 0x18
#define Cache_LEAF0 0x00
#define Cache_LEAF1 0x01
#define Cache_LEAF2 0x02
#define Cache_LEAF3 0x03
#define Cache_LEAF4 0x04
#define Cache_LEAF5 0x05
#define Index_Invalidate 0x08
#define Index_Writeback_Inv 0x08
#define Hit_Invalidate 0x10
#define Hit_Writeback_Inv 0x10
#define CacheOp_User_Defined 0x18
#define Index_Writeback_Inv_LEAF0 (Cache_LEAF0 | Index_Writeback_Inv)
#define Index_Writeback_Inv_LEAF1 (Cache_LEAF1 | Index_Writeback_Inv)
#define Index_Writeback_Inv_LEAF2 (Cache_LEAF2 | Index_Writeback_Inv)
#define Index_Writeback_Inv_LEAF3 (Cache_LEAF3 | Index_Writeback_Inv)
#define Index_Writeback_Inv_LEAF4 (Cache_LEAF4 | Index_Writeback_Inv)
#define Index_Writeback_Inv_LEAF5 (Cache_LEAF5 | Index_Writeback_Inv)
#define Hit_Writeback_Inv_LEAF0 (Cache_LEAF0 | Hit_Writeback_Inv)
#define Hit_Writeback_Inv_LEAF1 (Cache_LEAF1 | Hit_Writeback_Inv)
#define Hit_Writeback_Inv_LEAF2 (Cache_LEAF2 | Hit_Writeback_Inv)
#define Hit_Writeback_Inv_LEAF3 (Cache_LEAF3 | Hit_Writeback_Inv)
#define Hit_Writeback_Inv_LEAF4 (Cache_LEAF4 | Hit_Writeback_Inv)
#define Hit_Writeback_Inv_LEAF5 (Cache_LEAF5 | Hit_Writeback_Inv)
#endif /* __ASM_CACHEOPS_H */
@@ -0,0 +1,70 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2016 ARM Ltd.
* Copyright (C) 2023 Loongson Technology Corporation Limited
*/
#ifndef __ASM_CHECKSUM_H
#define __ASM_CHECKSUM_H
#include <linux/bitops.h>
#include <linux/in6.h>
#ifdef CONFIG_64BIT
#define _HAVE_ARCH_IPV6_CSUM
__sum16 csum_ipv6_magic(const struct in6_addr *saddr,
const struct in6_addr *daddr,
__u32 len, __u8 proto, __wsum sum);
/*
* turns a 32-bit partial checksum (e.g. from csum_partial) into a
* 1's complement 16-bit checksum.
*/
static inline __sum16 csum_fold(__wsum sum)
{
u32 tmp = (__force u32)sum;
/*
* swap the two 16-bit halves of sum
* if there is a carry from adding the two 16-bit halves,
* it will carry from the lower half into the upper half,
* giving us the correct sum in the upper half.
*/
return (__force __sum16)(~(tmp + rol32(tmp, 16)) >> 16);
}
#define csum_fold csum_fold
/*
* This is a version of ip_compute_csum() optimized for IP headers,
* which always checksum on 4 octet boundaries. ihl is the number
* of 32-bit words and is always >= 5.
*/
static inline __sum16 ip_fast_csum(const void *iph, unsigned int ihl)
{
u64 sum;
__uint128_t tmp;
int n = ihl; /* we want it signed */
tmp = *(const __uint128_t *)iph;
iph += 16;
n -= 4;
tmp += ((tmp >> 64) | (tmp << 64));
sum = tmp >> 64;
do {
sum += *(const u32 *)iph;
iph += 4;
} while (--n > 0);
sum += ror64(sum, 32);
return csum_fold((__force __wsum)(sum >> 32));
}
#define ip_fast_csum ip_fast_csum
extern unsigned int do_csum(const unsigned char *buff, int len);
#define do_csum do_csum
#endif
#include <asm-generic/checksum.h>
#endif /* __ASM_CHECKSUM_H */
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Author: Huacai Chen <chenhuacai@loongson.cn>
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __ASM_CLOCKSOURCE_H
#define __ASM_CLOCKSOURCE_H
#include <asm/vdso/clocksource.h>
#endif /* __ASM_CLOCKSOURCE_H */
@@ -0,0 +1,304 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __ASM_CMPXCHG_H
#define __ASM_CMPXCHG_H
#include <linux/bits.h>
#include <linux/build_bug.h>
#include <asm/barrier.h>
#include <asm/cpu-features.h>
#define __xchg_amo_asm(amswap_db, m, val) \
({ \
__typeof(val) __ret; \
\
__asm__ __volatile__ ( \
" "amswap_db" %1, %z2, %0 \n" \
: "+ZB" (*m), "=&r" (__ret) \
: "Jr" (val) \
: "memory"); \
\
__ret; \
})
#define __xchg_llsc_asm(ld, st, m, val) \
({ \
__typeof(val) __ret, __tmp; \
\
asm volatile ( \
"1: ll.w %0, %3 \n" \
" move %1, %z4 \n" \
" sc.w %1, %2 \n" \
" beqz %1, 1b \n" \
: "=&r" (__ret), "=&r" (__tmp), "=ZC" (*m) \
: "ZC" (*m), "Jr" (val) \
: "memory"); \
\
__ret; \
})
static inline unsigned int __xchg_small(volatile void *ptr, unsigned int val,
unsigned int size)
{
unsigned int shift;
u32 old32, mask, temp;
volatile u32 *ptr32;
/* Mask value to the correct size. */
mask = GENMASK((size * BITS_PER_BYTE) - 1, 0);
val &= mask;
/*
* Calculate a shift & mask that correspond to the value we wish to
* exchange within the naturally aligned 4 byte integerthat includes
* it.
*/
shift = (unsigned long)ptr & 0x3;
shift *= BITS_PER_BYTE;
mask <<= shift;
/*
* Calculate a pointer to the naturally aligned 4 byte integer that
* includes our byte of interest, and load its value.
*/
ptr32 = (volatile u32 *)((unsigned long)ptr & ~0x3);
asm volatile (
"1: ll.w %0, %3 \n"
" andn %1, %0, %z4 \n"
" or %1, %1, %z5 \n"
" sc.w %1, %2 \n"
" beqz %1, 1b \n"
: "=&r" (old32), "=&r" (temp), "=ZC" (*ptr32)
: "ZC" (*ptr32), "Jr" (mask), "Jr" (val << shift)
: "memory");
return (old32 & mask) >> shift;
}
static __always_inline unsigned long
__arch_xchg(volatile void *ptr, unsigned long x, int size)
{
switch (size) {
case 1:
case 2:
return __xchg_small((volatile void *)ptr, x, size);
case 4:
#ifdef CONFIG_CPU_HAS_AMO
return __xchg_amo_asm("amswap_db.w", (volatile u32 *)ptr, (u32)x);
#else
return __xchg_llsc_asm("ll.w", "sc.w", (volatile u32 *)ptr, (u32)x);
#endif /* CONFIG_CPU_HAS_AMO */
#ifdef CONFIG_64BIT
case 8:
#ifdef CONFIG_CPU_HAS_AMO
return __xchg_amo_asm("amswap_db.d", (volatile u64 *)ptr, (u64)x);
#else
return __xchg_llsc_asm("ll.d", "sc.d", (volatile u64 *)ptr, (u64)x);
#endif /* CONFIG_CPU_HAS_AMO */
#endif /* CONFIG_64BIT */
default:
BUILD_BUG();
}
return 0;
}
#define arch_xchg(ptr, x) \
({ \
__typeof__(*(ptr)) __res; \
\
__res = (__typeof__(*(ptr))) \
__arch_xchg((ptr), (unsigned long)(x), sizeof(*(ptr))); \
\
__res; \
})
#define __cmpxchg_asm(ld, st, m, old, new) \
({ \
__typeof(old) __ret; \
\
__asm__ __volatile__( \
"1: " ld " %0, %2 # __cmpxchg_asm \n" \
" bne %0, %z3, 2f \n" \
" move $t0, %z4 \n" \
" " st " $t0, %1 \n" \
" beqz $t0, 1b \n" \
"2: \n" \
__WEAK_LLSC_MB \
: "=&r" (__ret), "=ZB"(*m) \
: "ZB"(*m), "Jr" (old), "Jr" (new) \
: "t0", "memory"); \
\
__ret; \
})
static inline unsigned int __cmpxchg_small(volatile void *ptr, unsigned int old,
unsigned int new, unsigned int size)
{
unsigned int shift;
u32 old32, mask, temp;
volatile u32 *ptr32;
/* Mask inputs to the correct size. */
mask = GENMASK((size * BITS_PER_BYTE) - 1, 0);
old &= mask;
new &= mask;
/*
* Calculate a shift & mask that correspond to the value we wish to
* compare & exchange within the naturally aligned 4 byte integer
* that includes it.
*/
shift = (unsigned long)ptr & 0x3;
shift *= BITS_PER_BYTE;
old <<= shift;
new <<= shift;
mask <<= shift;
/*
* Calculate a pointer to the naturally aligned 4 byte integer that
* includes our byte of interest, and load its value.
*/
ptr32 = (volatile u32 *)((unsigned long)ptr & ~0x3);
asm volatile (
"1: ll.w %0, %3 \n"
" and %1, %0, %z4 \n"
" bne %1, %z5, 2f \n"
" andn %1, %0, %z4 \n"
" or %1, %1, %z6 \n"
" sc.w %1, %2 \n"
" beqz %1, 1b \n"
" b 3f \n"
"2: \n"
__WEAK_LLSC_MB
"3: \n"
: "=&r" (old32), "=&r" (temp), "=ZC" (*ptr32)
: "ZC" (*ptr32), "Jr" (mask), "Jr" (old), "Jr" (new)
: "memory");
return (old32 & mask) >> shift;
}
static __always_inline unsigned long
__cmpxchg(volatile void *ptr, unsigned long old, unsigned long new, unsigned int size)
{
switch (size) {
case 1:
case 2:
return __cmpxchg_small(ptr, old, new, size);
case 4:
return __cmpxchg_asm("ll.w", "sc.w", (volatile u32 *)ptr,
(u32)old, new);
case 8:
return __cmpxchg_asm("ll.d", "sc.d", (volatile u64 *)ptr,
(u64)old, new);
default:
BUILD_BUG();
}
return 0;
}
#define arch_cmpxchg_local(ptr, old, new) \
((__typeof__(*(ptr))) \
__cmpxchg((ptr), \
(unsigned long)(__typeof__(*(ptr)))(old), \
(unsigned long)(__typeof__(*(ptr)))(new), \
sizeof(*(ptr))))
#define arch_cmpxchg(ptr, old, new) \
({ \
__typeof__(*(ptr)) __res; \
\
__res = arch_cmpxchg_local((ptr), (old), (new)); \
\
__res; \
})
#ifdef CONFIG_64BIT
#define arch_cmpxchg64_local(ptr, o, n) \
({ \
BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
arch_cmpxchg_local((ptr), (o), (n)); \
})
#define arch_cmpxchg64(ptr, o, n) \
({ \
BUILD_BUG_ON(sizeof(*(ptr)) != 8); \
arch_cmpxchg((ptr), (o), (n)); \
})
#ifdef CONFIG_AS_HAS_SCQ_EXTENSION
union __u128_halves {
u128 full;
struct {
u64 low;
u64 high;
};
};
#define system_has_cmpxchg128() cpu_opt(LOONGARCH_CPU_SCQ)
#define __arch_cmpxchg128(ptr, old, new, llsc_mb) \
({ \
union __u128_halves __old, __new, __ret; \
volatile u64 *__ptr = (volatile u64 *)(ptr); \
\
__old.full = (old); \
__new.full = (new); \
\
__asm__ __volatile__( \
"1: ll.d %0, %3 # 128-bit cmpxchg low \n" \
llsc_mb \
" ld.d %1, %4 # 128-bit cmpxchg high \n" \
" move $t0, %0 \n" \
" move $t1, %1 \n" \
" bne %0, %z5, 2f \n" \
" bne %1, %z6, 2f \n" \
" move $t0, %z7 \n" \
" move $t1, %z8 \n" \
"2: sc.q $t0, $t1, %2 \n" \
" beqz $t0, 1b \n" \
llsc_mb \
: "=&r" (__ret.low), "=&r" (__ret.high) \
: "r" (__ptr), \
"ZC" (__ptr[0]), "m" (__ptr[1]), \
"Jr" (__old.low), "Jr" (__old.high), \
"Jr" (__new.low), "Jr" (__new.high) \
: "t0", "t1", "memory"); \
\
__ret.full; \
})
#define arch_cmpxchg128(ptr, o, n) \
({ \
BUILD_BUG_ON(sizeof(*(ptr)) != 16); \
__arch_cmpxchg128(ptr, o, n, __WEAK_LLSC_MB); \
})
#define arch_cmpxchg128_local(ptr, o, n) \
({ \
BUILD_BUG_ON(sizeof(*(ptr)) != 16); \
__arch_cmpxchg128(ptr, o, n, ""); \
})
#endif /* CONFIG_AS_HAS_SCQ_EXTENSION */
#else
#include <asm-generic/cmpxchg-local.h>
#define arch_cmpxchg64_local(ptr, o, n) __generic_cmpxchg64_local((ptr), (o), (n))
#define arch_cmpxchg64(ptr, o, n) arch_cmpxchg64_local((ptr), (o), (n))
#endif
#endif /* __ASM_CMPXCHG_H */
@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*
* Derived from MIPS:
* Copyright (C) 2003, 2004 Ralf Baechle
* Copyright (C) 2004 Maciej W. Rozycki
*/
#ifndef __ASM_CPU_FEATURES_H
#define __ASM_CPU_FEATURES_H
#include <asm/cpu.h>
#include <asm/cpu-info.h>
#define cpu_opt(opt) (cpu_data[0].options & (opt))
#define cpu_has(feat) (cpu_data[0].options & BIT_ULL(feat))
#define cpu_has_loongarch (cpu_has_loongarch32 | cpu_has_loongarch64)
#define cpu_has_loongarch32 (cpu_data[0].isa_level & LOONGARCH_CPU_ISA_32BIT)
#define cpu_has_loongarch64 (cpu_data[0].isa_level & LOONGARCH_CPU_ISA_64BIT)
#ifdef CONFIG_32BIT
# define cpu_vabits 31
# define cpu_pabits 31
#endif
#ifdef CONFIG_64BIT
# define cpu_vabits cpu_data[0].vabits
# define cpu_pabits cpu_data[0].pabits
#endif
/*
* SMP assumption: Options of CPU 0 are a superset of all processors.
* This is true for all known LoongArch systems.
*/
#define cpu_has_cpucfg cpu_opt(LOONGARCH_CPU_CPUCFG)
#define cpu_has_lam cpu_opt(LOONGARCH_CPU_LAM)
#define cpu_has_lam_bh cpu_opt(LOONGARCH_CPU_LAM_BH)
#define cpu_has_scq cpu_opt(LOONGARCH_CPU_SCQ)
#define cpu_has_ual cpu_opt(LOONGARCH_CPU_UAL)
#define cpu_has_fpu cpu_opt(LOONGARCH_CPU_FPU)
#define cpu_has_lsx cpu_opt(LOONGARCH_CPU_LSX)
#define cpu_has_lasx cpu_opt(LOONGARCH_CPU_LASX)
#define cpu_has_crc32 cpu_opt(LOONGARCH_CPU_CRC32)
#define cpu_has_complex cpu_opt(LOONGARCH_CPU_COMPLEX)
#define cpu_has_crypto cpu_opt(LOONGARCH_CPU_CRYPTO)
#define cpu_has_lvz cpu_opt(LOONGARCH_CPU_LVZ)
#define cpu_has_lbt_x86 cpu_opt(LOONGARCH_CPU_LBT_X86)
#define cpu_has_lbt_arm cpu_opt(LOONGARCH_CPU_LBT_ARM)
#define cpu_has_lbt_mips cpu_opt(LOONGARCH_CPU_LBT_MIPS)
#define cpu_has_lbt (cpu_has_lbt_x86|cpu_has_lbt_arm|cpu_has_lbt_mips)
#define cpu_has_csr cpu_opt(LOONGARCH_CPU_CSR)
#define cpu_has_iocsr cpu_opt(LOONGARCH_CPU_IOCSR)
#define cpu_has_tlb cpu_opt(LOONGARCH_CPU_TLB)
#define cpu_has_watch cpu_opt(LOONGARCH_CPU_WATCH)
#define cpu_has_vint cpu_opt(LOONGARCH_CPU_VINT)
#define cpu_has_csripi cpu_opt(LOONGARCH_CPU_CSRIPI)
#define cpu_has_extioi cpu_opt(LOONGARCH_CPU_EXTIOI)
#define cpu_has_prefetch cpu_opt(LOONGARCH_CPU_PREFETCH)
#define cpu_has_pmp cpu_opt(LOONGARCH_CPU_PMP)
#define cpu_has_perf cpu_opt(LOONGARCH_CPU_PMP)
#define cpu_has_scalefreq cpu_opt(LOONGARCH_CPU_SCALEFREQ)
#define cpu_has_flatmode cpu_opt(LOONGARCH_CPU_FLATMODE)
#define cpu_has_eiodecode cpu_opt(LOONGARCH_CPU_EIODECODE)
#define cpu_has_guestid cpu_opt(LOONGARCH_CPU_GUESTID)
#define cpu_has_hypervisor cpu_opt(LOONGARCH_CPU_HYPERVISOR)
#define cpu_has_ptw cpu_opt(LOONGARCH_CPU_PTW)
#define cpu_has_lspw cpu_opt(LOONGARCH_CPU_LSPW)
#define cpu_has_msgint cpu_opt(LOONGARCH_CPU_MSGINT)
#define cpu_has_avecint cpu_opt(LOONGARCH_CPU_AVECINT)
#define cpu_has_redirectint cpu_opt(LOONGARCH_CPU_REDIRECTINT)
#endif /* __ASM_CPU_FEATURES_H */
@@ -0,0 +1,104 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __ASM_CPU_INFO_H
#define __ASM_CPU_INFO_H
#include <linux/cache.h>
#include <linux/types.h>
#include <asm/loongarch.h>
/* cache_desc->flags */
enum {
CACHE_PRESENT = (1 << 0),
CACHE_PRIVATE = (1 << 1), /* core private cache */
CACHE_INCLUSIVE = (1 << 2), /* include the inner level caches */
};
/*
* Descriptor for a cache
*/
struct cache_desc {
unsigned char type;
unsigned char level;
unsigned short sets; /* Number of lines per set */
unsigned char ways; /* Number of ways */
unsigned char linesz; /* Size of line in bytes */
unsigned char flags; /* Flags describing cache properties */
};
#define CACHE_LEVEL_MAX 3
#define CACHE_LEAVES_MAX 6
struct cpuinfo_loongarch {
u64 asid_cache;
unsigned long asid_mask;
/*
* Capability and feature descriptor structure for LoongArch CPU
*/
unsigned long long options;
unsigned int processor_id;
unsigned int fpu_vers;
unsigned int fpu_csr0;
unsigned int fpu_mask;
unsigned int cputype;
int isa_level;
int tlbsize;
int tlbsizemtlb;
int tlbsizestlbsets;
int tlbsizestlbways;
int cache_leaves_present; /* number of cache_leaves[] elements */
struct cache_desc cache_leaves[CACHE_LEAVES_MAX];
int core; /* physical core number in package */
int package;/* physical package number */
int global_id; /* physical global thread number */
int vabits; /* Virtual Address size in bits */
int pabits; /* Physical Address size in bits */
int timerbits; /* Width of arch timer in bits */
unsigned int ksave_mask; /* Usable KSave mask. */
unsigned int watch_dreg_count; /* Number data breakpoints */
unsigned int watch_ireg_count; /* Number instruction breakpoints */
unsigned int watch_reg_use_cnt; /* min(NUM_WATCH_REGS, watch_dreg_count + watch_ireg_count), Usable by ptrace */
} __aligned(SMP_CACHE_BYTES);
extern struct cpuinfo_loongarch cpu_data[];
#define boot_cpu_data cpu_data[0]
#define current_cpu_data cpu_data[smp_processor_id()]
#define raw_current_cpu_data cpu_data[raw_smp_processor_id()]
extern void cpu_probe(void);
extern const char *__cpu_family[];
extern const char *__cpu_full_name[];
#define cpu_family_string() __cpu_family[raw_smp_processor_id()]
#define cpu_full_name_string() __cpu_full_name[raw_smp_processor_id()]
static inline bool cpus_are_siblings(int cpua, int cpub)
{
struct cpuinfo_loongarch *infoa = &cpu_data[cpua];
struct cpuinfo_loongarch *infob = &cpu_data[cpub];
if (infoa->package != infob->package)
return false;
if (infoa->core != infob->core)
return false;
return true;
}
static inline unsigned long cpu_asid_mask(struct cpuinfo_loongarch *cpuinfo)
{
return cpuinfo->asid_mask;
}
static inline void set_cpu_asid_mask(struct cpuinfo_loongarch *cpuinfo,
unsigned long asid_mask)
{
cpuinfo->asid_mask = asid_mask;
}
#endif /* __ASM_CPU_INFO_H */
@@ -0,0 +1,166 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* cpu.h: Values of the PRID register used to match up
* various LoongArch CPU types.
*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_CPU_H
#define _ASM_CPU_H
/*
* As described in LoongArch specs from Loongson Technology, the PRID register
* (CPUCFG.00) has the following layout:
*
* +---------------+----------------+------------+--------------------+
* | Reserved | Company ID | Series ID | Product ID |
* +---------------+----------------+------------+--------------------+
* 31 24 23 16 15 12 11 0
*/
/*
* Assigned Company values for bits 23:16 of the PRID register.
*/
#define PRID_COMP_MASK 0xff0000
#define PRID_COMP_LOONGSON 0x140000
/*
* Assigned Series ID values for bits 15:12 of the PRID register. In order
* to detect a certain CPU type exactly eventually additional registers may
* need to be examined.
*/
#define PRID_SERIES_MASK 0xf000
#define PRID_SERIES_LA132 0x8000 /* Loongson 32bit */
#define PRID_SERIES_LA264 0xa000 /* Loongson 64bit, 2-issue */
#define PRID_SERIES_LA364 0xb000 /* Loongson 64bit, 3-issue */
#define PRID_SERIES_LA464 0xc000 /* Loongson 64bit, 4-issue */
#define PRID_SERIES_LA664 0xd000 /* Loongson 64bit, 6-issue */
/*
* Particular Product ID values for bits 11:0 of the PRID register.
*/
#define PRID_PRODUCT_MASK 0x0fff
#if !defined(__ASSEMBLER__)
enum cpu_type_enum {
CPU_UNKNOWN,
CPU_LOONGSON32,
CPU_LOONGSON64,
CPU_LAST
};
static inline char *id_to_core_name(unsigned int id)
{
if ((id & PRID_COMP_MASK) != PRID_COMP_LOONGSON)
return "Unknown";
switch (id & PRID_SERIES_MASK) {
case PRID_SERIES_LA132:
return "LA132";
case PRID_SERIES_LA264:
return "LA264";
case PRID_SERIES_LA364:
return "LA364";
case PRID_SERIES_LA464:
return "LA464";
case PRID_SERIES_LA664:
return "LA664";
default:
return "Unknown";
}
}
#endif /* !__ASSEMBLER__ */
/*
* ISA Level encodings
*
*/
#define LOONGARCH_CPU_ISA_LA32R 0x00000001
#define LOONGARCH_CPU_ISA_LA32S 0x00000002
#define LOONGARCH_CPU_ISA_LA64 0x00000004
#define LOONGARCH_CPU_ISA_32BIT (LOONGARCH_CPU_ISA_LA32R | LOONGARCH_CPU_ISA_LA32S)
#define LOONGARCH_CPU_ISA_64BIT LOONGARCH_CPU_ISA_LA64
/*
* CPU Option encodings
*/
#define CPU_FEATURE_CPUCFG 0 /* CPU has CPUCFG */
#define CPU_FEATURE_LAM 1 /* CPU has Atomic instructions */
#define CPU_FEATURE_LAM_BH 2 /* CPU has AM{SWAP/ADD}[_DB].{B/H} instructions */
#define CPU_FEATURE_SCQ 3 /* CPU has SC.Q instruction */
#define CPU_FEATURE_UAL 4 /* CPU supports unaligned access */
#define CPU_FEATURE_FPU 5 /* CPU has FPU */
#define CPU_FEATURE_LSX 6 /* CPU has LSX (128-bit SIMD) */
#define CPU_FEATURE_LASX 7 /* CPU has LASX (256-bit SIMD) */
#define CPU_FEATURE_CRC32 8 /* CPU has CRC32 instructions */
#define CPU_FEATURE_COMPLEX 9 /* CPU has Complex instructions */
#define CPU_FEATURE_CRYPTO 10 /* CPU has Crypto instructions */
#define CPU_FEATURE_LVZ 11 /* CPU has Virtualization extension */
#define CPU_FEATURE_LBT_X86 12 /* CPU has X86 Binary Translation */
#define CPU_FEATURE_LBT_ARM 13 /* CPU has ARM Binary Translation */
#define CPU_FEATURE_LBT_MIPS 14 /* CPU has MIPS Binary Translation */
#define CPU_FEATURE_TLB 15 /* CPU has TLB */
#define CPU_FEATURE_CSR 16 /* CPU has CSR */
#define CPU_FEATURE_IOCSR 17 /* CPU has IOCSR */
#define CPU_FEATURE_WATCH 18 /* CPU has watchpoint registers */
#define CPU_FEATURE_VINT 19 /* CPU has vectored interrupts */
#define CPU_FEATURE_CSRIPI 20 /* CPU has CSR-IPI */
#define CPU_FEATURE_EXTIOI 21 /* CPU has EXT-IOI */
#define CPU_FEATURE_PREFETCH 22 /* CPU has prefetch instructions */
#define CPU_FEATURE_PMP 23 /* CPU has perfermance counter */
#define CPU_FEATURE_SCALEFREQ 24 /* CPU supports cpufreq scaling */
#define CPU_FEATURE_FLATMODE 25 /* CPU has flat mode */
#define CPU_FEATURE_EIODECODE 26 /* CPU has EXTIOI interrupt pin decode mode */
#define CPU_FEATURE_GUESTID 27 /* CPU has GuestID feature */
#define CPU_FEATURE_HYPERVISOR 28 /* CPU has hypervisor (running in VM) */
#define CPU_FEATURE_PTW 29 /* CPU has hardware page table walker */
#define CPU_FEATURE_LSPW 30 /* CPU has LSPW (lddir/ldpte instructions) */
#define CPU_FEATURE_MSGINT 31 /* CPU has MSG interrupt */
#define CPU_FEATURE_AVECINT 32 /* CPU has AVEC interrupt */
#define CPU_FEATURE_REDIRECTINT 33 /* CPU has interrupt remapping */
#define LOONGARCH_CPU_CPUCFG BIT_ULL(CPU_FEATURE_CPUCFG)
#define LOONGARCH_CPU_LAM BIT_ULL(CPU_FEATURE_LAM)
#define LOONGARCH_CPU_LAM_BH BIT_ULL(CPU_FEATURE_LAM_BH)
#define LOONGARCH_CPU_SCQ BIT_ULL(CPU_FEATURE_SCQ)
#define LOONGARCH_CPU_UAL BIT_ULL(CPU_FEATURE_UAL)
#define LOONGARCH_CPU_FPU BIT_ULL(CPU_FEATURE_FPU)
#define LOONGARCH_CPU_LSX BIT_ULL(CPU_FEATURE_LSX)
#define LOONGARCH_CPU_LASX BIT_ULL(CPU_FEATURE_LASX)
#define LOONGARCH_CPU_CRC32 BIT_ULL(CPU_FEATURE_CRC32)
#define LOONGARCH_CPU_COMPLEX BIT_ULL(CPU_FEATURE_COMPLEX)
#define LOONGARCH_CPU_CRYPTO BIT_ULL(CPU_FEATURE_CRYPTO)
#define LOONGARCH_CPU_LVZ BIT_ULL(CPU_FEATURE_LVZ)
#define LOONGARCH_CPU_LBT_X86 BIT_ULL(CPU_FEATURE_LBT_X86)
#define LOONGARCH_CPU_LBT_ARM BIT_ULL(CPU_FEATURE_LBT_ARM)
#define LOONGARCH_CPU_LBT_MIPS BIT_ULL(CPU_FEATURE_LBT_MIPS)
#define LOONGARCH_CPU_TLB BIT_ULL(CPU_FEATURE_TLB)
#define LOONGARCH_CPU_IOCSR BIT_ULL(CPU_FEATURE_IOCSR)
#define LOONGARCH_CPU_CSR BIT_ULL(CPU_FEATURE_CSR)
#define LOONGARCH_CPU_WATCH BIT_ULL(CPU_FEATURE_WATCH)
#define LOONGARCH_CPU_VINT BIT_ULL(CPU_FEATURE_VINT)
#define LOONGARCH_CPU_CSRIPI BIT_ULL(CPU_FEATURE_CSRIPI)
#define LOONGARCH_CPU_EXTIOI BIT_ULL(CPU_FEATURE_EXTIOI)
#define LOONGARCH_CPU_PREFETCH BIT_ULL(CPU_FEATURE_PREFETCH)
#define LOONGARCH_CPU_PMP BIT_ULL(CPU_FEATURE_PMP)
#define LOONGARCH_CPU_SCALEFREQ BIT_ULL(CPU_FEATURE_SCALEFREQ)
#define LOONGARCH_CPU_FLATMODE BIT_ULL(CPU_FEATURE_FLATMODE)
#define LOONGARCH_CPU_EIODECODE BIT_ULL(CPU_FEATURE_EIODECODE)
#define LOONGARCH_CPU_GUESTID BIT_ULL(CPU_FEATURE_GUESTID)
#define LOONGARCH_CPU_HYPERVISOR BIT_ULL(CPU_FEATURE_HYPERVISOR)
#define LOONGARCH_CPU_PTW BIT_ULL(CPU_FEATURE_PTW)
#define LOONGARCH_CPU_LSPW BIT_ULL(CPU_FEATURE_LSPW)
#define LOONGARCH_CPU_MSGINT BIT_ULL(CPU_FEATURE_MSGINT)
#define LOONGARCH_CPU_AVECINT BIT_ULL(CPU_FEATURE_AVECINT)
#define LOONGARCH_CPU_REDIRECTINT BIT_ULL(CPU_FEATURE_REDIRECTINT)
#endif /* _ASM_CPU_H */
@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* CPU feature definitions for module loading, used by
* module_cpu_feature_match(), see uapi/asm/hwcap.h for LoongArch CPU features.
*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __ASM_CPUFEATURE_H
#define __ASM_CPUFEATURE_H
#include <uapi/asm/hwcap.h>
#include <asm/elf.h>
#define MAX_CPU_FEATURES (8 * sizeof(elf_hwcap))
#define cpu_feature(x) ilog2(HWCAP_ ## x)
static inline bool cpu_have_feature(unsigned int num)
{
return elf_hwcap & (1UL << num);
}
#endif /* __ASM_CPUFEATURE_H */
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef _LOONGARCH_CRASH_RESERVE_H
#define _LOONGARCH_CRASH_RESERVE_H
#define CRASH_ALIGN SZ_2M
#define CRASH_ADDR_LOW_MAX SZ_4G
#define CRASH_ADDR_HIGH_MAX memblock_end_of_DRAM()
extern phys_addr_t memblock_end_of_DRAM(void);
#endif
@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_LOONGARCH_CURRENT_H
#define __ASM_LOONGARCH_CURRENT_H
#include <linux/compiler.h>
#ifndef __ASSEMBLER__
#include <asm/percpu.h>
struct task_struct;
DECLARE_PER_CPU(struct task_struct *, cpu_tasks);
register struct task_struct *current_thread_pointer __asm__("$tp");
static __always_inline struct task_struct *get_current(void)
{
return current_thread_pointer;
}
#define current get_current()
static __always_inline void set_current(struct task_struct *task)
{
__this_cpu_write(cpu_tasks, task);
}
#endif /* __ASSEMBLER__ */
#endif /* __ASM_LOONGARCH_CURRENT_H */
@@ -0,0 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_DELAY_H
#define _ASM_DELAY_H
#include <linux/param.h>
extern void __delay(unsigned long cycles);
extern void __ndelay(unsigned long ns);
extern void __udelay(unsigned long us);
#define ndelay(ns) __ndelay(ns)
#define udelay(us) __udelay(us)
/* make sure "usecs *= ..." in udelay do not overflow. */
#if HZ >= 1000
#define MAX_UDELAY_MS 1
#elif HZ <= 200
#define MAX_UDELAY_MS 5
#else
#define MAX_UDELAY_MS (1000 / HZ)
#endif
#endif /* _ASM_DELAY_H */
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __ASM_DMA_H
#define __ASM_DMA_H
#define MAX_DMA_ADDRESS PAGE_OFFSET
#define MAX_DMA32_PFN (1UL << (32 - PAGE_SHIFT))
#endif
@@ -0,0 +1,24 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_DMI_H
#define _ASM_DMI_H
#include <linux/io.h>
#include <linux/memblock.h>
#define dmi_early_remap(x, l) dmi_remap(x, l)
#define dmi_early_unmap(x, l) dmi_unmap(x)
#define dmi_alloc(l) memblock_alloc(l, PAGE_SIZE)
static inline void *dmi_remap(phys_addr_t phys_addr, unsigned long size)
{
return ((void *)TO_CACHE(phys_addr));
}
static inline void dmi_unmap(void *addr)
{
}
#endif /* _ASM_DMI_H */
@@ -0,0 +1,37 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_LOONGARCH_EFI_H
#define _ASM_LOONGARCH_EFI_H
#include <linux/efi.h>
void __init efi_init(void);
void __init efi_runtime_init(void);
void __init *efi_fdt_pointer(void);
void efifb_setup_from_dmi(struct screen_info *si, const char *opt);
#define ARCH_EFI_IRQ_FLAGS_MASK 0x00000004 /* Bit 2: CSR.CRMD.IE */
#define arch_efi_call_virt_setup()
#define arch_efi_call_virt_teardown()
#define EFI_ALLOC_ALIGN SZ_64K
#define EFI_RT_VIRTUAL_OFFSET CSR_DMW0_BASE
static inline unsigned long efi_get_max_initrd_addr(unsigned long image_addr)
{
return ULONG_MAX;
}
static inline unsigned long efi_get_kimg_min_align(void)
{
return SZ_2M;
}
unsigned long efi_get_kimg_kaslr_address(void);
#define EFI_KIMG_PREFERRED_ADDRESS efi_get_kimg_kaslr_address()
#endif /* _ASM_LOONGARCH_EFI_H */
@@ -0,0 +1,371 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_ELF_H
#define _ASM_ELF_H
#include <linux/auxvec.h>
#include <linux/fs.h>
#include <uapi/linux/elf.h>
#include <asm/current.h>
#include <asm/vdso.h>
/* The ABI of a file. */
#define EF_LOONGARCH_ABI_LP64_SOFT_FLOAT 0x1
#define EF_LOONGARCH_ABI_LP64_SINGLE_FLOAT 0x2
#define EF_LOONGARCH_ABI_LP64_DOUBLE_FLOAT 0x3
#define EF_LOONGARCH_ABI_ILP32_SOFT_FLOAT 0x5
#define EF_LOONGARCH_ABI_ILP32_SINGLE_FLOAT 0x6
#define EF_LOONGARCH_ABI_ILP32_DOUBLE_FLOAT 0x7
/* LoongArch relocation types used by the dynamic linker */
#define R_LARCH_NONE 0
#define R_LARCH_32 1
#define R_LARCH_64 2
#define R_LARCH_RELATIVE 3
#define R_LARCH_COPY 4
#define R_LARCH_JUMP_SLOT 5
#define R_LARCH_TLS_DTPMOD32 6
#define R_LARCH_TLS_DTPMOD64 7
#define R_LARCH_TLS_DTPREL32 8
#define R_LARCH_TLS_DTPREL64 9
#define R_LARCH_TLS_TPREL32 10
#define R_LARCH_TLS_TPREL64 11
#define R_LARCH_IRELATIVE 12
#define R_LARCH_MARK_LA 20
#define R_LARCH_MARK_PCREL 21
#define R_LARCH_SOP_PUSH_PCREL 22
#define R_LARCH_SOP_PUSH_ABSOLUTE 23
#define R_LARCH_SOP_PUSH_DUP 24
#define R_LARCH_SOP_PUSH_GPREL 25
#define R_LARCH_SOP_PUSH_TLS_TPREL 26
#define R_LARCH_SOP_PUSH_TLS_GOT 27
#define R_LARCH_SOP_PUSH_TLS_GD 28
#define R_LARCH_SOP_PUSH_PLT_PCREL 29
#define R_LARCH_SOP_ASSERT 30
#define R_LARCH_SOP_NOT 31
#define R_LARCH_SOP_SUB 32
#define R_LARCH_SOP_SL 33
#define R_LARCH_SOP_SR 34
#define R_LARCH_SOP_ADD 35
#define R_LARCH_SOP_AND 36
#define R_LARCH_SOP_IF_ELSE 37
#define R_LARCH_SOP_POP_32_S_10_5 38
#define R_LARCH_SOP_POP_32_U_10_12 39
#define R_LARCH_SOP_POP_32_S_10_12 40
#define R_LARCH_SOP_POP_32_S_10_16 41
#define R_LARCH_SOP_POP_32_S_10_16_S2 42
#define R_LARCH_SOP_POP_32_S_5_20 43
#define R_LARCH_SOP_POP_32_S_0_5_10_16_S2 44
#define R_LARCH_SOP_POP_32_S_0_10_10_16_S2 45
#define R_LARCH_SOP_POP_32_U 46
#define R_LARCH_ADD8 47
#define R_LARCH_ADD16 48
#define R_LARCH_ADD24 49
#define R_LARCH_ADD32 50
#define R_LARCH_ADD64 51
#define R_LARCH_SUB8 52
#define R_LARCH_SUB16 53
#define R_LARCH_SUB24 54
#define R_LARCH_SUB32 55
#define R_LARCH_SUB64 56
#define R_LARCH_GNU_VTINHERIT 57
#define R_LARCH_GNU_VTENTRY 58
#define R_LARCH_B16 64
#define R_LARCH_B21 65
#define R_LARCH_B26 66
#define R_LARCH_ABS_HI20 67
#define R_LARCH_ABS_LO12 68
#define R_LARCH_ABS64_LO20 69
#define R_LARCH_ABS64_HI12 70
#define R_LARCH_PCALA_HI20 71
#define R_LARCH_PCALA_LO12 72
#define R_LARCH_PCALA64_LO20 73
#define R_LARCH_PCALA64_HI12 74
#define R_LARCH_GOT_PC_HI20 75
#define R_LARCH_GOT_PC_LO12 76
#define R_LARCH_GOT64_PC_LO20 77
#define R_LARCH_GOT64_PC_HI12 78
#define R_LARCH_GOT_HI20 79
#define R_LARCH_GOT_LO12 80
#define R_LARCH_GOT64_LO20 81
#define R_LARCH_GOT64_HI12 82
#define R_LARCH_TLS_LE_HI20 83
#define R_LARCH_TLS_LE_LO12 84
#define R_LARCH_TLS_LE64_LO20 85
#define R_LARCH_TLS_LE64_HI12 86
#define R_LARCH_TLS_IE_PC_HI20 87
#define R_LARCH_TLS_IE_PC_LO12 88
#define R_LARCH_TLS_IE64_PC_LO20 89
#define R_LARCH_TLS_IE64_PC_HI12 90
#define R_LARCH_TLS_IE_HI20 91
#define R_LARCH_TLS_IE_LO12 92
#define R_LARCH_TLS_IE64_LO20 93
#define R_LARCH_TLS_IE64_HI12 94
#define R_LARCH_TLS_LD_PC_HI20 95
#define R_LARCH_TLS_LD_HI20 96
#define R_LARCH_TLS_GD_PC_HI20 97
#define R_LARCH_TLS_GD_HI20 98
#define R_LARCH_32_PCREL 99
#define R_LARCH_RELAX 100
#define R_LARCH_DELETE 101
#define R_LARCH_ALIGN 102
#define R_LARCH_PCREL20_S2 103
#define R_LARCH_CFA 104
#define R_LARCH_ADD6 105
#define R_LARCH_SUB6 106
#define R_LARCH_ADD_ULEB128 107
#define R_LARCH_SUB_ULEB128 108
#define R_LARCH_64_PCREL 109
#define R_LARCH_CALL36 110
#define R_LARCH_TLS_DESC_PC_HI20 111
#define R_LARCH_TLS_DESC_PC_LO12 112
#define R_LARCH_TLS_DESC64_PC_LO20 113
#define R_LARCH_TLS_DESC64_PC_HI12 114
#define R_LARCH_TLS_DESC_HI20 115
#define R_LARCH_TLS_DESC_LO12 116
#define R_LARCH_TLS_DESC64_LO20 117
#define R_LARCH_TLS_DESC64_HI12 118
#define R_LARCH_TLS_DESC_LD 119
#define R_LARCH_TLS_DESC_CALL 120
#define R_LARCH_TLS_LE_HI20_R 121
#define R_LARCH_TLS_LE_ADD_R 122
#define R_LARCH_TLS_LE_LO12_R 123
#define R_LARCH_TLS_LD_PCREL20_S2 124
#define R_LARCH_TLS_GD_PCREL20_S2 125
#define R_LARCH_TLS_DESC_PCREL20_S2 126
#define R_LARCH_CALL30 127
#define R_LARCH_PCADD_HI20 128
#define R_LARCH_PCADD_LO12 129
#define R_LARCH_GOT_PCADD_HI20 130
#define R_LARCH_GOT_PCADD_LO12 131
#define R_LARCH_TLS_IE_PCADD_HI20 132
#define R_LARCH_TLS_IE_PCADD_LO12 133
#define R_LARCH_TLS_LD_PCADD_HI20 134
#define R_LARCH_TLS_LD_PCADD_LO12 135
#define R_LARCH_TLS_GD_PCADD_HI20 136
#define R_LARCH_TLS_GD_PCADD_LO12 137
#define R_LARCH_TLS_DESC_PCADD_HI20 138
#define R_LARCH_TLS_DESC_PCADD_LO12 139
#ifndef ELF_ARCH
/* ELF register definitions */
/*
* General purpose have the following registers:
* Register Number
* GPRs 32
* ORIG_A0 1
* ERA 1
* BADVADDR 1
* CRMD 1
* PRMD 1
* EUEN 1
* ECFG 1
* ESTAT 1
* Reserved 5
*/
#define ELF_NGREG 45
/*
* Floating point have the following registers:
* Register Number
* FPR 32
* FCC 1
* FCSR 1
*/
#define ELF_NFPREG 34
typedef unsigned long elf_greg_t;
typedef elf_greg_t elf_gregset_t[ELF_NGREG];
typedef double elf_fpreg_t;
typedef elf_fpreg_t elf_fpregset_t[ELF_NFPREG];
void loongarch_dump_regs32(u32 *uregs, const struct pt_regs *regs);
void loongarch_dump_regs64(u64 *uregs, const struct pt_regs *regs);
#ifdef CONFIG_32BIT
/*
* This is used to ensure we don't load something for the wrong architecture.
*/
#define elf_check_arch elf32_check_arch
/*
* These are used to set parameters in the core dumps.
*/
#define ELF_CLASS ELFCLASS32
#define ELF_CORE_COPY_REGS(dest, regs) \
loongarch_dump_regs32((u32 *)&(dest), (regs));
#endif /* CONFIG_32BIT */
#ifdef CONFIG_64BIT
/*
* This is used to ensure we don't load something for the wrong architecture.
*/
#define elf_check_arch elf64_check_arch
/*
* These are used to set parameters in the core dumps.
*/
#define ELF_CLASS ELFCLASS64
#define ELF_CORE_COPY_REGS(dest, regs) \
loongarch_dump_regs64((u64 *)&(dest), (regs));
#endif /* CONFIG_64BIT */
/*
* These are used to set parameters in the core dumps.
*/
#define ELF_DATA ELFDATA2LSB
#define ELF_ARCH EM_LOONGARCH
#endif /* !defined(ELF_ARCH) */
#define loongarch_elf_check_machine(x) ((x)->e_machine == EM_LOONGARCH)
#define vmcore_elf32_check_arch loongarch_elf_check_machine
#define vmcore_elf64_check_arch loongarch_elf_check_machine
/*
* Return non-zero if HDR identifies an 32bit ELF binary.
*/
#define elf32_check_arch(hdr) \
({ \
int __res = 1; \
struct elfhdr *__h = (hdr); \
\
if (!loongarch_elf_check_machine(__h)) \
__res = 0; \
if (__h->e_ident[EI_CLASS] != ELFCLASS32) \
__res = 0; \
\
__res; \
})
/*
* Return non-zero if HDR identifies an 64bit ELF binary.
*/
#define elf64_check_arch(hdr) \
({ \
int __res = 1; \
struct elfhdr *__h = (hdr); \
\
if (!loongarch_elf_check_machine(__h)) \
__res = 0; \
if (__h->e_ident[EI_CLASS] != ELFCLASS64) \
__res = 0; \
\
__res; \
})
#ifdef CONFIG_32BIT
#define SET_PERSONALITY2(ex, state) \
do { \
current->thread.vdso = &vdso_info; \
\
if (personality(current->personality) != PER_LINUX) \
set_personality(PER_LINUX); \
} while (0)
#endif /* CONFIG_32BIT */
#ifdef CONFIG_64BIT
#define SET_PERSONALITY2(ex, state) \
do { \
unsigned int p; \
\
clear_thread_flag(TIF_32BIT_REGS); \
clear_thread_flag(TIF_32BIT_ADDR); \
\
current->thread.vdso = &vdso_info; \
\
p = personality(current->personality); \
if (p != PER_LINUX32 && p != PER_LINUX) \
set_personality(PER_LINUX); \
} while (0)
#endif /* CONFIG_64BIT */
#define CORE_DUMP_USE_REGSET
#define ELF_EXEC_PAGESIZE PAGE_SIZE
/*
* This yields a mask that user programs can use to figure out what
* instruction set this cpu supports. This could be done in userspace,
* but it's not easy, and we've already done it here.
*/
#define ELF_HWCAP (elf_hwcap)
extern unsigned int elf_hwcap;
#include <asm/hwcap.h>
/*
* This yields a string that ld.so will use to load implementation
* specific libraries for optimization. This is more specific in
* intent than poking at uname or /proc/cpuinfo.
*/
#define ELF_PLATFORM __elf_platform
extern const char *__elf_platform;
#define ELF_PLAT_INIT(_r, load_addr) do { \
_r->regs[1] = _r->regs[2] = _r->regs[3] = _r->regs[4] = 0; \
_r->regs[5] = _r->regs[6] = _r->regs[7] = _r->regs[8] = 0; \
_r->regs[9] = _r->regs[10] /* syscall n */ = _r->regs[12] = 0; \
_r->regs[13] = _r->regs[14] = _r->regs[15] = _r->regs[16] = 0; \
_r->regs[17] = _r->regs[18] = _r->regs[19] = _r->regs[20] = 0; \
_r->regs[21] = _r->regs[22] = _r->regs[23] = _r->regs[24] = 0; \
_r->regs[25] = _r->regs[26] = _r->regs[27] = _r->regs[28] = 0; \
_r->regs[29] = _r->regs[30] = _r->regs[31] = 0; \
} while (0)
/*
* This is the location that an ET_DYN program is loaded if exec'ed. Typical
* use of this is to invoke "./ld.so someprog" to test out a new version of
* the loader. We need to make sure that it is out of the way of the program
* that it will "exec", and that there is sufficient room for the brk.
*/
#define ELF_ET_DYN_BASE (TASK_SIZE / 3 * 2)
/* update AT_VECTOR_SIZE_ARCH if the number of NEW_AUX_ENT entries changes */
#define ARCH_DLINFO \
do { \
NEW_AUX_ENT(AT_SYSINFO_EHDR, \
(unsigned long)current->mm->context.vdso); \
} while (0)
#define ARCH_HAS_SETUP_ADDITIONAL_PAGES 1
struct linux_binprm;
extern int arch_setup_additional_pages(struct linux_binprm *bprm,
int uses_interp);
struct arch_elf_state {
int fp_abi;
int interp_fp_abi;
};
#define LOONGARCH_ABI_FP_ANY (0)
#define INIT_ARCH_ELF_STATE { \
.fp_abi = LOONGARCH_ABI_FP_ANY, \
.interp_fp_abi = LOONGARCH_ABI_FP_ANY, \
}
extern int arch_elf_pt_proc(void *ehdr, void *phdr, struct file *elf,
bool is_interp, struct arch_elf_state *state);
extern int arch_check_elf(void *ehdr, bool has_interpreter, void *interp_ehdr,
struct arch_elf_state *state);
#endif /* _ASM_ELF_H */
@@ -0,0 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef ARCH_LOONGARCH_ENTRY_COMMON_H
#define ARCH_LOONGARCH_ENTRY_COMMON_H
#include <asm/stacktrace.h> /* For on_thread_stack() */
#endif
@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __ASM_EXCEPTION_H
#define __ASM_EXCEPTION_H
#include <asm/ptrace.h>
#include <linux/kprobes.h>
extern void *exception_table[];
void show_registers(struct pt_regs *regs);
asmlinkage void cache_parity_error(void);
asmlinkage void noinstr do_ade(struct pt_regs *regs);
asmlinkage void noinstr do_ale(struct pt_regs *regs);
asmlinkage void noinstr do_bce(struct pt_regs *regs);
asmlinkage void noinstr do_bp(struct pt_regs *regs);
asmlinkage void noinstr do_ri(struct pt_regs *regs);
asmlinkage void noinstr do_fpu(struct pt_regs *regs);
asmlinkage void noinstr do_fpe(struct pt_regs *regs, unsigned long fcsr);
asmlinkage void noinstr do_lsx(struct pt_regs *regs);
asmlinkage void noinstr do_lasx(struct pt_regs *regs);
asmlinkage void noinstr do_lbt(struct pt_regs *regs);
asmlinkage void noinstr do_watch(struct pt_regs *regs);
asmlinkage void noinstr do_syscall(struct pt_regs *regs);
asmlinkage void noinstr do_reserved(struct pt_regs *regs);
asmlinkage void noinstr do_vint(struct pt_regs *regs, unsigned long sp);
asmlinkage void __kprobes do_page_fault(struct pt_regs *regs,
unsigned long write, unsigned long address);
asmlinkage void handle_ade(void);
asmlinkage void handle_ale(void);
asmlinkage void handle_bce(void);
asmlinkage void handle_sys(void);
asmlinkage void handle_bp(void);
asmlinkage void handle_ri(void);
asmlinkage void handle_fpu(void);
asmlinkage void handle_fpe(void);
asmlinkage void handle_lsx(void);
asmlinkage void handle_lasx(void);
asmlinkage void handle_lbt(void);
asmlinkage void handle_watch(void);
asmlinkage void handle_reserved(void);
asmlinkage void handle_vint(void);
asmlinkage void noinstr handle_loongarch_irq(struct pt_regs *regs);
#endif /* __ASM_EXCEPTION_H */
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_EXEC_H
#define _ASM_EXEC_H
extern unsigned long arch_align_stack(unsigned long sp);
#endif /* _ASM_EXEC_H */
@@ -0,0 +1,47 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_LOONGARCH_EXTABLE_H
#define _ASM_LOONGARCH_EXTABLE_H
/*
* The exception table consists of pairs of relative offsets: the first
* is the relative offset to an instruction that is allowed to fault,
* and the second is the relative offset at which the program should
* continue. No registers are modified, so it is entirely up to the
* continuation code to figure out what to do.
*
* All the routines below use bits of fixup code that are out of line
* with the main instruction path. This means when everything is well,
* we don't even have to jump over them. Further, they do not intrude
* on our cache or tlb entries.
*/
struct exception_table_entry {
int insn, fixup;
short type, data;
};
#define ARCH_HAS_RELATIVE_EXTABLE
#define swap_ex_entry_fixup(a, b, tmp, delta) \
do { \
(a)->fixup = (b)->fixup + (delta); \
(b)->fixup = (tmp).fixup - (delta); \
(a)->type = (b)->type; \
(b)->type = (tmp).type; \
(a)->data = (b)->data; \
(b)->data = (tmp).data; \
} while (0)
#ifdef CONFIG_BPF_JIT
bool ex_handler_bpf(const struct exception_table_entry *ex, struct pt_regs *regs);
#else
static inline
bool ex_handler_bpf(const struct exception_table_entry *ex, struct pt_regs *regs)
{
return false;
}
#endif /* !CONFIG_BPF_JIT */
bool fixup_exception(struct pt_regs *regs);
#endif
@@ -0,0 +1,42 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* fixmap.h: compile-time virtual memory allocation
*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_FIXMAP_H
#define _ASM_FIXMAP_H
#ifdef CONFIG_HIGHMEM
#include <linux/threads.h>
#include <asm/kmap_size.h>
#endif
#define NR_FIX_BTMAPS 64
enum fixed_addresses {
FIX_HOLE,
#ifdef CONFIG_HIGHMEM
FIX_KMAP_BEGIN,
FIX_KMAP_END = FIX_KMAP_BEGIN + (KM_MAX_IDX * NR_CPUS) - 1,
#endif
FIX_EARLYCON_MEM_BASE,
__end_of_fixed_addresses
};
#define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT)
#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE)
#define FIXMAP_PAGE_IO PAGE_KERNEL_SUC
extern void __set_fixmap(enum fixed_addresses idx,
phys_addr_t phys, pgprot_t flags);
#include <asm-generic/fixmap.h>
/*
* Called from pagetable_init()
*/
extern void fixrange_init(unsigned long start, unsigned long end, pgd_t *pgd_base);
#endif
@@ -0,0 +1,59 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Definitions for the FPU register names
*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_FPREGDEF_H
#define _ASM_FPREGDEF_H
#define fa0 $f0 /* argument registers, fa0/fa1 reused as fv0/fv1 for return value */
#define fa1 $f1
#define fa2 $f2
#define fa3 $f3
#define fa4 $f4
#define fa5 $f5
#define fa6 $f6
#define fa7 $f7
#define ft0 $f8 /* caller saved */
#define ft1 $f9
#define ft2 $f10
#define ft3 $f11
#define ft4 $f12
#define ft5 $f13
#define ft6 $f14
#define ft7 $f15
#define ft8 $f16
#define ft9 $f17
#define ft10 $f18
#define ft11 $f19
#define ft12 $f20
#define ft13 $f21
#define ft14 $f22
#define ft15 $f23
#define fs0 $f24 /* callee saved */
#define fs1 $f25
#define fs2 $f26
#define fs3 $f27
#define fs4 $f28
#define fs5 $f29
#define fs6 $f30
#define fs7 $f31
#ifndef CONFIG_AS_HAS_FCSR_CLASS
/*
* Current binutils expects *GPRs* at FCSR position for the FCSR
* operation instructions, so define aliases for those used.
*/
#define fcsr0 $r0
#define fcsr1 $r1
#define fcsr2 $r2
#define fcsr3 $r3
#else
#define fcsr0 $fcsr0
#define fcsr1 $fcsr1
#define fcsr2 $fcsr2
#define fcsr3 $fcsr3
#endif
#endif /* _ASM_FPREGDEF_H */
@@ -0,0 +1,12 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_LOONGARCH_FPROBE_H
#define _ASM_LOONGARCH_FPROBE_H
/*
* Explicitly undef ARCH_DEFINE_ENCODE_FPROBE_HEADER, because loongarch does not
* have enough number of fixed MSBs of the address of kernel objects for
* encoding the size of data in fprobe_header. Use 2-entries encoding instead.
*/
#undef ARCH_DEFINE_ENCODE_FPROBE_HEADER
#endif /* _ASM_LOONGARCH_FPROBE_H */
@@ -0,0 +1,326 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Author: Huacai Chen <chenhuacai@loongson.cn>
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_FPU_H
#define _ASM_FPU_H
#include <linux/sched.h>
#include <linux/sched/task_stack.h>
#include <linux/ptrace.h>
#include <linux/thread_info.h>
#include <linux/bitops.h>
#include <asm/cpu.h>
#include <asm/cpu-features.h>
#include <asm/current.h>
#include <asm/loongarch.h>
#include <asm/processor.h>
#include <asm/ptrace.h>
struct sigcontext;
#define kernel_fpu_available() cpu_has_fpu
void kernel_fpu_begin(void);
void kernel_fpu_end(void);
asmlinkage void _init_fpu(unsigned int);
asmlinkage void _save_fp(struct loongarch_fpu *);
asmlinkage void _restore_fp(struct loongarch_fpu *);
asmlinkage int _save_fp_context(void __user *fpregs, void __user *fcc, void __user *csr);
asmlinkage int _restore_fp_context(void __user *fpregs, void __user *fcc, void __user *csr);
asmlinkage void _save_lsx(struct loongarch_fpu *fpu);
asmlinkage void _restore_lsx(struct loongarch_fpu *fpu);
asmlinkage void _init_lsx_upper(void);
asmlinkage void _restore_lsx_upper(struct loongarch_fpu *fpu);
asmlinkage int _save_lsx_context(void __user *fpregs, void __user *fcc, void __user *fcsr);
asmlinkage int _restore_lsx_context(void __user *fpregs, void __user *fcc, void __user *fcsr);
asmlinkage void _save_lasx(struct loongarch_fpu *fpu);
asmlinkage void _restore_lasx(struct loongarch_fpu *fpu);
asmlinkage void _init_lasx_upper(void);
asmlinkage void _restore_lasx_upper(struct loongarch_fpu *fpu);
asmlinkage int _save_lasx_context(void __user *fpregs, void __user *fcc, void __user *fcsr);
asmlinkage int _restore_lasx_context(void __user *fpregs, void __user *fcc, void __user *fcsr);
static inline void enable_lsx(void);
static inline void disable_lsx(void);
static inline void save_lsx(struct task_struct *t);
static inline void restore_lsx(struct task_struct *t);
static inline void enable_lasx(void);
static inline void disable_lasx(void);
static inline void save_lasx(struct task_struct *t);
static inline void restore_lasx(struct task_struct *t);
/*
* Mask the FCSR Cause bits according to the Enable bits, observing
* that Unimplemented is always enabled.
*/
static inline unsigned long mask_fcsr_x(unsigned long fcsr)
{
return fcsr & ((fcsr & FPU_CSR_ALL_E) <<
(ffs(FPU_CSR_ALL_X) - ffs(FPU_CSR_ALL_E)));
}
static inline int is_fp_enabled(void)
{
return (csr_read32(LOONGARCH_CSR_EUEN) & CSR_EUEN_FPEN) ?
1 : 0;
}
static inline int is_lsx_enabled(void)
{
if (!cpu_has_lsx)
return 0;
return (csr_read32(LOONGARCH_CSR_EUEN) & CSR_EUEN_LSXEN) ?
1 : 0;
}
static inline int is_lasx_enabled(void)
{
if (!cpu_has_lasx)
return 0;
return (csr_read32(LOONGARCH_CSR_EUEN) & CSR_EUEN_LASXEN) ?
1 : 0;
}
static inline int is_simd_enabled(void)
{
return is_lsx_enabled() | is_lasx_enabled();
}
#define enable_fpu() set_csr_euen(CSR_EUEN_FPEN)
#define disable_fpu() clear_csr_euen(CSR_EUEN_FPEN)
#define clear_fpu_owner() clear_thread_flag(TIF_USEDFPU)
static inline int is_fpu_owner(void)
{
return test_thread_flag(TIF_USEDFPU);
}
static inline void __own_fpu(void)
{
enable_fpu();
set_thread_flag(TIF_USEDFPU);
KSTK_EUEN(current) |= CSR_EUEN_FPEN;
}
static inline void own_fpu_inatomic(int restore)
{
if (cpu_has_fpu && !is_fpu_owner()) {
__own_fpu();
if (restore)
_restore_fp(&current->thread.fpu);
}
}
static inline void own_fpu(int restore)
{
preempt_disable();
own_fpu_inatomic(restore);
preempt_enable();
}
static inline void lose_fpu_inatomic(int save, struct task_struct *tsk)
{
if (is_fpu_owner()) {
if (!is_simd_enabled()) {
if (save)
_save_fp(&tsk->thread.fpu);
disable_fpu();
} else {
if (save) {
if (!is_lasx_enabled())
save_lsx(tsk);
else
save_lasx(tsk);
}
disable_fpu();
disable_lsx();
disable_lasx();
clear_tsk_thread_flag(tsk, TIF_USEDSIMD);
}
clear_tsk_thread_flag(tsk, TIF_USEDFPU);
}
KSTK_EUEN(tsk) &= ~(CSR_EUEN_FPEN | CSR_EUEN_LSXEN | CSR_EUEN_LASXEN);
}
static inline void lose_fpu(int save)
{
preempt_disable();
lose_fpu_inatomic(save, current);
preempt_enable();
}
static inline void init_fpu(void)
{
unsigned int fcsr = current->thread.fpu.fcsr;
__own_fpu();
_init_fpu(fcsr);
set_used_math();
}
static inline void save_fp(struct task_struct *tsk)
{
if (cpu_has_fpu)
_save_fp(&tsk->thread.fpu);
}
static inline void restore_fp(struct task_struct *tsk)
{
if (cpu_has_fpu)
_restore_fp(&tsk->thread.fpu);
}
static inline void save_fpu_regs(struct task_struct *tsk)
{
unsigned int euen;
if (tsk == current) {
preempt_disable();
euen = csr_read32(LOONGARCH_CSR_EUEN);
#ifdef CONFIG_CPU_HAS_LASX
if (euen & CSR_EUEN_LASXEN)
_save_lasx(&current->thread.fpu);
else
#endif
#ifdef CONFIG_CPU_HAS_LSX
if (euen & CSR_EUEN_LSXEN)
_save_lsx(&current->thread.fpu);
else
#endif
if (euen & CSR_EUEN_FPEN)
_save_fp(&current->thread.fpu);
preempt_enable();
}
}
static inline int is_simd_owner(void)
{
return test_thread_flag(TIF_USEDSIMD);
}
#ifdef CONFIG_CPU_HAS_LSX
static inline void enable_lsx(void)
{
if (cpu_has_lsx)
csr_xchg32(CSR_EUEN_LSXEN, CSR_EUEN_LSXEN, LOONGARCH_CSR_EUEN);
}
static inline void disable_lsx(void)
{
if (cpu_has_lsx)
csr_xchg32(0, CSR_EUEN_LSXEN, LOONGARCH_CSR_EUEN);
}
static inline void save_lsx(struct task_struct *t)
{
if (cpu_has_lsx)
_save_lsx(&t->thread.fpu);
}
static inline void restore_lsx(struct task_struct *t)
{
if (cpu_has_lsx)
_restore_lsx(&t->thread.fpu);
}
static inline void init_lsx_upper(void)
{
if (cpu_has_lsx)
_init_lsx_upper();
}
static inline void restore_lsx_upper(struct task_struct *t)
{
if (cpu_has_lsx)
_restore_lsx_upper(&t->thread.fpu);
}
#else
static inline void enable_lsx(void) {}
static inline void disable_lsx(void) {}
static inline void save_lsx(struct task_struct *t) {}
static inline void restore_lsx(struct task_struct *t) {}
static inline void init_lsx_upper(void) {}
static inline void restore_lsx_upper(struct task_struct *t) {}
#endif
#ifdef CONFIG_CPU_HAS_LASX
static inline void enable_lasx(void)
{
if (cpu_has_lasx)
csr_xchg32(CSR_EUEN_LASXEN, CSR_EUEN_LASXEN, LOONGARCH_CSR_EUEN);
}
static inline void disable_lasx(void)
{
if (cpu_has_lasx)
csr_xchg32(0, CSR_EUEN_LASXEN, LOONGARCH_CSR_EUEN);
}
static inline void save_lasx(struct task_struct *t)
{
if (cpu_has_lasx)
_save_lasx(&t->thread.fpu);
}
static inline void restore_lasx(struct task_struct *t)
{
if (cpu_has_lasx)
_restore_lasx(&t->thread.fpu);
}
static inline void init_lasx_upper(void)
{
if (cpu_has_lasx)
_init_lasx_upper();
}
static inline void restore_lasx_upper(struct task_struct *t)
{
if (cpu_has_lasx)
_restore_lasx_upper(&t->thread.fpu);
}
#else
static inline void enable_lasx(void) {}
static inline void disable_lasx(void) {}
static inline void save_lasx(struct task_struct *t) {}
static inline void restore_lasx(struct task_struct *t) {}
static inline void init_lasx_upper(void) {}
static inline void restore_lasx_upper(struct task_struct *t) {}
#endif
static inline int thread_lsx_context_live(void)
{
if (!cpu_has_lsx)
return 0;
return test_thread_flag(TIF_LSX_CTX_LIVE);
}
static inline int thread_lasx_context_live(void)
{
if (!cpu_has_lasx)
return 0;
return test_thread_flag(TIF_LASX_CTX_LIVE);
}
#endif /* _ASM_FPU_H */
@@ -0,0 +1,91 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_LOONGARCH_FTRACE_H
#define _ASM_LOONGARCH_FTRACE_H
#define FTRACE_PLT_IDX 0
#define FTRACE_REGS_PLT_IDX 1
#define NR_FTRACE_PLTS 2
#ifdef CONFIG_FUNCTION_TRACER
#define MCOUNT_INSN_SIZE 4 /* sizeof mcount call */
#ifndef __ASSEMBLER__
#ifndef CONFIG_DYNAMIC_FTRACE
#define mcount _mcount
extern void _mcount(void);
extern void prepare_ftrace_return(unsigned long self_addr, unsigned long callsite_sp, unsigned long old);
#else
struct dyn_ftrace;
struct dyn_arch_ftrace { };
#define ARCH_SUPPORTS_FTRACE_OPS 1
#define ftrace_init_nop ftrace_init_nop
int ftrace_init_nop(struct module *mod, struct dyn_ftrace *rec);
static inline unsigned long ftrace_call_adjust(unsigned long addr)
{
return addr;
}
void prepare_ftrace_return(unsigned long self_addr, unsigned long *parent);
#endif /* CONFIG_DYNAMIC_FTRACE */
#ifdef CONFIG_HAVE_DYNAMIC_FTRACE_WITH_ARGS
struct ftrace_ops;
#include <linux/ftrace_regs.h>
static __always_inline struct pt_regs *arch_ftrace_get_regs(struct ftrace_regs *fregs)
{
return &arch_ftrace_regs(fregs)->regs;
}
static __always_inline void
ftrace_regs_set_instruction_pointer(struct ftrace_regs *fregs, unsigned long ip)
{
instruction_pointer_set(&arch_ftrace_regs(fregs)->regs, ip);
}
#undef ftrace_regs_get_frame_pointer
#define ftrace_regs_get_frame_pointer(fregs) \
(arch_ftrace_regs(fregs)->regs.regs[22])
static __always_inline unsigned long
ftrace_regs_get_return_address(struct ftrace_regs *fregs)
{
return *(unsigned long *)(arch_ftrace_regs(fregs)->regs.regs[1]);
}
#define ftrace_graph_func ftrace_graph_func
void ftrace_graph_func(unsigned long ip, unsigned long parent_ip,
struct ftrace_ops *op, struct ftrace_regs *fregs);
#ifdef CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS
static inline void
__arch_ftrace_set_direct_caller(struct pt_regs *regs, unsigned long addr)
{
regs->regs[13] = addr; /* t1 */
}
#define arch_ftrace_set_direct_caller(fregs, addr) \
__arch_ftrace_set_direct_caller(&arch_ftrace_regs(fregs)->regs, addr)
#endif /* CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS */
#endif
#endif /* __ASSEMBLER__ */
#endif /* CONFIG_FUNCTION_TRACER */
#endif /* _ASM_LOONGARCH_FTRACE_H */
@@ -0,0 +1,94 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_FUTEX_H
#define _ASM_FUTEX_H
#include <linux/futex.h>
#include <linux/uaccess.h>
#include <asm/asm-extable.h>
#include <asm/barrier.h>
#include <asm/errno.h>
#define __futex_atomic_op(insn, ret, oldval, uaddr, oparg) \
{ \
__asm__ __volatile__( \
"1: ll.w %1, %4 # __futex_atomic_op\n" \
" " insn " \n" \
"2: sc.w $t0, %2 \n" \
" beqz $t0, 1b \n" \
"3: \n" \
_ASM_EXTABLE_UACCESS_ERR(1b, 3b, %0) \
_ASM_EXTABLE_UACCESS_ERR(2b, 3b, %0) \
: "=r" (ret), "=&r" (oldval), \
"=ZC" (*uaddr) \
: "0" (0), "ZC" (*uaddr), "Jr" (oparg) \
: "memory", "t0"); \
}
static inline int
arch_futex_atomic_op_inuser(int op, int oparg, int *oval, u32 __user *uaddr)
{
int oldval = 0, ret = 0;
pagefault_disable();
switch (op) {
case FUTEX_OP_SET:
__futex_atomic_op("move $t0, %z5", ret, oldval, uaddr, oparg);
break;
case FUTEX_OP_ADD:
__futex_atomic_op("add.w $t0, %1, %z5", ret, oldval, uaddr, oparg);
break;
case FUTEX_OP_OR:
__futex_atomic_op("or $t0, %1, %z5", ret, oldval, uaddr, oparg);
break;
case FUTEX_OP_ANDN:
__futex_atomic_op("and $t0, %1, %z5", ret, oldval, uaddr, ~oparg);
break;
case FUTEX_OP_XOR:
__futex_atomic_op("xor $t0, %1, %z5", ret, oldval, uaddr, oparg);
break;
default:
ret = -ENOSYS;
}
pagefault_enable();
if (!ret)
*oval = oldval;
return ret;
}
static inline int
futex_atomic_cmpxchg_inatomic(u32 *uval, u32 __user *uaddr, u32 oldval, u32 newval)
{
int ret = 0;
u32 val = 0;
if (!access_ok(uaddr, sizeof(u32)))
return -EFAULT;
__asm__ __volatile__(
"# futex_atomic_cmpxchg_inatomic \n"
"1: ll.w %1, %3 \n"
" bne %1, %z4, 3f \n"
" move $t0, %z5 \n"
"2: sc.w $t0, %2 \n"
" beqz $t0, 1b \n"
"3: \n"
__WEAK_LLSC_MB
_ASM_EXTABLE_UACCESS_ERR(1b, 3b, %0)
_ASM_EXTABLE_UACCESS_ERR(2b, 3b, %0)
: "+r" (ret), "=&r" (val), "=ZC" (*uaddr)
: "ZC" (*uaddr), "Jr" (oldval), "Jr" (newval)
: "memory", "t0");
*uval = val;
return ret;
}
#endif /* _ASM_FUTEX_H */
@@ -0,0 +1,52 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __ASM_GPR_NUM_H
#define __ASM_GPR_NUM_H
#ifdef __ASSEMBLER__
.equ .L__gpr_num_zero, 0
.irp num,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31
.equ .L__gpr_num_$r\num, \num
.endr
/* ABI names of registers */
.equ .L__gpr_num_$ra, 1
.equ .L__gpr_num_$tp, 2
.equ .L__gpr_num_$sp, 3
.irp num,0,1,2,3,4,5,6,7
.equ .L__gpr_num_$a\num, 4 + \num
.endr
.irp num,0,1,2,3,4,5,6,7,8
.equ .L__gpr_num_$t\num, 12 + \num
.endr
.equ .L__gpr_num_$s9, 22
.equ .L__gpr_num_$fp, 22
.irp num,0,1,2,3,4,5,6,7,8
.equ .L__gpr_num_$s\num, 23 + \num
.endr
#else /* __ASSEMBLER__ */
#define __DEFINE_ASM_GPR_NUMS \
" .equ .L__gpr_num_zero, 0\n" \
" .irp num,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31\n" \
" .equ .L__gpr_num_$r\\num, \\num\n" \
" .endr\n" \
" .equ .L__gpr_num_$ra, 1\n" \
" .equ .L__gpr_num_$tp, 2\n" \
" .equ .L__gpr_num_$sp, 3\n" \
" .irp num,0,1,2,3,4,5,6,7\n" \
" .equ .L__gpr_num_$a\\num, 4 + \\num\n" \
" .endr\n" \
" .irp num,0,1,2,3,4,5,6,7,8\n" \
" .equ .L__gpr_num_$t\\num, 12 + \\num\n" \
" .endr\n" \
" .equ .L__gpr_num_$s9, 22\n" \
" .equ .L__gpr_num_$fp, 22\n" \
" .irp num,0,1,2,3,4,5,6,7,8\n" \
" .equ .L__gpr_num_$s\\num, 23 + \\num\n" \
" .endr\n" \
#endif /* __ASSEMBLER__ */
#endif /* __ASM_GPR_NUM_H */
@@ -0,0 +1,34 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_HARDIRQ_H
#define _ASM_HARDIRQ_H
#include <linux/cache.h>
#include <linux/threads.h>
#include <linux/irq.h>
extern void ack_bad_irq(unsigned int irq);
#define ack_bad_irq ack_bad_irq
#define NR_IPI 4
enum ipi_msg_type {
IPI_RESCHEDULE,
IPI_CALL_FUNCTION,
IPI_IRQ_WORK,
IPI_CLEAR_VECTOR,
};
typedef struct {
unsigned int ipi_irqs[NR_IPI];
unsigned int __softirq_pending;
atomic_t message ____cacheline_aligned_in_smp;
} ____cacheline_aligned irq_cpustat_t;
DECLARE_PER_CPU_SHARED_ALIGNED(irq_cpustat_t, irq_stat);
#define __ARCH_IRQ_STAT
#endif /* _ASM_HARDIRQ_H */
@@ -0,0 +1,43 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* highmem.h: virtual kernel memory mappings for high memory
*
* Used in CONFIG_HIGHMEM systems for memory pages which
* are not addressable by direct kernel virtual addresses.
*
* Copyright (C) 2025 Loongson Technology Corporation Limited
*/
#ifndef _ASM_HIGHMEM_H
#define _ASM_HIGHMEM_H
#ifdef __KERNEL__
#include <asm/kmap_size.h>
#ifndef __ASSEMBLER__
extern pte_t *pkmap_page_table;
#define ARCH_HAS_KMAP_FLUSH_TLB
void kmap_flush_tlb(unsigned long addr);
#endif /* !__ASSEMBLER__ */
/*
* Right now we initialize only a single pte table. It can be extended
* easily, subsequent pte tables have to be allocated in one physical
* chunk of RAM.
*/
#define LAST_PKMAP 1024
#define LAST_PKMAP_MASK (LAST_PKMAP - 1)
#define PKMAP_NR(virt) ((virt - PKMAP_BASE) >> PAGE_SHIFT)
#define PKMAP_ADDR(nr) (PKMAP_BASE + ((nr) << PAGE_SHIFT))
#define flush_cache_kmaps() do {} while (0)
#define arch_kmap_local_post_map(vaddr, pteval) local_flush_tlb_one(vaddr)
#define arch_kmap_local_post_unmap(vaddr) local_flush_tlb_one(vaddr)
#endif /* __KERNEL__ */
#endif /* _ASM_HIGHMEM_H */
@@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __ASM_HUGETLB_H
#define __ASM_HUGETLB_H
#include <asm/page.h>
uint64_t pmd_to_entrylo(unsigned long pmd_val);
#define __HAVE_ARCH_HUGE_PTE_CLEAR
static inline void huge_pte_clear(struct mm_struct *mm, unsigned long addr,
pte_t *ptep, unsigned long sz)
{
pte_t clear;
pte_val(clear) = (unsigned long)invalid_pte_table;
set_pte_at(mm, addr, ptep, clear);
}
#define __HAVE_ARCH_HUGE_PTEP_GET_AND_CLEAR
static inline pte_t huge_ptep_get_and_clear(struct mm_struct *mm,
unsigned long addr, pte_t *ptep,
unsigned long sz)
{
pte_t clear;
pte_t pte = ptep_get(ptep);
pte_val(clear) = (unsigned long)invalid_pte_table;
set_pte_at(mm, addr, ptep, clear);
return pte;
}
#define __HAVE_ARCH_HUGE_PTEP_CLEAR_FLUSH
static inline pte_t huge_ptep_clear_flush(struct vm_area_struct *vma,
unsigned long addr, pte_t *ptep)
{
pte_t pte;
unsigned long sz = huge_page_size(hstate_vma(vma));
pte = huge_ptep_get_and_clear(vma->vm_mm, addr, ptep, sz);
flush_tlb_page(vma, addr);
return pte;
}
#define __HAVE_ARCH_HUGE_PTE_NONE
static inline int huge_pte_none(pte_t pte)
{
unsigned long val = pte_val(pte) & ~_PAGE_GLOBAL;
return !val || (val == (unsigned long)invalid_pte_table);
}
#define __HAVE_ARCH_HUGE_PTEP_SET_ACCESS_FLAGS
static inline int huge_ptep_set_access_flags(struct vm_area_struct *vma,
unsigned long addr,
pte_t *ptep, pte_t pte,
int dirty)
{
int changed = !pte_same(ptep_get(ptep), pte);
if (changed) {
set_pte_at(vma->vm_mm, addr, ptep, pte);
/*
* There could be some standard sized pages in there,
* get them all.
*/
flush_tlb_range(vma, addr, addr + HPAGE_SIZE);
}
return changed;
}
#include <asm-generic/hugetlb.h>
#endif /* __ASM_HUGETLB_H */
@@ -0,0 +1,147 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2022-2023 Loongson Technology Corporation Limited
*/
#ifndef __ASM_HW_BREAKPOINT_H
#define __ASM_HW_BREAKPOINT_H
#include <asm/loongarch.h>
#ifdef __KERNEL__
/* Breakpoint */
#define LOONGARCH_BREAKPOINT_EXECUTE (0 << 0)
/* Watchpoints */
#define LOONGARCH_BREAKPOINT_LOAD (1 << 0)
#define LOONGARCH_BREAKPOINT_STORE (1 << 1)
struct arch_hw_breakpoint_ctrl {
u32 __reserved : 28,
len : 2,
type : 2;
};
struct arch_hw_breakpoint {
u64 address;
u64 mask;
struct arch_hw_breakpoint_ctrl ctrl;
};
/* Lengths */
#define LOONGARCH_BREAKPOINT_LEN_1 0b11
#define LOONGARCH_BREAKPOINT_LEN_2 0b10
#define LOONGARCH_BREAKPOINT_LEN_4 0b01
#define LOONGARCH_BREAKPOINT_LEN_8 0b00
/*
* Limits.
* Changing these will require modifications to the register accessors.
*/
#define LOONGARCH_MAX_BRP 14
#define LOONGARCH_MAX_WRP 14
/* Virtual debug register bases. */
#define CSR_CFG_ADDR 0
#define CSR_CFG_MASK (CSR_CFG_ADDR + LOONGARCH_MAX_BRP)
#define CSR_CFG_CTRL (CSR_CFG_MASK + LOONGARCH_MAX_BRP)
#define CSR_CFG_ASID (CSR_CFG_CTRL + LOONGARCH_MAX_WRP)
/* Debug register names. */
#define LOONGARCH_CSR_NAME_ADDR ADDR
#define LOONGARCH_CSR_NAME_MASK MASK
#define LOONGARCH_CSR_NAME_CTRL CTRL
#define LOONGARCH_CSR_NAME_ASID ASID
/* Accessor macros for the debug registers. */
#define LOONGARCH_CSR_WATCH_READ(N, REG, T, VAL) \
do { \
if (T == 0) \
VAL = csr_read64(LOONGARCH_CSR_##IB##N##REG); \
else \
VAL = csr_read64(LOONGARCH_CSR_##DB##N##REG); \
} while (0)
#define LOONGARCH_CSR_WATCH_WRITE(N, REG, T, VAL) \
do { \
if (T == 0) \
csr_write64(VAL, LOONGARCH_CSR_##IB##N##REG); \
else \
csr_write64(VAL, LOONGARCH_CSR_##DB##N##REG); \
} while (0)
/* Exact number */
#define CSR_FWPC_NUM 0x3f
#define CSR_MWPC_NUM 0x3f
#define CTRL_PLV_ENABLE 0x1e
#define CTRL_PLV0_ENABLE 0x02
#define CTRL_PLV3_ENABLE 0x10
#define MWPnCFG3_LoadEn 8
#define MWPnCFG3_StoreEn 9
#define MWPnCFG3_Type_mask 0x3
#define MWPnCFG3_Size_mask 0x3
static inline u32 encode_ctrl_reg(struct arch_hw_breakpoint_ctrl ctrl)
{
return (ctrl.len << 10) | (ctrl.type << 8);
}
static inline void decode_ctrl_reg(u32 reg, struct arch_hw_breakpoint_ctrl *ctrl)
{
reg >>= 8;
ctrl->type = reg & MWPnCFG3_Type_mask;
reg >>= 2;
ctrl->len = reg & MWPnCFG3_Size_mask;
}
struct task_struct;
struct notifier_block;
struct perf_event;
struct perf_event_attr;
extern int arch_bp_generic_fields(struct arch_hw_breakpoint_ctrl ctrl,
int *gen_len, int *gen_type);
extern int arch_check_bp_in_kernelspace(struct arch_hw_breakpoint *hw);
extern int hw_breakpoint_arch_parse(struct perf_event *bp,
const struct perf_event_attr *attr,
struct arch_hw_breakpoint *hw);
extern int hw_breakpoint_exceptions_notify(struct notifier_block *unused,
unsigned long val, void *data);
extern int arch_install_hw_breakpoint(struct perf_event *bp);
extern void arch_uninstall_hw_breakpoint(struct perf_event *bp);
extern int hw_breakpoint_slots(int type);
extern void hw_breakpoint_pmu_read(struct perf_event *bp);
void breakpoint_handler(struct pt_regs *regs);
void watchpoint_handler(struct pt_regs *regs);
#ifdef CONFIG_HAVE_HW_BREAKPOINT
extern void ptrace_hw_copy_thread(struct task_struct *task);
extern void hw_breakpoint_thread_switch(struct task_struct *next);
#else
static inline void ptrace_hw_copy_thread(struct task_struct *task)
{
}
static inline void hw_breakpoint_thread_switch(struct task_struct *next)
{
}
#endif
/* Determine number of BRP registers available. */
static inline int get_num_brps(void)
{
return csr_read32(LOONGARCH_CSR_FWPC) & CSR_FWPC_NUM;
}
/* Determine number of WRP registers available. */
static inline int get_num_wrps(void)
{
return csr_read32(LOONGARCH_CSR_MWPC) & CSR_MWPC_NUM;
}
#endif /* __KERNEL__ */
#endif /* __ASM_BREAKPOINT_H */
@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __ASM_HW_IRQ_H
#define __ASM_HW_IRQ_H
#include <linux/atomic.h>
extern atomic_t irq_err_count;
#define ARCH_IRQ_INIT_FLAGS IRQ_NOPROBE
/*
* interrupt-retrigger: NOP for now. This may not be appropriate for all
* machines, we'll see ...
*/
#endif /* __ASM_HW_IRQ_H */
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_IDLE_H
#define __ASM_IDLE_H
#include <linux/linkage.h>
extern asmlinkage void __arch_cpu_idle(void);
#endif /* __ASM_IDLE_H */
@@ -0,0 +1,52 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* LoongArch binary image header for EFI(PE/COFF) format.
*
* Author: Youling Tang <tangyouling@kylinos.cn>
* Copyright (C) 2025 KylinSoft Corporation.
*/
#ifndef __ASM_IMAGE_H
#define __ASM_IMAGE_H
#ifndef __ASSEMBLER__
/**
* struct loongarch_image_header
*
* @dos_sig: Optional PE format 'MZ' signature.
* @padding_1: Reserved.
* @kernel_entry: Kernel image entry pointer.
* @kernel_asize: An estimated size of the memory image size in LSB byte order.
* @text_offset: The image load offset in LSB byte order.
* @padding_2: Reserved.
* @pe_header: Optional offset to a PE format header.
**/
struct loongarch_image_header {
uint8_t dos_sig[2];
uint16_t padding_1[3];
uint64_t kernel_entry;
uint64_t kernel_asize;
uint64_t text_offset;
uint32_t padding_2[7];
uint32_t pe_header;
};
/*
* loongarch_header_check_dos_sig - Helper to check the header
*
* Returns true (non-zero) if 'MZ' signature is found.
*/
static inline int loongarch_header_check_dos_sig(const struct loongarch_image_header *h)
{
if (!h)
return 0;
return (h->dos_sig[0] == 'M' && h->dos_sig[1] == 'Z');
}
#endif /* __ASSEMBLER__ */
#endif /* __ASM_IMAGE_H */
@@ -0,0 +1,809 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_INST_H
#define _ASM_INST_H
#include <linux/bitops.h>
#include <linux/types.h>
#include <asm/asm.h>
#include <asm/ptrace.h>
#define INSN_NOP 0x03400000
#define INSN_BREAK 0x002a0000
#define INSN_HVCL 0x002b8000
#define ADDR_IMMMASK_LU52ID 0xFFF0000000000000
#define ADDR_IMMMASK_LU32ID 0x000FFFFF00000000
#define ADDR_IMMMASK_LU12IW 0x00000000FFFFF000
#define ADDR_IMMMASK_ORI 0x0000000000000FFF
#define ADDR_IMMMASK_ADDU16ID 0x00000000FFFF0000
#define ADDR_IMMSHIFT_LU52ID 52
#define ADDR_IMMSBIDX_LU52ID 11
#define ADDR_IMMSHIFT_LU32ID 32
#define ADDR_IMMSBIDX_LU32ID 19
#define ADDR_IMMSHIFT_LU12IW 12
#define ADDR_IMMSBIDX_LU12IW 19
#define ADDR_IMMSHIFT_ORI 0
#define ADDR_IMMSBIDX_ORI 63
#define ADDR_IMMSHIFT_ADDU16ID 16
#define ADDR_IMMSBIDX_ADDU16ID 15
#define ADDR_IMM(addr, INSN) \
(sign_extend64(((addr & ADDR_IMMMASK_##INSN) >> ADDR_IMMSHIFT_##INSN), ADDR_IMMSBIDX_##INSN))
enum reg0i15_op {
break_op = 0x54,
dbar_op = 0x70e4,
};
enum reg0i26_op {
b_op = 0x14,
bl_op = 0x15,
};
enum reg1i20_op {
lu12iw_op = 0x0a,
lu32id_op = 0x0b,
pcaddi_op = 0x0c,
pcalau12i_op = 0x0d,
pcaddu12i_op = 0x0e,
pcaddu18i_op = 0x0f,
};
enum reg1i21_op {
beqz_op = 0x10,
bnez_op = 0x11,
bceqz_op = 0x12, /* bits[9:8] = 0x00 */
bcnez_op = 0x12, /* bits[9:8] = 0x01 */
};
enum reg2_op {
revb2h_op = 0x0c,
revb4h_op = 0x0d,
revb2w_op = 0x0e,
revbd_op = 0x0f,
revh2w_op = 0x10,
revhd_op = 0x11,
extwh_op = 0x16,
extwb_op = 0x17,
cpucfg_op = 0x1b,
iocsrrdb_op = 0x19200,
iocsrrdh_op = 0x19201,
iocsrrdw_op = 0x19202,
iocsrrdd_op = 0x19203,
iocsrwrb_op = 0x19204,
iocsrwrh_op = 0x19205,
iocsrwrw_op = 0x19206,
iocsrwrd_op = 0x19207,
llacqw_op = 0xe15e0,
screlw_op = 0xe15e1,
llacqd_op = 0xe15e2,
screld_op = 0xe15e3,
};
enum reg2i5_op {
slliw_op = 0x81,
srliw_op = 0x89,
sraiw_op = 0x91,
};
enum reg2i6_op {
sllid_op = 0x41,
srlid_op = 0x45,
sraid_op = 0x49,
};
enum reg2i12_op {
addiw_op = 0x0a,
addid_op = 0x0b,
lu52id_op = 0x0c,
andi_op = 0x0d,
ori_op = 0x0e,
xori_op = 0x0f,
ldb_op = 0xa0,
ldh_op = 0xa1,
ldw_op = 0xa2,
ldd_op = 0xa3,
stb_op = 0xa4,
sth_op = 0xa5,
stw_op = 0xa6,
std_op = 0xa7,
ldbu_op = 0xa8,
ldhu_op = 0xa9,
ldwu_op = 0xaa,
flds_op = 0xac,
fsts_op = 0xad,
fldd_op = 0xae,
fstd_op = 0xaf,
};
enum reg2i14_op {
llw_op = 0x20,
scw_op = 0x21,
lld_op = 0x22,
scd_op = 0x23,
ldptrw_op = 0x24,
stptrw_op = 0x25,
ldptrd_op = 0x26,
stptrd_op = 0x27,
};
enum reg2i16_op {
jirl_op = 0x13,
beq_op = 0x16,
bne_op = 0x17,
blt_op = 0x18,
bge_op = 0x19,
bltu_op = 0x1a,
bgeu_op = 0x1b,
};
enum reg2bstrd_op {
bstrinsd_op = 0x2,
bstrpickd_op = 0x3,
};
enum reg3_op {
asrtle_op = 0x02,
asrtgt_op = 0x03,
addw_op = 0x20,
addd_op = 0x21,
subw_op = 0x22,
subd_op = 0x23,
nor_op = 0x28,
and_op = 0x29,
or_op = 0x2a,
xor_op = 0x2b,
orn_op = 0x2c,
andn_op = 0x2d,
sllw_op = 0x2e,
srlw_op = 0x2f,
sraw_op = 0x30,
slld_op = 0x31,
srld_op = 0x32,
srad_op = 0x33,
mulw_op = 0x38,
mulhw_op = 0x39,
mulhwu_op = 0x3a,
muld_op = 0x3b,
mulhd_op = 0x3c,
mulhdu_op = 0x3d,
divw_op = 0x40,
modw_op = 0x41,
divwu_op = 0x42,
modwu_op = 0x43,
divd_op = 0x44,
modd_op = 0x45,
divdu_op = 0x46,
moddu_op = 0x47,
ldxb_op = 0x7000,
ldxh_op = 0x7008,
ldxw_op = 0x7010,
ldxd_op = 0x7018,
stxb_op = 0x7020,
stxh_op = 0x7028,
stxw_op = 0x7030,
stxd_op = 0x7038,
ldxbu_op = 0x7040,
ldxhu_op = 0x7048,
ldxwu_op = 0x7050,
fldxs_op = 0x7060,
fldxd_op = 0x7068,
fstxs_op = 0x7070,
fstxd_op = 0x7078,
scq_op = 0x70ae,
amswapb_op = 0x70b8,
amswaph_op = 0x70b9,
amaddb_op = 0x70ba,
amaddh_op = 0x70bb,
amswapw_op = 0x70c0,
amswapd_op = 0x70c1,
amaddw_op = 0x70c2,
amaddd_op = 0x70c3,
amandw_op = 0x70c4,
amandd_op = 0x70c5,
amorw_op = 0x70c6,
amord_op = 0x70c7,
amxorw_op = 0x70c8,
amxord_op = 0x70c9,
ammaxw_op = 0x70ca,
ammaxd_op = 0x70cb,
amminw_op = 0x70cc,
ammind_op = 0x70cd,
ammaxwu_op = 0x70ce,
ammaxdu_op = 0x70cf,
amminwu_op = 0x70d0,
ammindu_op = 0x70d1,
amswapdbw_op = 0x70d2,
amswapdbd_op = 0x70d3,
amadddbw_op = 0x70d4,
amadddbd_op = 0x70d5,
amanddbw_op = 0x70d6,
amanddbd_op = 0x70d7,
amordbw_op = 0x70d8,
amordbd_op = 0x70d9,
amxordbw_op = 0x70da,
amxordbd_op = 0x70db,
ammaxdbw_op = 0x70dc,
ammaxdbd_op = 0x70dd,
ammindbw_op = 0x70de,
ammindbd_op = 0x70df,
ammaxdbwu_op = 0x70e0,
ammaxdbdu_op = 0x70e1,
ammindbwu_op = 0x70e2,
ammindbdu_op = 0x70e3,
fldgts_op = 0x70e8,
fldgtd_op = 0x70e9,
fldles_op = 0x70ea,
fldled_op = 0x70eb,
fstgts_op = 0x70ec,
fstgtd_op = 0x70ed,
fstles_op = 0x70ee,
fstled_op = 0x70ef,
ldgtb_op = 0x70f0,
ldgth_op = 0x70f1,
ldgtw_op = 0x70f2,
ldgtd_op = 0x70f3,
ldleb_op = 0x70f4,
ldleh_op = 0x70f5,
ldlew_op = 0x70f6,
ldled_op = 0x70f7,
stgtb_op = 0x70f8,
stgth_op = 0x70f9,
stgtw_op = 0x70fa,
stgtd_op = 0x70fb,
stleb_op = 0x70fc,
stleh_op = 0x70fd,
stlew_op = 0x70fe,
stled_op = 0x70ff,
};
enum reg3sa2_op {
alslw_op = 0x02,
alslwu_op = 0x03,
alsld_op = 0x16,
};
struct reg0i15_format {
unsigned int immediate : 15;
unsigned int opcode : 17;
};
struct reg0i26_format {
unsigned int immediate_h : 10;
unsigned int immediate_l : 16;
unsigned int opcode : 6;
};
struct reg1i20_format {
unsigned int rd : 5;
unsigned int immediate : 20;
unsigned int opcode : 7;
};
struct reg1i21_format {
unsigned int immediate_h : 5;
unsigned int rj : 5;
unsigned int immediate_l : 16;
unsigned int opcode : 6;
};
struct reg2_format {
unsigned int rd : 5;
unsigned int rj : 5;
unsigned int opcode : 22;
};
struct reg2i5_format {
unsigned int rd : 5;
unsigned int rj : 5;
unsigned int immediate : 5;
unsigned int opcode : 17;
};
struct reg2i6_format {
unsigned int rd : 5;
unsigned int rj : 5;
unsigned int immediate : 6;
unsigned int opcode : 16;
};
struct reg2i12_format {
unsigned int rd : 5;
unsigned int rj : 5;
unsigned int immediate : 12;
unsigned int opcode : 10;
};
struct reg2i14_format {
unsigned int rd : 5;
unsigned int rj : 5;
unsigned int immediate : 14;
unsigned int opcode : 8;
};
struct reg2i16_format {
unsigned int rd : 5;
unsigned int rj : 5;
unsigned int immediate : 16;
unsigned int opcode : 6;
};
struct reg2bstrd_format {
unsigned int rd : 5;
unsigned int rj : 5;
unsigned int lsbd : 6;
unsigned int msbd : 6;
unsigned int opcode : 10;
};
struct reg2csr_format {
unsigned int rd : 5;
unsigned int rj : 5;
unsigned int csr : 14;
unsigned int opcode : 8;
};
struct reg3_format {
unsigned int rd : 5;
unsigned int rj : 5;
unsigned int rk : 5;
unsigned int opcode : 17;
};
struct reg3sa2_format {
unsigned int rd : 5;
unsigned int rj : 5;
unsigned int rk : 5;
unsigned int immediate : 2;
unsigned int opcode : 15;
};
union loongarch_instruction {
unsigned int word;
struct reg0i15_format reg0i15_format;
struct reg0i26_format reg0i26_format;
struct reg1i20_format reg1i20_format;
struct reg1i21_format reg1i21_format;
struct reg2_format reg2_format;
struct reg2i5_format reg2i5_format;
struct reg2i6_format reg2i6_format;
struct reg2i12_format reg2i12_format;
struct reg2i14_format reg2i14_format;
struct reg2i16_format reg2i16_format;
struct reg2bstrd_format reg2bstrd_format;
struct reg2csr_format reg2csr_format;
struct reg3_format reg3_format;
struct reg3sa2_format reg3sa2_format;
};
#define LOONGARCH_INSN_SIZE sizeof(union loongarch_instruction)
enum loongarch_gpr {
LOONGARCH_GPR_ZERO = 0,
LOONGARCH_GPR_RA = 1,
LOONGARCH_GPR_TP = 2,
LOONGARCH_GPR_SP = 3,
LOONGARCH_GPR_A0 = 4, /* Reused as V0 for return value */
LOONGARCH_GPR_A1, /* Reused as V1 for return value */
LOONGARCH_GPR_A2,
LOONGARCH_GPR_A3,
LOONGARCH_GPR_A4,
LOONGARCH_GPR_A5,
LOONGARCH_GPR_A6,
LOONGARCH_GPR_A7,
LOONGARCH_GPR_T0 = 12,
LOONGARCH_GPR_T1,
LOONGARCH_GPR_T2,
LOONGARCH_GPR_T3,
LOONGARCH_GPR_T4,
LOONGARCH_GPR_T5,
LOONGARCH_GPR_T6,
LOONGARCH_GPR_T7,
LOONGARCH_GPR_T8,
LOONGARCH_GPR_FP = 22,
LOONGARCH_GPR_S0 = 23,
LOONGARCH_GPR_S1,
LOONGARCH_GPR_S2,
LOONGARCH_GPR_S3,
LOONGARCH_GPR_S4,
LOONGARCH_GPR_S5,
LOONGARCH_GPR_S6,
LOONGARCH_GPR_S7,
LOONGARCH_GPR_S8,
LOONGARCH_GPR_MAX
};
#define is_imm12_negative(val) is_imm_negative(val, 12)
static inline bool is_imm_negative(unsigned long val, unsigned int bit)
{
return val & (1UL << (bit - 1));
}
static inline bool is_break_ins(union loongarch_instruction *ip)
{
return ip->reg0i15_format.opcode == break_op;
}
static inline bool is_pc_ins(union loongarch_instruction *ip)
{
return ip->reg1i20_format.opcode >= pcaddi_op &&
ip->reg1i20_format.opcode <= pcaddu18i_op;
}
static inline bool is_branch_ins(union loongarch_instruction *ip)
{
return ip->reg1i21_format.opcode >= beqz_op &&
ip->reg1i21_format.opcode <= bgeu_op;
}
static inline bool is_ra_save_ins(union loongarch_instruction *ip)
{
const u32 opcode = IS_ENABLED(CONFIG_32BIT) ? stw_op : std_op;
/* st.w / st.d $ra, $sp, offset */
return ip->reg2i12_format.opcode == opcode &&
ip->reg2i12_format.rj == LOONGARCH_GPR_SP &&
ip->reg2i12_format.rd == LOONGARCH_GPR_RA &&
!is_imm12_negative(ip->reg2i12_format.immediate);
}
static inline bool is_stack_alloc_ins(union loongarch_instruction *ip)
{
const u32 opcode = IS_ENABLED(CONFIG_32BIT) ? addiw_op : addid_op;
/* addi.w / addi.d $sp, $sp, -imm */
return ip->reg2i12_format.opcode == opcode &&
ip->reg2i12_format.rj == LOONGARCH_GPR_SP &&
ip->reg2i12_format.rd == LOONGARCH_GPR_SP &&
is_imm12_negative(ip->reg2i12_format.immediate);
}
static inline bool is_self_loop_ins(union loongarch_instruction *ip, struct pt_regs *regs)
{
switch (ip->reg0i26_format.opcode) {
case b_op:
case bl_op:
if (ip->reg0i26_format.immediate_l == 0
&& ip->reg0i26_format.immediate_h == 0)
return true;
}
switch (ip->reg1i21_format.opcode) {
case beqz_op:
case bnez_op:
case bceqz_op:
if (ip->reg1i21_format.immediate_l == 0
&& ip->reg1i21_format.immediate_h == 0)
return true;
}
switch (ip->reg2i16_format.opcode) {
case beq_op:
case bne_op:
case blt_op:
case bge_op:
case bltu_op:
case bgeu_op:
if (ip->reg2i16_format.immediate == 0)
return true;
break;
case jirl_op:
if (regs->regs[ip->reg2i16_format.rj] +
((unsigned long)ip->reg2i16_format.immediate << 2) == (unsigned long)ip)
return true;
}
return false;
}
void simu_pc(struct pt_regs *regs, union loongarch_instruction insn);
void simu_branch(struct pt_regs *regs, union loongarch_instruction insn);
bool insns_not_supported(union loongarch_instruction insn);
bool insns_need_simulation(union loongarch_instruction insn);
void arch_simulate_insn(union loongarch_instruction insn, struct pt_regs *regs);
int larch_insn_read(void *addr, u32 *insnp);
int larch_insn_write(void *addr, u32 insn);
int larch_insn_patch_text(void *addr, u32 insn);
int larch_insn_text_copy(void *dst, void *src, size_t len);
u32 larch_insn_gen_nop(void);
u32 larch_insn_gen_b(unsigned long pc, unsigned long dest);
u32 larch_insn_gen_bl(unsigned long pc, unsigned long dest);
u32 larch_insn_gen_break(int imm);
u32 larch_insn_gen_or(enum loongarch_gpr rd, enum loongarch_gpr rj, enum loongarch_gpr rk);
u32 larch_insn_gen_move(enum loongarch_gpr rd, enum loongarch_gpr rj);
u32 larch_insn_gen_lu12iw(enum loongarch_gpr rd, int imm);
u32 larch_insn_gen_lu32id(enum loongarch_gpr rd, int imm);
u32 larch_insn_gen_lu52id(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm);
u32 larch_insn_gen_beq(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm);
u32 larch_insn_gen_bne(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm);
u32 larch_insn_gen_jirl(enum loongarch_gpr rd, enum loongarch_gpr rj, int imm);
static inline bool signed_imm_check(long val, unsigned int bit)
{
return -(1L << (bit - 1)) <= val && val < (1L << (bit - 1));
}
static inline bool unsigned_imm_check(unsigned long val, unsigned int bit)
{
return val < (1UL << bit);
}
#define DEF_EMIT_REG0I15_FORMAT(NAME, OP) \
static inline void emit_##NAME(union loongarch_instruction *insn, \
int imm) \
{ \
insn->reg0i15_format.opcode = OP; \
insn->reg0i15_format.immediate = imm; \
}
DEF_EMIT_REG0I15_FORMAT(break, break_op)
DEF_EMIT_REG0I15_FORMAT(dbar, dbar_op)
/* like emit_break(imm) but returns a constant expression */
#define __emit_break(imm) ((u32)((imm) | (break_op << 15)))
#define DEF_EMIT_REG0I26_FORMAT(NAME, OP) \
static inline void emit_##NAME(union loongarch_instruction *insn, \
int offset) \
{ \
unsigned int immediate_l, immediate_h; \
\
immediate_l = offset & 0xffff; \
offset >>= 16; \
immediate_h = offset & 0x3ff; \
\
insn->reg0i26_format.opcode = OP; \
insn->reg0i26_format.immediate_l = immediate_l; \
insn->reg0i26_format.immediate_h = immediate_h; \
}
DEF_EMIT_REG0I26_FORMAT(b, b_op)
DEF_EMIT_REG0I26_FORMAT(bl, bl_op)
#define DEF_EMIT_REG1I20_FORMAT(NAME, OP) \
static inline void emit_##NAME(union loongarch_instruction *insn, \
enum loongarch_gpr rd, int imm) \
{ \
insn->reg1i20_format.opcode = OP; \
insn->reg1i20_format.immediate = imm; \
insn->reg1i20_format.rd = rd; \
}
DEF_EMIT_REG1I20_FORMAT(lu12iw, lu12iw_op)
DEF_EMIT_REG1I20_FORMAT(lu32id, lu32id_op)
DEF_EMIT_REG1I20_FORMAT(pcaddu18i, pcaddu18i_op)
#define DEF_EMIT_REG2_FORMAT(NAME, OP) \
static inline void emit_##NAME(union loongarch_instruction *insn, \
enum loongarch_gpr rd, \
enum loongarch_gpr rj) \
{ \
insn->reg2_format.opcode = OP; \
insn->reg2_format.rd = rd; \
insn->reg2_format.rj = rj; \
}
DEF_EMIT_REG2_FORMAT(revb2h, revb2h_op)
DEF_EMIT_REG2_FORMAT(revb2w, revb2w_op)
DEF_EMIT_REG2_FORMAT(revbd, revbd_op)
DEF_EMIT_REG2_FORMAT(extwh, extwh_op)
DEF_EMIT_REG2_FORMAT(extwb, extwb_op)
#define DEF_EMIT_REG2I5_FORMAT(NAME, OP) \
static inline void emit_##NAME(union loongarch_instruction *insn, \
enum loongarch_gpr rd, \
enum loongarch_gpr rj, \
int imm) \
{ \
insn->reg2i5_format.opcode = OP; \
insn->reg2i5_format.immediate = imm; \
insn->reg2i5_format.rd = rd; \
insn->reg2i5_format.rj = rj; \
}
DEF_EMIT_REG2I5_FORMAT(slliw, slliw_op)
DEF_EMIT_REG2I5_FORMAT(srliw, srliw_op)
DEF_EMIT_REG2I5_FORMAT(sraiw, sraiw_op)
#define DEF_EMIT_REG2I6_FORMAT(NAME, OP) \
static inline void emit_##NAME(union loongarch_instruction *insn, \
enum loongarch_gpr rd, \
enum loongarch_gpr rj, \
int imm) \
{ \
insn->reg2i6_format.opcode = OP; \
insn->reg2i6_format.immediate = imm; \
insn->reg2i6_format.rd = rd; \
insn->reg2i6_format.rj = rj; \
}
DEF_EMIT_REG2I6_FORMAT(sllid, sllid_op)
DEF_EMIT_REG2I6_FORMAT(srlid, srlid_op)
DEF_EMIT_REG2I6_FORMAT(sraid, sraid_op)
#define DEF_EMIT_REG2I12_FORMAT(NAME, OP) \
static inline void emit_##NAME(union loongarch_instruction *insn, \
enum loongarch_gpr rd, \
enum loongarch_gpr rj, \
int imm) \
{ \
insn->reg2i12_format.opcode = OP; \
insn->reg2i12_format.immediate = imm; \
insn->reg2i12_format.rd = rd; \
insn->reg2i12_format.rj = rj; \
}
DEF_EMIT_REG2I12_FORMAT(addiw, addiw_op)
DEF_EMIT_REG2I12_FORMAT(addid, addid_op)
DEF_EMIT_REG2I12_FORMAT(lu52id, lu52id_op)
DEF_EMIT_REG2I12_FORMAT(andi, andi_op)
DEF_EMIT_REG2I12_FORMAT(ori, ori_op)
DEF_EMIT_REG2I12_FORMAT(xori, xori_op)
DEF_EMIT_REG2I12_FORMAT(ldb, ldb_op)
DEF_EMIT_REG2I12_FORMAT(ldh, ldh_op)
DEF_EMIT_REG2I12_FORMAT(ldw, ldw_op)
DEF_EMIT_REG2I12_FORMAT(ldbu, ldbu_op)
DEF_EMIT_REG2I12_FORMAT(ldhu, ldhu_op)
DEF_EMIT_REG2I12_FORMAT(ldwu, ldwu_op)
DEF_EMIT_REG2I12_FORMAT(ldd, ldd_op)
DEF_EMIT_REG2I12_FORMAT(stb, stb_op)
DEF_EMIT_REG2I12_FORMAT(sth, sth_op)
DEF_EMIT_REG2I12_FORMAT(stw, stw_op)
DEF_EMIT_REG2I12_FORMAT(std, std_op)
#define DEF_EMIT_REG2I14_FORMAT(NAME, OP) \
static inline void emit_##NAME(union loongarch_instruction *insn, \
enum loongarch_gpr rd, \
enum loongarch_gpr rj, \
int imm) \
{ \
insn->reg2i14_format.opcode = OP; \
insn->reg2i14_format.immediate = imm; \
insn->reg2i14_format.rd = rd; \
insn->reg2i14_format.rj = rj; \
}
DEF_EMIT_REG2I14_FORMAT(llw, llw_op)
DEF_EMIT_REG2I14_FORMAT(scw, scw_op)
DEF_EMIT_REG2I14_FORMAT(lld, lld_op)
DEF_EMIT_REG2I14_FORMAT(scd, scd_op)
DEF_EMIT_REG2I14_FORMAT(ldptrw, ldptrw_op)
DEF_EMIT_REG2I14_FORMAT(stptrw, stptrw_op)
DEF_EMIT_REG2I14_FORMAT(ldptrd, ldptrd_op)
DEF_EMIT_REG2I14_FORMAT(stptrd, stptrd_op)
#define DEF_EMIT_REG2I16_FORMAT(NAME, OP) \
static inline void emit_##NAME(union loongarch_instruction *insn, \
enum loongarch_gpr rj, \
enum loongarch_gpr rd, \
int offset) \
{ \
insn->reg2i16_format.opcode = OP; \
insn->reg2i16_format.immediate = offset; \
insn->reg2i16_format.rj = rj; \
insn->reg2i16_format.rd = rd; \
}
DEF_EMIT_REG2I16_FORMAT(beq, beq_op)
DEF_EMIT_REG2I16_FORMAT(bne, bne_op)
DEF_EMIT_REG2I16_FORMAT(blt, blt_op)
DEF_EMIT_REG2I16_FORMAT(bge, bge_op)
DEF_EMIT_REG2I16_FORMAT(bltu, bltu_op)
DEF_EMIT_REG2I16_FORMAT(bgeu, bgeu_op)
static inline void emit_jirl(union loongarch_instruction *insn,
enum loongarch_gpr rd,
enum loongarch_gpr rj,
int offset)
{
insn->reg2i16_format.opcode = jirl_op;
insn->reg2i16_format.immediate = offset;
insn->reg2i16_format.rd = rd;
insn->reg2i16_format.rj = rj;
}
#define DEF_EMIT_REG2BSTRD_FORMAT(NAME, OP) \
static inline void emit_##NAME(union loongarch_instruction *insn, \
enum loongarch_gpr rd, \
enum loongarch_gpr rj, \
int msbd, \
int lsbd) \
{ \
insn->reg2bstrd_format.opcode = OP; \
insn->reg2bstrd_format.msbd = msbd; \
insn->reg2bstrd_format.lsbd = lsbd; \
insn->reg2bstrd_format.rj = rj; \
insn->reg2bstrd_format.rd = rd; \
}
DEF_EMIT_REG2BSTRD_FORMAT(bstrpickd, bstrpickd_op)
#define DEF_EMIT_REG3_FORMAT(NAME, OP) \
static inline void emit_##NAME(union loongarch_instruction *insn, \
enum loongarch_gpr rd, \
enum loongarch_gpr rj, \
enum loongarch_gpr rk) \
{ \
insn->reg3_format.opcode = OP; \
insn->reg3_format.rd = rd; \
insn->reg3_format.rj = rj; \
insn->reg3_format.rk = rk; \
}
DEF_EMIT_REG3_FORMAT(addw, addw_op)
DEF_EMIT_REG3_FORMAT(addd, addd_op)
DEF_EMIT_REG3_FORMAT(subd, subd_op)
DEF_EMIT_REG3_FORMAT(muld, muld_op)
DEF_EMIT_REG3_FORMAT(divd, divd_op)
DEF_EMIT_REG3_FORMAT(modd, modd_op)
DEF_EMIT_REG3_FORMAT(divdu, divdu_op)
DEF_EMIT_REG3_FORMAT(moddu, moddu_op)
DEF_EMIT_REG3_FORMAT(and, and_op)
DEF_EMIT_REG3_FORMAT(or, or_op)
DEF_EMIT_REG3_FORMAT(xor, xor_op)
DEF_EMIT_REG3_FORMAT(sllw, sllw_op)
DEF_EMIT_REG3_FORMAT(slld, slld_op)
DEF_EMIT_REG3_FORMAT(srlw, srlw_op)
DEF_EMIT_REG3_FORMAT(srld, srld_op)
DEF_EMIT_REG3_FORMAT(sraw, sraw_op)
DEF_EMIT_REG3_FORMAT(srad, srad_op)
DEF_EMIT_REG3_FORMAT(ldxb, ldxb_op)
DEF_EMIT_REG3_FORMAT(ldxh, ldxh_op)
DEF_EMIT_REG3_FORMAT(ldxw, ldxw_op)
DEF_EMIT_REG3_FORMAT(ldxbu, ldxbu_op)
DEF_EMIT_REG3_FORMAT(ldxhu, ldxhu_op)
DEF_EMIT_REG3_FORMAT(ldxwu, ldxwu_op)
DEF_EMIT_REG3_FORMAT(ldxd, ldxd_op)
DEF_EMIT_REG3_FORMAT(stxb, stxb_op)
DEF_EMIT_REG3_FORMAT(stxh, stxh_op)
DEF_EMIT_REG3_FORMAT(stxw, stxw_op)
DEF_EMIT_REG3_FORMAT(stxd, stxd_op)
DEF_EMIT_REG3_FORMAT(amaddb, amaddb_op)
DEF_EMIT_REG3_FORMAT(amaddh, amaddh_op)
DEF_EMIT_REG3_FORMAT(amaddw, amaddw_op)
DEF_EMIT_REG3_FORMAT(amaddd, amaddd_op)
DEF_EMIT_REG3_FORMAT(amandw, amandw_op)
DEF_EMIT_REG3_FORMAT(amandd, amandd_op)
DEF_EMIT_REG3_FORMAT(amorw, amorw_op)
DEF_EMIT_REG3_FORMAT(amord, amord_op)
DEF_EMIT_REG3_FORMAT(amxorw, amxorw_op)
DEF_EMIT_REG3_FORMAT(amxord, amxord_op)
DEF_EMIT_REG3_FORMAT(amswapb, amswapb_op)
DEF_EMIT_REG3_FORMAT(amswaph, amswaph_op)
DEF_EMIT_REG3_FORMAT(amswapw, amswapw_op)
DEF_EMIT_REG3_FORMAT(amswapd, amswapd_op)
#define DEF_EMIT_REG3SA2_FORMAT(NAME, OP) \
static inline void emit_##NAME(union loongarch_instruction *insn, \
enum loongarch_gpr rd, \
enum loongarch_gpr rj, \
enum loongarch_gpr rk, \
int imm) \
{ \
insn->reg3sa2_format.opcode = OP; \
insn->reg3sa2_format.immediate = imm; \
insn->reg3sa2_format.rd = rd; \
insn->reg3sa2_format.rj = rj; \
insn->reg3sa2_format.rk = rk; \
}
DEF_EMIT_REG3SA2_FORMAT(alsld, alsld_op)
struct pt_regs;
void emulate_load_store_insn(struct pt_regs *regs, void __user *addr, unsigned int *pc);
unsigned long unaligned_read(void __user *addr, void *value, unsigned long n, bool sign);
unsigned long unaligned_write(void __user *addr, unsigned long value, unsigned long n);
#endif /* _ASM_INST_H */
@@ -0,0 +1,91 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_IO_H
#define _ASM_IO_H
#include <linux/kernel.h>
#include <linux/types.h>
#include <asm/addrspace.h>
#include <asm/cpu.h>
#include <asm/page.h>
#include <asm/pgtable-bits.h>
#include <asm/string.h>
extern void __init __iomem *early_ioremap(phys_addr_t phys_addr, unsigned long size);
extern void __init early_iounmap(void __iomem *addr, unsigned long size);
#define early_memremap early_ioremap
#define early_memunmap early_iounmap
#ifdef CONFIG_ARCH_IOREMAP
static inline void __iomem *ioremap_prot(phys_addr_t offset, unsigned long size,
pgprot_t prot)
{
if (offset > TO_PHYS_MASK)
return NULL;
switch (pgprot_val(prot) & _CACHE_MASK) {
case _CACHE_CC:
return (void __iomem *)(unsigned long)(CACHE_BASE + offset);
case _CACHE_SUC:
return (void __iomem *)(unsigned long)(UNCACHE_BASE + offset);
case _CACHE_WUC:
return (void __iomem *)(unsigned long)(WRITECOMBINE_BASE + offset);
default:
return NULL;
}
}
#define ioremap(offset, size) \
ioremap_prot((offset), (size), PAGE_KERNEL_SUC)
#define iounmap(addr) ((void)(addr))
#endif
/*
* On LoongArch, ioremap() has two variants, ioremap_wc() and ioremap_cache().
* They map bus memory into CPU space, the mapped memory is marked uncachable
* (_CACHE_SUC), uncachable but accelerated by write-combine (_CACHE_WUC) and
* cachable (_CACHE_CC) respectively for CPU access.
*
* @offset: bus address of the memory
* @size: size of the resource to map
*/
#define ioremap_wc(offset, size) \
ioremap_prot((offset), (size), \
wc_enabled ? PAGE_KERNEL_WUC : PAGE_KERNEL_SUC)
#define ioremap_cache(offset, size) \
ioremap_prot((offset), (size), PAGE_KERNEL)
#define mmiowb() wmb()
#define __io_aw() mmiowb()
#ifdef CONFIG_KFENCE
#define virt_to_phys(kaddr) \
({ \
(likely((unsigned long)kaddr < vm_map_base)) ? __pa((unsigned long)kaddr) : \
page_to_phys(tlb_virt_to_page((unsigned long)kaddr)) + offset_in_page((unsigned long)kaddr);\
})
#define phys_to_virt(paddr) \
({ \
extern char *__kfence_pool; \
(unlikely(__kfence_pool == NULL)) ? __va((unsigned long)paddr) : \
page_address(phys_to_page((unsigned long)paddr)) + offset_in_page((unsigned long)paddr);\
})
#endif
#include <asm-generic/io.h>
#define ARCH_HAS_VALID_PHYS_ADDR_RANGE
extern int valid_phys_addr_range(phys_addr_t addr, size_t size);
extern int valid_mmap_phys_addr_range(unsigned long pfn, size_t size);
#endif /* _ASM_IO_H */
@@ -0,0 +1,144 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_IRQ_H
#define _ASM_IRQ_H
#include <linux/irqdomain.h>
#include <linux/irqreturn.h>
#define IRQ_STACK_SIZE THREAD_SIZE
#define IRQ_STACK_START (IRQ_STACK_SIZE - 16)
DECLARE_PER_CPU(unsigned long, irq_stack);
/*
* The highest address on the IRQ stack contains a dummy frame which is
* structured as follows:
*
* top ------------
* | task sp | <- irq_stack[cpu] + IRQ_STACK_START
* ------------
* | | <- First frame of IRQ context
* ------------
*
* task sp holds a copy of the task stack pointer where the struct pt_regs
* from exception entry can be found.
*/
static inline bool on_irq_stack(int cpu, unsigned long sp)
{
unsigned long low = per_cpu(irq_stack, cpu);
unsigned long high = low + IRQ_STACK_SIZE;
return (low <= sp && sp <= high);
}
void spurious_interrupt(void);
#define NR_IRQS_LEGACY 16
/*
* 256 Vectors Mapping for AVECINTC:
*
* 0 - 15: Mapping classic IPs, e.g. IP0-12.
* 16 - 255: Mapping vectors for external IRQ.
*
*/
#define NR_VECTORS 256
#define NR_LEGACY_VECTORS 16
#define AVEC_IRQ_SHIFT 4
#define AVEC_IRQ_BIT 8
#define AVEC_IRQ_MASK GENMASK(AVEC_IRQ_BIT - 1, 0)
#define AVEC_CPU_SHIFT 12
#define AVEC_CPU_BIT 16
#define AVEC_CPU_MASK GENMASK(AVEC_CPU_BIT - 1, 0)
#define arch_trigger_cpumask_backtrace arch_trigger_cpumask_backtrace
void arch_trigger_cpumask_backtrace(const struct cpumask *mask, int exclude_cpu);
#ifdef CONFIG_32BIT
#define MAX_IO_PICS 1
#else
#define MAX_IO_PICS 8
#endif
#define NR_IRQS (64 + NR_VECTORS * (NR_CPUS + MAX_IO_PICS))
struct acpi_vector_group {
int node;
int pci_segment;
struct irq_domain *parent;
};
extern struct acpi_vector_group pch_group[MAX_IO_PICS];
extern struct acpi_vector_group msi_group[MAX_IO_PICS];
#define CORES_PER_EIO_NODE 4
#define CORES_PER_VEIO_NODE 256
#define LOONGSON_CPU_UART0_VEC 10 /* CPU UART0 */
#define LOONGSON_CPU_THSENS_VEC 14 /* CPU Thsens */
#define LOONGSON_CPU_HT0_VEC 16 /* CPU HT0 irq vector base number */
#define LOONGSON_CPU_HT1_VEC 24 /* CPU HT1 irq vector base number */
/* IRQ number definitions */
#define LOONGSON_LPC_IRQ_BASE 0
#define LOONGSON_LPC_LAST_IRQ (LOONGSON_LPC_IRQ_BASE + 15)
#define LOONGSON_CPU_IRQ_BASE 16
#define LOONGSON_CPU_LAST_IRQ (LOONGSON_CPU_IRQ_BASE + 15)
#define LOONGSON_PCH_IRQ_BASE 64
#define LOONGSON_PCH_ACPI_IRQ (LOONGSON_PCH_IRQ_BASE + 47)
#define LOONGSON_PCH_LAST_IRQ (LOONGSON_PCH_IRQ_BASE + 64 - 1)
#define LOONGSON_MSI_IRQ_BASE (LOONGSON_PCH_IRQ_BASE + 64)
#define LOONGSON_MSI_LAST_IRQ (LOONGSON_PCH_IRQ_BASE + 256 - 1)
#define GSI_MIN_LPC_IRQ LOONGSON_LPC_IRQ_BASE
#define GSI_MAX_LPC_IRQ (LOONGSON_LPC_IRQ_BASE + 16 - 1)
#define GSI_MIN_CPU_IRQ LOONGSON_CPU_IRQ_BASE
#define GSI_MAX_CPU_IRQ (LOONGSON_CPU_IRQ_BASE + 48 - 1)
#define GSI_MIN_PCH_IRQ LOONGSON_PCH_IRQ_BASE
#define GSI_MAX_PCH_IRQ (LOONGSON_PCH_IRQ_BASE + 256 - 1)
struct acpi_madt_lio_pic;
struct acpi_madt_eio_pic;
struct acpi_madt_ht_pic;
struct acpi_madt_bio_pic;
struct acpi_madt_msi_pic;
struct acpi_madt_lpc_pic;
void complete_irq_moving(void);
struct fwnode_handle *get_pch_msi_handle(int pci_segment);
extern struct acpi_madt_lio_pic *acpi_liointc;
extern struct acpi_madt_eio_pic *acpi_eiointc[MAX_IO_PICS];
extern struct acpi_madt_ht_pic *acpi_htintc;
extern struct acpi_madt_lpc_pic *acpi_pchlpc;
extern struct acpi_madt_msi_pic *acpi_pchmsi[MAX_IO_PICS];
extern struct acpi_madt_bio_pic *acpi_pchpic[MAX_IO_PICS];
extern struct fwnode_handle *cpuintc_handle;
extern struct fwnode_handle *liointc_handle;
extern struct fwnode_handle *pch_lpc_handle;
extern struct fwnode_handle *pch_pic_handle[MAX_IO_PICS];
static inline int get_percpu_irq(int vector)
{
struct irq_domain *d;
d = irq_find_matching_fwnode(cpuintc_handle, DOMAIN_BUS_ANY);
if (d)
return irq_create_mapping(d, vector);
return -EINVAL;
}
#include <asm-generic/irq.h>
#endif /* _ASM_IRQ_H */
@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __ASM_IRQ_REGS_H
#define __ASM_IRQ_REGS_H
#define ARCH_HAS_OWN_IRQ_REGS
#include <linux/thread_info.h>
static inline struct pt_regs *get_irq_regs(void)
{
return current_thread_info()->regs;
}
static inline struct pt_regs *set_irq_regs(struct pt_regs *new_regs)
{
struct pt_regs *old_regs;
old_regs = get_irq_regs();
current_thread_info()->regs = new_regs;
return old_regs;
}
#endif /* __ASM_IRQ_REGS_H */
@@ -0,0 +1,10 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_LOONGARCH_IRQ_WORK_H
#define _ASM_LOONGARCH_IRQ_WORK_H
static inline bool arch_irq_work_has_interrupt(void)
{
return IS_ENABLED(CONFIG_SMP) && cpu_opt(LOONGARCH_CPU_CSRIPI);
}
#endif /* _ASM_LOONGARCH_IRQ_WORK_H */
@@ -0,0 +1,85 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_IRQFLAGS_H
#define _ASM_IRQFLAGS_H
#ifndef __ASSEMBLER__
#include <linux/compiler.h>
#include <linux/stringify.h>
#include <asm/loongarch.h>
static inline void arch_local_irq_enable(void)
{
u32 flags = CSR_CRMD_IE;
register u32 mask asm("t0") = CSR_CRMD_IE;
__asm__ __volatile__(
"csrxchg %[val], %[mask], %[reg]\n\t"
: [val] "+r" (flags)
: [mask] "r" (mask), [reg] "i" (LOONGARCH_CSR_CRMD)
: "memory");
}
static inline void arch_local_irq_disable(void)
{
u32 flags = 0;
register u32 mask asm("t0") = CSR_CRMD_IE;
__asm__ __volatile__(
"csrxchg %[val], %[mask], %[reg]\n\t"
: [val] "+r" (flags)
: [mask] "r" (mask), [reg] "i" (LOONGARCH_CSR_CRMD)
: "memory");
}
static inline unsigned long arch_local_irq_save(void)
{
u32 flags = 0;
register u32 mask asm("t0") = CSR_CRMD_IE;
__asm__ __volatile__(
"csrxchg %[val], %[mask], %[reg]\n\t"
: [val] "+r" (flags)
: [mask] "r" (mask), [reg] "i" (LOONGARCH_CSR_CRMD)
: "memory");
return flags;
}
static inline void arch_local_irq_restore(unsigned long flags)
{
register u32 mask asm("t0") = CSR_CRMD_IE;
__asm__ __volatile__(
"csrxchg %[val], %[mask], %[reg]\n\t"
: [val] "+r" (flags)
: [mask] "r" (mask), [reg] "i" (LOONGARCH_CSR_CRMD)
: "memory");
}
static inline unsigned long arch_local_save_flags(void)
{
u32 flags;
__asm__ __volatile__(
"csrrd %[val], %[reg]\n\t"
: [val] "=r" (flags)
: [reg] "i" (LOONGARCH_CSR_CRMD)
: "memory");
return flags;
}
static inline int arch_irqs_disabled_flags(unsigned long flags)
{
return !(flags & CSR_CRMD_IE);
}
static inline int arch_irqs_disabled(void)
{
return arch_irqs_disabled_flags(arch_local_save_flags());
}
#endif /* #ifndef __ASSEMBLER__ */
#endif /* _ASM_IRQFLAGS_H */
@@ -0,0 +1,64 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2023 Loongson Technology Corporation Limited
*
* Based on arch/arm64/include/asm/jump_label.h
*/
#ifndef __ASM_JUMP_LABEL_H
#define __ASM_JUMP_LABEL_H
#ifndef __ASSEMBLER__
#include <linux/types.h>
#include <linux/stringify.h>
#include <asm/asm.h>
#define HAVE_JUMP_LABEL_BATCH
#define JUMP_LABEL_NOP_SIZE 4
#ifdef CONFIG_32BIT
#define JUMP_LABEL_TYPE ".long "
#else
#define JUMP_LABEL_TYPE ".quad "
#endif
/* This macro is also expanded on the Rust side. */
#define JUMP_TABLE_ENTRY(key, label) \
".pushsection __jump_table, \"aw\" \n\t" \
".align " __stringify(PTRLOG) " \n\t" \
".long 1b - ., " label " - . \n\t" \
JUMP_LABEL_TYPE key " - . \n\t" \
".popsection \n\t"
#define ARCH_STATIC_BRANCH_ASM(key, label) \
"1: nop \n\t" \
JUMP_TABLE_ENTRY(key, label)
static __always_inline bool arch_static_branch(struct static_key * const key, const bool branch)
{
asm goto(
ARCH_STATIC_BRANCH_ASM("%0", "%l[l_yes]")
: : "i"(&((char *)key)[branch]) : : l_yes);
return false;
l_yes:
return true;
}
static __always_inline bool arch_static_branch_jump(struct static_key * const key, const bool branch)
{
asm goto(
"1: b %l[l_yes] \n\t"
JUMP_TABLE_ENTRY("%0", "%l[l_yes]")
: : "i"(&((char *)key)[branch]) : : l_yes);
return false;
l_yes:
return true;
}
#endif /* __ASSEMBLER__ */
#endif /* __ASM_JUMP_LABEL_H */
@@ -0,0 +1,87 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_KASAN_H
#define __ASM_KASAN_H
#ifndef __ASSEMBLER__
#include <linux/linkage.h>
#include <linux/mmzone.h>
#include <asm/addrspace.h>
#include <asm/io.h>
#include <asm/pgtable.h>
#define KASAN_SHADOW_SCALE_SHIFT 3
#define KASAN_SHADOW_OFFSET _AC(CONFIG_KASAN_SHADOW_OFFSET, UL)
#define XRANGE_SHIFT (48)
/* Valid address length */
#define XRANGE_SHADOW_SHIFT min(cpu_vabits, VA_BITS)
/* Used for taking out the valid address */
#define XRANGE_SHADOW_MASK GENMASK_ULL(XRANGE_SHADOW_SHIFT - 1, 0)
/* One segment whole address space size */
#define XRANGE_SIZE (XRANGE_SHADOW_MASK + 1)
/* 64-bit segment value. */
#define XKPRANGE_UC_SEG (0x8000)
#define XKPRANGE_CC_SEG (0x9000)
#define XKPRANGE_WC_SEG (0xa000)
#define XKVRANGE_VC_SEG (0xffff)
/* Cached */
#define XKPRANGE_CC_START CACHE_BASE
#define XKPRANGE_CC_SIZE XRANGE_SIZE
#define XKPRANGE_CC_KASAN_OFFSET (0)
#define XKPRANGE_CC_SHADOW_SIZE (XKPRANGE_CC_SIZE >> KASAN_SHADOW_SCALE_SHIFT)
#define XKPRANGE_CC_SHADOW_END (XKPRANGE_CC_KASAN_OFFSET + XKPRANGE_CC_SHADOW_SIZE)
/* UnCached */
#define XKPRANGE_UC_START UNCACHE_BASE
#define XKPRANGE_UC_SIZE XRANGE_SIZE
#define XKPRANGE_UC_KASAN_OFFSET XKPRANGE_CC_SHADOW_END
#define XKPRANGE_UC_SHADOW_SIZE (XKPRANGE_UC_SIZE >> KASAN_SHADOW_SCALE_SHIFT)
#define XKPRANGE_UC_SHADOW_END (XKPRANGE_UC_KASAN_OFFSET + XKPRANGE_UC_SHADOW_SIZE)
/* WriteCombine */
#define XKPRANGE_WC_START WRITECOMBINE_BASE
#define XKPRANGE_WC_SIZE XRANGE_SIZE
#define XKPRANGE_WC_KASAN_OFFSET XKPRANGE_UC_SHADOW_END
#define XKPRANGE_WC_SHADOW_SIZE (XKPRANGE_WC_SIZE >> KASAN_SHADOW_SCALE_SHIFT)
#define XKPRANGE_WC_SHADOW_END (XKPRANGE_WC_KASAN_OFFSET + XKPRANGE_WC_SHADOW_SIZE)
/* VMALLOC (Cached or UnCached) */
#define XKVRANGE_VC_START MODULES_VADDR
#define XKVRANGE_VC_SIZE round_up(KFENCE_AREA_END - MODULES_VADDR + 1, PGDIR_SIZE)
#define XKVRANGE_VC_KASAN_OFFSET XKPRANGE_WC_SHADOW_END
#define XKVRANGE_VC_SHADOW_SIZE (XKVRANGE_VC_SIZE >> KASAN_SHADOW_SCALE_SHIFT)
#define XKVRANGE_VC_SHADOW_END (XKVRANGE_VC_KASAN_OFFSET + XKVRANGE_VC_SHADOW_SIZE)
/* KAsan shadow memory start right after vmalloc. */
#define KASAN_SHADOW_START round_up(KFENCE_AREA_END, PGDIR_SIZE)
#define KASAN_SHADOW_SIZE (XKVRANGE_VC_SHADOW_END - XKPRANGE_CC_KASAN_OFFSET)
#define KASAN_SHADOW_END (round_up(KASAN_SHADOW_START + KASAN_SHADOW_SIZE, PGDIR_SIZE) - 1)
#define XKPRANGE_CC_SHADOW_OFFSET (KASAN_SHADOW_START + XKPRANGE_CC_KASAN_OFFSET)
#define XKPRANGE_UC_SHADOW_OFFSET (KASAN_SHADOW_START + XKPRANGE_UC_KASAN_OFFSET)
#define XKPRANGE_WC_SHADOW_OFFSET (KASAN_SHADOW_START + XKPRANGE_WC_KASAN_OFFSET)
#define XKVRANGE_VC_SHADOW_OFFSET (KASAN_SHADOW_START + XKVRANGE_VC_KASAN_OFFSET)
extern unsigned char kasan_early_shadow_page[PAGE_SIZE];
#define kasan_mem_to_shadow kasan_mem_to_shadow
void *kasan_mem_to_shadow(const void *addr);
#define kasan_shadow_to_mem kasan_shadow_to_mem
const void *kasan_shadow_to_mem(const void *shadow_addr);
#define addr_has_metadata addr_has_metadata
static __always_inline bool addr_has_metadata(const void *addr)
{
return (kasan_mem_to_shadow((void *)addr) != NULL);
}
void kasan_init(void);
asmlinkage void kasan_early_init(void);
#endif
#endif
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_LOONGARCH_KDEBUG_H
#define _ASM_LOONGARCH_KDEBUG_H
#include <linux/notifier.h>
enum die_val {
DIE_OOPS = 1,
DIE_RI,
DIE_FP,
DIE_SIMD,
DIE_TRAP,
};
#endif /* _ASM_LOONGARCH_KDEBUG_H */
@@ -0,0 +1,73 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* kexec.h for kexec
*
* Copyright (C) 2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_KEXEC_H
#define _ASM_KEXEC_H
#include <asm/stacktrace.h>
#include <asm/page.h>
/* Maximum physical address we can use pages from */
#define KEXEC_SOURCE_MEMORY_LIMIT (-1UL)
/* Maximum address we can reach in physical address mode */
#define KEXEC_DESTINATION_MEMORY_LIMIT (-1UL)
/* Maximum address we can use for the control code buffer */
#define KEXEC_CONTROL_MEMORY_LIMIT (-1UL)
/* Reserve a page for the control code buffer */
#define KEXEC_CONTROL_PAGE_SIZE PAGE_SIZE
/* The native architecture */
#define KEXEC_ARCH KEXEC_ARCH_LOONGARCH
static inline void crash_setup_regs(struct pt_regs *newregs,
struct pt_regs *oldregs)
{
if (oldregs)
memcpy(newregs, oldregs, sizeof(*newregs));
else
prepare_frametrace(newregs);
}
#define ARCH_HAS_KIMAGE_ARCH
struct kimage_arch {
unsigned long efi_boot;
unsigned long cmdline_ptr;
unsigned long systable_ptr;
};
struct kimage;
#ifdef CONFIG_KEXEC_FILE
extern const struct kexec_file_ops kexec_efi_ops;
extern const struct kexec_file_ops kexec_elf_ops;
int arch_kimage_file_post_load_cleanup(struct kimage *image);
#define arch_kimage_file_post_load_cleanup arch_kimage_file_post_load_cleanup
extern int load_other_segments(struct kimage *image,
unsigned long kernel_load_addr, unsigned long kernel_size,
char *initrd, unsigned long initrd_len, char *cmdline, unsigned long cmdline_len);
#endif
typedef void (*do_kexec_t)(unsigned long efi_boot,
unsigned long cmdline_ptr,
unsigned long systable_ptr,
unsigned long start_addr,
unsigned long first_ind_entry);
extern const unsigned char relocate_new_kernel[];
extern const size_t relocate_new_kernel_size;
extern void kexec_reboot(void);
#ifdef CONFIG_SMP
extern atomic_t kexec_ready_to_reboot;
extern const unsigned char kexec_smp_wait[];
#endif
#endif /* !_ASM_KEXEC_H */
@@ -0,0 +1,71 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* KFENCE support for LoongArch.
*
* Author: Enze Li <lienze@kylinos.cn>
* Copyright (C) 2022-2023 KylinSoft Corporation.
*/
#ifndef _ASM_LOONGARCH_KFENCE_H
#define _ASM_LOONGARCH_KFENCE_H
#include <linux/kfence.h>
#include <linux/vmalloc.h>
#include <asm/pgtable.h>
#include <asm/tlb.h>
static inline bool arch_kfence_init_pool(void)
{
int err;
char *kaddr, *vaddr;
char *kfence_pool = __kfence_pool;
struct vm_struct *area;
area = __get_vm_area_caller(KFENCE_POOL_SIZE, VM_IOREMAP,
KFENCE_AREA_START, KFENCE_AREA_END,
__builtin_return_address(0));
if (!area)
return false;
__kfence_pool = (char *)area->addr;
err = ioremap_page_range((unsigned long)__kfence_pool,
(unsigned long)__kfence_pool + KFENCE_POOL_SIZE,
virt_to_phys((void *)kfence_pool), PAGE_KERNEL);
if (err) {
free_vm_area(area);
__kfence_pool = kfence_pool;
return false;
}
kaddr = kfence_pool;
vaddr = __kfence_pool;
while (kaddr < kfence_pool + KFENCE_POOL_SIZE) {
set_page_address(virt_to_page(kaddr), vaddr);
kaddr += PAGE_SIZE;
vaddr += PAGE_SIZE;
}
return true;
}
/* Protect the given page and flush TLB. */
static inline bool kfence_protect_page(unsigned long addr, bool protect)
{
pte_t *pte = virt_to_kpte(addr);
if (WARN_ON(!pte) || pte_none(ptep_get(pte)))
return false;
if (protect)
set_pte(pte, __pte(pte_val(ptep_get(pte)) & ~(_PAGE_VALID | _PAGE_PRESENT)));
else
set_pte(pte, __pte(pte_val(ptep_get(pte)) | (_PAGE_VALID | _PAGE_PRESENT)));
preempt_disable();
local_flush_tlb_one(addr);
preempt_enable();
return true;
}
#endif /* _ASM_LOONGARCH_KFENCE_H */
@@ -0,0 +1,97 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2023 Loongson Technology Corporation Limited
*/
#ifndef _ASM_LOONGARCH_KGDB_H
#define _ASM_LOONGARCH_KGDB_H
#define GDB_SIZEOF_REG sizeof(u64)
/* gdb remote procotol expects the following register layout. */
/*
* General purpose registers:
* r0-r31: 64 bit
* orig_a0: 64 bit
* pc : 64 bit
* csr_badvaddr: 64 bit
*/
#define DBG_PT_REGS_BASE 0
#define DBG_PT_REGS_NUM 35
#define DBG_PT_REGS_END (DBG_PT_REGS_BASE + DBG_PT_REGS_NUM - 1)
/*
* Floating point registers:
* f0-f31: 64 bit
*/
#define DBG_FPR_BASE (DBG_PT_REGS_END + 1)
#define DBG_FPR_NUM 32
#define DBG_FPR_END (DBG_FPR_BASE + DBG_FPR_NUM - 1)
/*
* Condition Flag registers:
* fcc0-fcc8: 8 bit
*/
#define DBG_FCC_BASE (DBG_FPR_END + 1)
#define DBG_FCC_NUM 8
#define DBG_FCC_END (DBG_FCC_BASE + DBG_FCC_NUM - 1)
/*
* Floating-point Control and Status registers:
* fcsr: 32 bit
*/
#define DBG_FCSR_NUM 1
#define DBG_FCSR (DBG_FCC_END + 1)
#define DBG_MAX_REG_NUM (DBG_FCSR + 1)
/*
* Size of I/O buffer for gdb packet.
* considering to hold all register contents, size is set
*/
#define BUFMAX 2048
/*
* Number of bytes required for gdb_regs buffer.
* PT_REGS and FPR: 8 bytes; FCSR: 4 bytes; FCC: 1 bytes.
* GDB fails to connect for size beyond this with error
* "'g' packet reply is too long"
*/
#define NUMREGBYTES ((DBG_PT_REGS_NUM + DBG_FPR_NUM) * GDB_SIZEOF_REG + DBG_FCC_NUM * 1 + DBG_FCSR_NUM * 4)
#define BREAK_INSTR_SIZE 4
#define CACHE_FLUSH_IS_SAFE 0
/* Register numbers of various important registers. */
enum dbg_loongarch_regnum {
DBG_LOONGARCH_ZERO = 0,
DBG_LOONGARCH_RA,
DBG_LOONGARCH_TP,
DBG_LOONGARCH_SP,
DBG_LOONGARCH_A0,
DBG_LOONGARCH_FP = 22,
DBG_LOONGARCH_S0,
DBG_LOONGARCH_S1,
DBG_LOONGARCH_S2,
DBG_LOONGARCH_S3,
DBG_LOONGARCH_S4,
DBG_LOONGARCH_S5,
DBG_LOONGARCH_S6,
DBG_LOONGARCH_S7,
DBG_LOONGARCH_S8,
DBG_LOONGARCH_ORIG_A0,
DBG_LOONGARCH_PC,
DBG_LOONGARCH_BADV
};
void kgdb_breakinst(void);
void arch_kgdb_breakpoint(void);
#ifdef CONFIG_KGDB
bool kgdb_breakpoint_handler(struct pt_regs *regs);
#else /* !CONFIG_KGDB */
static inline bool kgdb_breakpoint_handler(struct pt_regs *regs) { return false; }
#endif /* CONFIG_KGDB */
#endif /* __ASM_KGDB_H_ */
@@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef __ASM_LOONGARCH_KPROBES_H
#define __ASM_LOONGARCH_KPROBES_H
#include <asm-generic/kprobes.h>
#ifdef CONFIG_KPROBES
#include <asm/inst.h>
#include <asm/cacheflush.h>
#define __ARCH_WANT_KPROBES_INSN_SLOT
#define MAX_INSN_SIZE 2
#define flush_insn_slot(p) \
do { \
if (p->addr) \
flush_icache_range((unsigned long)p->addr, \
(unsigned long)p->addr + \
(MAX_INSN_SIZE * sizeof(kprobe_opcode_t))); \
} while (0)
#define kretprobe_blacklist_size 0
typedef u32 kprobe_opcode_t;
/* Architecture specific copy of original instruction */
struct arch_specific_insn {
/* copy of the original instruction */
kprobe_opcode_t *insn;
/* restore address after simulation */
unsigned long restore;
};
struct prev_kprobe {
struct kprobe *kp;
unsigned int status;
};
/* per-cpu kprobe control block */
struct kprobe_ctlblk {
unsigned int kprobe_status;
unsigned long saved_status;
struct prev_kprobe prev_kprobe;
};
void arch_remove_kprobe(struct kprobe *p);
bool kprobe_fault_handler(struct pt_regs *regs, int trapnr);
bool kprobe_breakpoint_handler(struct pt_regs *regs);
bool kprobe_singlestep_handler(struct pt_regs *regs);
#else /* !CONFIG_KPROBES */
static inline bool kprobe_breakpoint_handler(struct pt_regs *regs) { return false; }
static inline bool kprobe_singlestep_handler(struct pt_regs *regs) { return false; }
#endif /* CONFIG_KPROBES */
#endif /* __ASM_LOONGARCH_KPROBES_H */
@@ -0,0 +1,217 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2023 Loongson Technology Corporation Limited
*/
#ifndef __ASM_LOONGARCH_KVM_CSR_H__
#define __ASM_LOONGARCH_KVM_CSR_H__
#include <linux/uaccess.h>
#include <linux/kvm_host.h>
#include <asm/loongarch.h>
#include <asm/kvm_vcpu.h>
#define gcsr_read(csr) \
({ \
register unsigned long __v; \
__asm__ __volatile__( \
" gcsrrd %[val], %[reg]\n\t" \
: [val] "=r" (__v) \
: [reg] "i" (csr) \
: "memory"); \
__v; \
})
#define gcsr_write(v, csr) \
({ \
register unsigned long __v = v; \
__asm__ __volatile__ ( \
" gcsrwr %[val], %[reg]\n\t" \
: [val] "+r" (__v) \
: [reg] "i" (csr) \
: "memory"); \
__v; \
})
#define gcsr_xchg(v, m, csr) \
({ \
register unsigned long __v = v; \
__asm__ __volatile__( \
" gcsrxchg %[val], %[mask], %[reg]\n\t" \
: [val] "+r" (__v) \
: [mask] "r" (m), [reg] "i" (csr) \
: "memory"); \
__v; \
})
/* Guest CSRS read and write */
#define read_gcsr_crmd() gcsr_read(LOONGARCH_CSR_CRMD)
#define write_gcsr_crmd(val) gcsr_write(val, LOONGARCH_CSR_CRMD)
#define read_gcsr_prmd() gcsr_read(LOONGARCH_CSR_PRMD)
#define write_gcsr_prmd(val) gcsr_write(val, LOONGARCH_CSR_PRMD)
#define read_gcsr_euen() gcsr_read(LOONGARCH_CSR_EUEN)
#define write_gcsr_euen(val) gcsr_write(val, LOONGARCH_CSR_EUEN)
#define read_gcsr_misc() gcsr_read(LOONGARCH_CSR_MISC)
#define write_gcsr_misc(val) gcsr_write(val, LOONGARCH_CSR_MISC)
#define read_gcsr_ecfg() gcsr_read(LOONGARCH_CSR_ECFG)
#define write_gcsr_ecfg(val) gcsr_write(val, LOONGARCH_CSR_ECFG)
#define read_gcsr_estat() gcsr_read(LOONGARCH_CSR_ESTAT)
#define write_gcsr_estat(val) gcsr_write(val, LOONGARCH_CSR_ESTAT)
#define read_gcsr_era() gcsr_read(LOONGARCH_CSR_ERA)
#define write_gcsr_era(val) gcsr_write(val, LOONGARCH_CSR_ERA)
#define read_gcsr_badv() gcsr_read(LOONGARCH_CSR_BADV)
#define write_gcsr_badv(val) gcsr_write(val, LOONGARCH_CSR_BADV)
#define read_gcsr_badi() gcsr_read(LOONGARCH_CSR_BADI)
#define write_gcsr_badi(val) gcsr_write(val, LOONGARCH_CSR_BADI)
#define read_gcsr_eentry() gcsr_read(LOONGARCH_CSR_EENTRY)
#define write_gcsr_eentry(val) gcsr_write(val, LOONGARCH_CSR_EENTRY)
#define read_gcsr_asid() gcsr_read(LOONGARCH_CSR_ASID)
#define write_gcsr_asid(val) gcsr_write(val, LOONGARCH_CSR_ASID)
#define read_gcsr_pgdl() gcsr_read(LOONGARCH_CSR_PGDL)
#define write_gcsr_pgdl(val) gcsr_write(val, LOONGARCH_CSR_PGDL)
#define read_gcsr_pgdh() gcsr_read(LOONGARCH_CSR_PGDH)
#define write_gcsr_pgdh(val) gcsr_write(val, LOONGARCH_CSR_PGDH)
#define write_gcsr_pgd(val) gcsr_write(val, LOONGARCH_CSR_PGD)
#define read_gcsr_pgd() gcsr_read(LOONGARCH_CSR_PGD)
#define read_gcsr_pwctl0() gcsr_read(LOONGARCH_CSR_PWCTL0)
#define write_gcsr_pwctl0(val) gcsr_write(val, LOONGARCH_CSR_PWCTL0)
#define read_gcsr_pwctl1() gcsr_read(LOONGARCH_CSR_PWCTL1)
#define write_gcsr_pwctl1(val) gcsr_write(val, LOONGARCH_CSR_PWCTL1)
#define read_gcsr_stlbpgsize() gcsr_read(LOONGARCH_CSR_STLBPGSIZE)
#define write_gcsr_stlbpgsize(val) gcsr_write(val, LOONGARCH_CSR_STLBPGSIZE)
#define read_gcsr_rvacfg() gcsr_read(LOONGARCH_CSR_RVACFG)
#define write_gcsr_rvacfg(val) gcsr_write(val, LOONGARCH_CSR_RVACFG)
#define read_gcsr_cpuid() gcsr_read(LOONGARCH_CSR_CPUID)
#define write_gcsr_cpuid(val) gcsr_write(val, LOONGARCH_CSR_CPUID)
#define read_gcsr_prcfg1() gcsr_read(LOONGARCH_CSR_PRCFG1)
#define write_gcsr_prcfg1(val) gcsr_write(val, LOONGARCH_CSR_PRCFG1)
#define read_gcsr_prcfg2() gcsr_read(LOONGARCH_CSR_PRCFG2)
#define write_gcsr_prcfg2(val) gcsr_write(val, LOONGARCH_CSR_PRCFG2)
#define read_gcsr_prcfg3() gcsr_read(LOONGARCH_CSR_PRCFG3)
#define write_gcsr_prcfg3(val) gcsr_write(val, LOONGARCH_CSR_PRCFG3)
#define read_gcsr_kscratch0() gcsr_read(LOONGARCH_CSR_KS0)
#define write_gcsr_kscratch0(val) gcsr_write(val, LOONGARCH_CSR_KS0)
#define read_gcsr_kscratch1() gcsr_read(LOONGARCH_CSR_KS1)
#define write_gcsr_kscratch1(val) gcsr_write(val, LOONGARCH_CSR_KS1)
#define read_gcsr_kscratch2() gcsr_read(LOONGARCH_CSR_KS2)
#define write_gcsr_kscratch2(val) gcsr_write(val, LOONGARCH_CSR_KS2)
#define read_gcsr_kscratch3() gcsr_read(LOONGARCH_CSR_KS3)
#define write_gcsr_kscratch3(val) gcsr_write(val, LOONGARCH_CSR_KS3)
#define read_gcsr_kscratch4() gcsr_read(LOONGARCH_CSR_KS4)
#define write_gcsr_kscratch4(val) gcsr_write(val, LOONGARCH_CSR_KS4)
#define read_gcsr_kscratch5() gcsr_read(LOONGARCH_CSR_KS5)
#define write_gcsr_kscratch5(val) gcsr_write(val, LOONGARCH_CSR_KS5)
#define read_gcsr_kscratch6() gcsr_read(LOONGARCH_CSR_KS6)
#define write_gcsr_kscratch6(val) gcsr_write(val, LOONGARCH_CSR_KS6)
#define read_gcsr_kscratch7() gcsr_read(LOONGARCH_CSR_KS7)
#define write_gcsr_kscratch7(val) gcsr_write(val, LOONGARCH_CSR_KS7)
#define read_gcsr_timerid() gcsr_read(LOONGARCH_CSR_TMID)
#define write_gcsr_timerid(val) gcsr_write(val, LOONGARCH_CSR_TMID)
#define read_gcsr_timercfg() gcsr_read(LOONGARCH_CSR_TCFG)
#define write_gcsr_timercfg(val) gcsr_write(val, LOONGARCH_CSR_TCFG)
#define read_gcsr_timertick() gcsr_read(LOONGARCH_CSR_TVAL)
#define write_gcsr_timertick(val) gcsr_write(val, LOONGARCH_CSR_TVAL)
#define read_gcsr_timeroffset() gcsr_read(LOONGARCH_CSR_CNTC)
#define write_gcsr_timeroffset(val) gcsr_write(val, LOONGARCH_CSR_CNTC)
#define read_gcsr_llbctl() gcsr_read(LOONGARCH_CSR_LLBCTL)
#define write_gcsr_llbctl(val) gcsr_write(val, LOONGARCH_CSR_LLBCTL)
#define read_gcsr_tlbidx() gcsr_read(LOONGARCH_CSR_TLBIDX)
#define write_gcsr_tlbidx(val) gcsr_write(val, LOONGARCH_CSR_TLBIDX)
#define read_gcsr_tlbrentry() gcsr_read(LOONGARCH_CSR_TLBRENTRY)
#define write_gcsr_tlbrentry(val) gcsr_write(val, LOONGARCH_CSR_TLBRENTRY)
#define read_gcsr_tlbrbadv() gcsr_read(LOONGARCH_CSR_TLBRBADV)
#define write_gcsr_tlbrbadv(val) gcsr_write(val, LOONGARCH_CSR_TLBRBADV)
#define read_gcsr_tlbrera() gcsr_read(LOONGARCH_CSR_TLBRERA)
#define write_gcsr_tlbrera(val) gcsr_write(val, LOONGARCH_CSR_TLBRERA)
#define read_gcsr_tlbrsave() gcsr_read(LOONGARCH_CSR_TLBRSAVE)
#define write_gcsr_tlbrsave(val) gcsr_write(val, LOONGARCH_CSR_TLBRSAVE)
#define read_gcsr_tlbrelo0() gcsr_read(LOONGARCH_CSR_TLBRELO0)
#define write_gcsr_tlbrelo0(val) gcsr_write(val, LOONGARCH_CSR_TLBRELO0)
#define read_gcsr_tlbrelo1() gcsr_read(LOONGARCH_CSR_TLBRELO1)
#define write_gcsr_tlbrelo1(val) gcsr_write(val, LOONGARCH_CSR_TLBRELO1)
#define read_gcsr_tlbrehi() gcsr_read(LOONGARCH_CSR_TLBREHI)
#define write_gcsr_tlbrehi(val) gcsr_write(val, LOONGARCH_CSR_TLBREHI)
#define read_gcsr_tlbrprmd() gcsr_read(LOONGARCH_CSR_TLBRPRMD)
#define write_gcsr_tlbrprmd(val) gcsr_write(val, LOONGARCH_CSR_TLBRPRMD)
#define read_gcsr_directwin0() gcsr_read(LOONGARCH_CSR_DMWIN0)
#define write_gcsr_directwin0(val) gcsr_write(val, LOONGARCH_CSR_DMWIN0)
#define read_gcsr_directwin1() gcsr_read(LOONGARCH_CSR_DMWIN1)
#define write_gcsr_directwin1(val) gcsr_write(val, LOONGARCH_CSR_DMWIN1)
#define read_gcsr_directwin2() gcsr_read(LOONGARCH_CSR_DMWIN2)
#define write_gcsr_directwin2(val) gcsr_write(val, LOONGARCH_CSR_DMWIN2)
#define read_gcsr_directwin3() gcsr_read(LOONGARCH_CSR_DMWIN3)
#define write_gcsr_directwin3(val) gcsr_write(val, LOONGARCH_CSR_DMWIN3)
/* Guest related CSRs */
#define read_csr_gtlbc() csr_read64(LOONGARCH_CSR_GTLBC)
#define write_csr_gtlbc(val) csr_write64(val, LOONGARCH_CSR_GTLBC)
#define read_csr_trgp() csr_read64(LOONGARCH_CSR_TRGP)
#define read_csr_gcfg() csr_read64(LOONGARCH_CSR_GCFG)
#define write_csr_gcfg(val) csr_write64(val, LOONGARCH_CSR_GCFG)
#define read_csr_gstat() csr_read64(LOONGARCH_CSR_GSTAT)
#define write_csr_gstat(val) csr_write64(val, LOONGARCH_CSR_GSTAT)
#define read_csr_gintc() csr_read64(LOONGARCH_CSR_GINTC)
#define write_csr_gintc(val) csr_write64(val, LOONGARCH_CSR_GINTC)
#define read_csr_gcntc() csr_read64(LOONGARCH_CSR_GCNTC)
#define write_csr_gcntc(val) csr_write64(val, LOONGARCH_CSR_GCNTC)
#define __BUILD_GCSR_OP(name) __BUILD_CSR_COMMON(gcsr_##name)
__BUILD_CSR_OP(gcfg)
__BUILD_CSR_OP(gstat)
__BUILD_CSR_OP(gtlbc)
__BUILD_CSR_OP(gintc)
__BUILD_GCSR_OP(llbctl)
__BUILD_GCSR_OP(tlbidx)
#define set_gcsr_estat(val) \
gcsr_xchg(val, val, LOONGARCH_CSR_ESTAT)
#define clear_gcsr_estat(val) \
gcsr_xchg(~(val), val, LOONGARCH_CSR_ESTAT)
#define kvm_read_hw_gcsr(id) gcsr_read(id)
#define kvm_write_hw_gcsr(id, val) gcsr_write(val, id)
#define kvm_save_hw_gcsr(csr, gid) (csr->csrs[gid] = gcsr_read(gid))
#define kvm_restore_hw_gcsr(csr, gid) (gcsr_write(csr->csrs[gid], gid))
#define kvm_read_clear_hw_gcsr(csr, gid) (csr->csrs[gid] = gcsr_write(0, gid))
int kvm_emu_iocsr(larch_inst inst, struct kvm_run *run, struct kvm_vcpu *vcpu);
static __always_inline unsigned long kvm_read_sw_gcsr(struct loongarch_csrs *csr, int gid)
{
return csr->csrs[gid];
}
static __always_inline void kvm_write_sw_gcsr(struct loongarch_csrs *csr, int gid, unsigned long val)
{
csr->csrs[gid] = val;
}
static __always_inline void kvm_set_sw_gcsr(struct loongarch_csrs *csr,
int gid, unsigned long val)
{
csr->csrs[gid] |= val;
}
static __always_inline void kvm_change_sw_gcsr(struct loongarch_csrs *csr,
int gid, unsigned long mask, unsigned long val)
{
unsigned long _mask = mask;
csr->csrs[gid] &= ~_mask;
csr->csrs[gid] |= val & _mask;
}
#define KVM_PMU_EVENT_ENABLED (CSR_PERFCTRL_PLV0 | CSR_PERFCTRL_PLV1 | \
CSR_PERFCTRL_PLV2 | CSR_PERFCTRL_PLV3)
#endif /* __ASM_LOONGARCH_KVM_CSR_H__ */
@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2025 Loongson Technology Corporation Limited
*/
#ifndef __ASM_KVM_DMSINTC_H
#define __ASM_KVM_DMSINTC_H
#include <linux/kvm_types.h>
struct loongarch_dmsintc {
struct kvm *kvm;
uint64_t msg_addr_base;
uint64_t msg_addr_size;
uint32_t cpu_mask;
};
struct dmsintc_state {
atomic64_t vector_map[4];
};
int kvm_loongarch_register_dmsintc_device(void);
void dmsintc_inject_irq(struct kvm_vcpu *vcpu);
int dmsintc_set_irq(struct kvm *kvm, u64 addr, int data, int level);
int dmsintc_deliver_msi_to_vcpu(struct kvm *kvm, struct kvm_vcpu *vcpu, u32 vector, int level);
#endif
@@ -0,0 +1,84 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2024 Loongson Technology Corporation Limited
*/
#ifndef __ASM_KVM_EIOINTC_H
#define __ASM_KVM_EIOINTC_H
#include <kvm/iodev.h>
#define EIOINTC_IRQS 256
#define EIOINTC_ROUTE_MAX_VCPUS 256
#define EIOINTC_IRQS_U64_NUMS (EIOINTC_IRQS / 64)
/* map to ipnum per 32 irqs */
#define EIOINTC_IRQS_NODETYPE_COUNT 16
#define EIOINTC_BASE 0x1400
#define EIOINTC_SIZE 0x900
#define EIOINTC_NODETYPE_START 0xa0
#define EIOINTC_NODETYPE_END 0xbf
#define EIOINTC_IPMAP_START 0xc0
#define EIOINTC_IPMAP_END 0xc7
#define EIOINTC_ENABLE_START 0x200
#define EIOINTC_ENABLE_END 0x21f
#define EIOINTC_BOUNCE_START 0x280
#define EIOINTC_BOUNCE_END 0x29f
#define EIOINTC_ISR_START 0x300
#define EIOINTC_ISR_END 0x31f
#define EIOINTC_COREISR_START 0x400
#define EIOINTC_COREISR_END 0x41f
#define EIOINTC_COREMAP_START 0x800
#define EIOINTC_COREMAP_END 0x8ff
#define EIOINTC_VIRT_BASE (0x40000000)
#define EIOINTC_VIRT_SIZE (0x1000)
#define EIOINTC_VIRT_FEATURES (0x0)
#define EIOINTC_HAS_VIRT_EXTENSION (0)
#define EIOINTC_HAS_ENABLE_OPTION (1)
#define EIOINTC_HAS_INT_ENCODE (2)
#define EIOINTC_HAS_CPU_ENCODE (3)
#define EIOINTC_VIRT_HAS_FEATURES ((1U << EIOINTC_HAS_VIRT_EXTENSION) \
| (1U << EIOINTC_HAS_ENABLE_OPTION) \
| (1U << EIOINTC_HAS_INT_ENCODE) \
| (1U << EIOINTC_HAS_CPU_ENCODE))
#define EIOINTC_VIRT_CONFIG (0x4)
#define EIOINTC_ENABLE (1)
#define EIOINTC_ENABLE_INT_ENCODE (2)
#define EIOINTC_ENABLE_CPU_ENCODE (3)
#define LOONGSON_IP_NUM 8
struct loongarch_eiointc {
spinlock_t lock;
struct kvm *kvm;
struct kvm_io_device device;
struct kvm_io_device device_vext;
uint32_t num_cpu;
uint32_t features;
uint32_t status;
/* hardware state */
u64 nodetype[EIOINTC_IRQS_NODETYPE_COUNT / 4];
/* one bit shows the state of one irq */
u64 bounce[EIOINTC_IRQS_U64_NUMS];
u64 isr[EIOINTC_IRQS_U64_NUMS];
u64 coreisr[EIOINTC_ROUTE_MAX_VCPUS][EIOINTC_IRQS_U64_NUMS];
u64 enable[EIOINTC_IRQS_U64_NUMS];
/* use one byte to config ipmap for 32 irqs at once */
u64 ipmap;
/* use one byte to config coremap for one irq */
u64 coremap[EIOINTC_IRQS / 8];
DECLARE_BITMAP(sw_coreisr[EIOINTC_ROUTE_MAX_VCPUS][LOONGSON_IP_NUM], EIOINTC_IRQS);
uint8_t sw_coremap[EIOINTC_IRQS];
};
int kvm_loongarch_register_eiointc_device(void);
void eiointc_set_irq(struct loongarch_eiointc *s, int irq, int level);
#endif /* __ASM_KVM_EIOINTC_H */
@@ -0,0 +1,368 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2023 Loongson Technology Corporation Limited
*/
#ifndef __ASM_LOONGARCH_KVM_HOST_H__
#define __ASM_LOONGARCH_KVM_HOST_H__
#include <linux/cpumask.h>
#include <linux/hrtimer.h>
#include <linux/interrupt.h>
#include <linux/kvm.h>
#include <linux/kvm_types.h>
#include <linux/mutex.h>
#include <linux/perf_event.h>
#include <linux/spinlock.h>
#include <linux/threads.h>
#include <linux/types.h>
#include <asm/inst.h>
#include <asm/kvm_mmu.h>
#include <asm/kvm_ipi.h>
#include <asm/kvm_dmsintc.h>
#include <asm/kvm_eiointc.h>
#include <asm/kvm_pch_pic.h>
#include <asm/loongarch.h>
#define __KVM_HAVE_ARCH_INTC_INITIALIZED
/* Loongarch KVM register ids */
#define KVM_GET_IOC_CSR_IDX(id) ((id & KVM_CSR_IDX_MASK) >> LOONGARCH_REG_SHIFT)
#define KVM_GET_IOC_CPUCFG_IDX(id) ((id & KVM_CPUCFG_IDX_MASK) >> LOONGARCH_REG_SHIFT)
#define KVM_MAX_VCPUS 256
#define KVM_MAX_CPUCFG_REGS 21
#define KVM_HALT_POLL_NS_DEFAULT 500000
#define KVM_REQ_TLB_FLUSH_GPA KVM_ARCH_REQ(0)
#define KVM_REQ_STEAL_UPDATE KVM_ARCH_REQ(1)
#define KVM_REQ_PMU KVM_ARCH_REQ(2)
#define KVM_REQ_FPU_LOAD KVM_ARCH_REQ(3)
#define KVM_REQ_LBT_LOAD KVM_ARCH_REQ(4)
#define KVM_GUESTDBG_SW_BP_MASK \
(KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP)
#define KVM_GUESTDBG_VALID_MASK \
(KVM_GUESTDBG_ENABLE | KVM_GUESTDBG_USE_SW_BP | KVM_GUESTDBG_SINGLESTEP)
#define KVM_DIRTY_LOG_MANUAL_CAPS \
(KVM_DIRTY_LOG_MANUAL_PROTECT_ENABLE | KVM_DIRTY_LOG_INITIALLY_SET)
struct kvm_vm_stat {
struct kvm_vm_stat_generic generic;
u64 pages;
u64 hugepages;
};
struct kvm_vcpu_stat {
struct kvm_vcpu_stat_generic generic;
u64 int_exits;
u64 idle_exits;
u64 cpucfg_exits;
u64 signal_exits;
u64 hypercall_exits;
u64 ipi_read_exits;
u64 ipi_write_exits;
u64 eiointc_read_exits;
u64 eiointc_write_exits;
u64 pch_pic_read_exits;
u64 pch_pic_write_exits;
};
#define KVM_MEM_HUGEPAGE_CAPABLE (1UL << 0)
#define KVM_MEM_HUGEPAGE_INCAPABLE (1UL << 1)
struct kvm_arch_memory_slot {
unsigned long flags;
};
#define HOST_MAX_PMNUM 16
struct kvm_context {
unsigned long vpid_cache;
struct kvm_vcpu *last_vcpu;
/* Host PMU CSR */
u64 perf_ctrl[HOST_MAX_PMNUM];
u64 perf_cntr[HOST_MAX_PMNUM];
};
struct kvm_world_switch {
int (*exc_entry)(void);
int (*enter_guest)(struct kvm_run *run, struct kvm_vcpu *vcpu);
};
#define MAX_PGTABLE_LEVELS 4
/*
* Physical CPUID is used for interrupt routing, there are different
* definitions about physical cpuid on different hardwares.
*
* For LOONGARCH_CSR_CPUID register, max CPUID size if 512
* For IPI hardware, max destination CPUID size 1024
* For eiointc interrupt controller, max destination CPUID size is 256
* For msgint interrupt controller, max supported CPUID size is 65536
*
* Currently max CPUID is defined as 256 for KVM hypervisor, in future
* it will be expanded to 4096, including 16 packages at most. And every
* package supports at most 256 vcpus
*/
#define KVM_MAX_PHYID 256
struct kvm_phyid_info {
struct kvm_vcpu *vcpu;
bool enabled;
};
struct kvm_phyid_map {
int max_phyid;
struct kvm_phyid_info phys_map[KVM_MAX_PHYID];
};
struct kvm_arch {
/* Guest physical mm */
kvm_pte_t *pgd;
unsigned long gpa_size;
unsigned long invalid_ptes[MAX_PGTABLE_LEVELS];
unsigned int pte_shifts[MAX_PGTABLE_LEVELS];
unsigned int root_level;
spinlock_t phyid_map_lock;
struct kvm_phyid_map *phyid_map;
/* Enabled PV features */
unsigned long pv_features;
/* Supported KVM features */
unsigned long kvm_features;
s64 time_offset;
struct kvm_context __percpu *vmcs;
struct loongarch_ipi *ipi;
struct loongarch_dmsintc *dmsintc;
struct loongarch_eiointc *eiointc;
struct loongarch_pch_pic *pch_pic;
};
#define CSR_MAX_NUMS 0x800
struct loongarch_csrs {
unsigned long csrs[CSR_MAX_NUMS];
};
/* Resume Flags */
#define RESUME_HOST 0
#define RESUME_GUEST 1
enum emulation_result {
EMULATE_DONE, /* no further processing */
EMULATE_DO_MMIO, /* kvm_run filled with MMIO request */
EMULATE_DO_IOCSR, /* handle IOCSR request */
EMULATE_FAIL, /* can't emulate this instruction */
EMULATE_EXCEPT, /* A guest exception has been generated */
};
#define KVM_LARCH_FPU (0x1 << 0)
#define KVM_LARCH_LBT (0x1 << 1)
#define KVM_LARCH_PMU (0x1 << 2)
#define KVM_LARCH_SWCSR_LATEST (0x1 << 3)
#define KVM_LARCH_HWCSR_USABLE (0x1 << 4)
#define LOONGARCH_PV_FEAT_UPDATED BIT_ULL(63)
#define LOONGARCH_PV_FEAT_MASK (BIT(KVM_FEATURE_IPI) | \
BIT(KVM_FEATURE_PREEMPT) | \
BIT(KVM_FEATURE_STEAL_TIME) | \
BIT(KVM_FEATURE_USER_HCALL) | \
BIT(KVM_FEATURE_VIRT_EXTIOI))
struct kvm_vcpu_arch {
/*
* Switch pointer-to-function type to unsigned long
* for loading the value into register directly.
*/
unsigned long host_eentry;
unsigned long guest_eentry;
/* Pointers stored here for easy accessing from assembly code */
int (*handle_exit)(struct kvm_run *run, struct kvm_vcpu *vcpu);
/* GPA (=HVA) of PGD for secondary mmu */
unsigned long kvm_pgd;
/* Host registers preserved across guest mode execution */
unsigned long host_sp;
unsigned long host_tp;
unsigned long host_pgd;
/* Host CSRs are used when handling exits from guest */
unsigned long badi;
unsigned long badv;
unsigned long host_ecfg;
unsigned long host_estat;
unsigned long host_percpu;
/* GPRs */
unsigned long gprs[32];
unsigned long pc;
/* Which auxiliary state is loaded (KVM_LARCH_*) */
unsigned int aux_inuse;
/* FPU state */
struct loongarch_fpu fpu FPU_ALIGN;
struct loongarch_lbt lbt;
/* CSR state */
struct loongarch_csrs *csr;
/* Guest max PMU CSR id */
int max_pmu_csrid;
/* GPR used as IO source/target */
u32 io_gpr;
/* KVM register to control count timer */
u32 count_ctl;
struct hrtimer swtimer;
/* Bitmask of intr that are pending */
unsigned long irq_pending;
/* Bitmask of pending intr to be cleared */
unsigned long irq_clear;
/* Bitmask of exceptions that are pending */
unsigned long exception_pending;
unsigned int esubcode;
/* Cache for pages needed inside spinlock regions */
struct kvm_mmu_memory_cache mmu_page_cache;
/* vcpu's vpid */
u64 vpid;
gpa_t flush_gpa;
/* Frequency of stable timer in Hz */
u64 timer_mhz;
ktime_t expire;
/* Last CPU the vCPU state was loaded on */
int last_sched_cpu;
/* mp state */
struct kvm_mp_state mp_state;
/* ipi state */
struct ipi_state ipi_state;
struct dmsintc_state dmsintc_state;
/* cpucfg */
u32 cpucfg[KVM_MAX_CPUCFG_REGS];
/* paravirt steal time */
struct {
u64 guest_addr;
u64 last_steal;
struct gfn_to_hva_cache cache;
u8 preempted;
} st;
};
static inline unsigned long readl_sw_gcsr(struct loongarch_csrs *csr, int reg)
{
return csr->csrs[reg];
}
static inline void writel_sw_gcsr(struct loongarch_csrs *csr, int reg, unsigned long val)
{
csr->csrs[reg] = val;
}
static inline bool kvm_guest_has_msgint(struct kvm_vcpu_arch *arch)
{
return arch->cpucfg[1] & CPUCFG1_MSGINT;
}
static inline bool kvm_guest_has_fpu(struct kvm_vcpu_arch *arch)
{
return arch->cpucfg[2] & CPUCFG2_FP;
}
static inline bool kvm_guest_has_lsx(struct kvm_vcpu_arch *arch)
{
return arch->cpucfg[2] & CPUCFG2_LSX;
}
static inline bool kvm_guest_has_lasx(struct kvm_vcpu_arch *arch)
{
return arch->cpucfg[2] & CPUCFG2_LASX;
}
static inline bool kvm_guest_has_lbt(struct kvm_vcpu_arch *arch)
{
return arch->cpucfg[2] & (CPUCFG2_X86BT | CPUCFG2_ARMBT | CPUCFG2_MIPSBT);
}
static inline bool kvm_guest_has_pmu(struct kvm_vcpu_arch *arch)
{
return arch->cpucfg[6] & CPUCFG6_PMP;
}
static inline int kvm_get_pmu_num(struct kvm_vcpu_arch *arch)
{
return (arch->cpucfg[6] & CPUCFG6_PMNUM) >> CPUCFG6_PMNUM_SHIFT;
}
/* Check whether KVM support this feature (VMM may disable it) */
static inline bool kvm_vm_support(struct kvm_arch *arch, int feature)
{
return !!(arch->kvm_features & BIT_ULL(feature));
}
bool kvm_arch_pmi_in_guest(struct kvm_vcpu *vcpu);
/* Debug: dump vcpu state */
int kvm_arch_vcpu_dump_regs(struct kvm_vcpu *vcpu);
/* MMU handling */
void kvm_flush_tlb_all(void);
void kvm_flush_tlb_gpa(struct kvm_vcpu *vcpu, unsigned long gpa);
int kvm_handle_mm_fault(struct kvm_vcpu *vcpu, unsigned long badv, bool write, int ecode);
int kvm_unmap_hva_range(struct kvm *kvm, unsigned long start, unsigned long end, bool blockable);
int kvm_age_hva(struct kvm *kvm, unsigned long start, unsigned long end);
int kvm_test_age_hva(struct kvm *kvm, unsigned long hva);
static inline void update_pc(struct kvm_vcpu_arch *arch)
{
arch->pc += 4;
}
/*
* kvm_is_ifetch_fault() - Find whether a TLBL exception is due to ifetch fault.
* @vcpu: Virtual CPU.
*
* Returns: Whether the TLBL exception was likely due to an instruction
* fetch fault rather than a data load fault.
*/
static inline bool kvm_is_ifetch_fault(struct kvm_vcpu_arch *arch)
{
return arch->pc == arch->badv;
}
/* Misc */
static inline void kvm_arch_hardware_unsetup(void) {}
static inline void kvm_arch_memslots_updated(struct kvm *kvm, u64 gen) {}
static inline void kvm_arch_vcpu_blocking(struct kvm_vcpu *vcpu) {}
static inline void kvm_arch_vcpu_unblocking(struct kvm_vcpu *vcpu) {}
static inline void kvm_arch_vcpu_block_finish(struct kvm_vcpu *vcpu) {}
static inline void kvm_arch_free_memslot(struct kvm *kvm, struct kvm_memory_slot *slot) {}
void kvm_check_vpid(struct kvm_vcpu *vcpu);
enum hrtimer_restart kvm_swtimer_wakeup(struct hrtimer *timer);
void kvm_arch_flush_remote_tlbs_memslot(struct kvm *kvm, const struct kvm_memory_slot *memslot);
void kvm_init_vmcs(struct kvm *kvm);
void kvm_exc_entry(void);
int kvm_enter_guest(struct kvm_run *run, struct kvm_vcpu *vcpu);
extern unsigned long vpid_mask;
extern struct kvm_world_switch *kvm_loongarch_ops;
#define SW_GCSR (1 << 0)
#define HW_GCSR (1 << 1)
#define INVALID_GCSR (1 << 2)
int get_gcsr_flag(int csr);
void set_hw_gcsr(int csr_id, unsigned long val);
#endif /* __ASM_LOONGARCH_KVM_HOST_H__ */
@@ -0,0 +1,45 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2024 Loongson Technology Corporation Limited
*/
#ifndef __ASM_KVM_IPI_H
#define __ASM_KVM_IPI_H
#include <kvm/iodev.h>
#define LARCH_INT_IPI 12
struct loongarch_ipi {
spinlock_t lock;
struct kvm *kvm;
struct kvm_io_device device;
};
struct ipi_state {
spinlock_t lock;
uint32_t status;
uint32_t en;
uint32_t set;
uint32_t clear;
uint64_t buf[4];
};
#define IOCSR_IPI_BASE 0x1000
#define IOCSR_IPI_SIZE 0x160
#define IOCSR_IPI_STATUS 0x000
#define IOCSR_IPI_EN 0x004
#define IOCSR_IPI_SET 0x008
#define IOCSR_IPI_CLEAR 0x00c
#define IOCSR_IPI_BUF_20 0x020
#define IOCSR_IPI_BUF_28 0x028
#define IOCSR_IPI_BUF_30 0x030
#define IOCSR_IPI_BUF_38 0x038
#define IOCSR_IPI_SEND 0x040
#define IOCSR_MAIL_SEND 0x048
#define IOCSR_ANY_SEND 0x158
int kvm_loongarch_register_ipi_device(void);
#endif
@@ -0,0 +1,151 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2023 Loongson Technology Corporation Limited
*/
#ifndef __ASM_LOONGARCH_KVM_MMU_H__
#define __ASM_LOONGARCH_KVM_MMU_H__
#include <linux/kvm_host.h>
#include <asm/pgalloc.h>
#include <asm/tlb.h>
/*
* KVM_MMU_CACHE_MIN_PAGES is the number of GPA page table translation levels
* for which pages need to be cached.
*/
#define KVM_MMU_CACHE_MIN_PAGES (CONFIG_PGTABLE_LEVELS - 1)
/*
* _PAGE_MODIFIED is a SW pte bit, it records page ever written on host
* kernel, on secondary MMU it records the page writeable attribute, in
* order for fast path handling.
*/
#define KVM_PAGE_WRITEABLE _PAGE_MODIFIED
#define _KVM_FLUSH_PGTABLE 0x1
#define _KVM_HAS_PGMASK 0x2
#define kvm_pfn_pte(pfn, prot) (((pfn) << PFN_PTE_SHIFT) | pgprot_val(prot))
#define kvm_pte_pfn(x) ((phys_addr_t)((x & _PFN_MASK) >> PFN_PTE_SHIFT))
typedef unsigned long kvm_pte_t;
typedef struct kvm_ptw_ctx kvm_ptw_ctx;
typedef int (*kvm_pte_ops)(kvm_pte_t *pte, phys_addr_t addr, kvm_ptw_ctx *ctx);
struct kvm_ptw_ctx {
kvm_pte_ops ops;
unsigned long flag;
/* for kvm_arch_mmu_enable_log_dirty_pt_masked use */
unsigned long mask;
unsigned long gfn;
/* page walk mmu info */
unsigned int level;
unsigned long pgtable_shift;
unsigned long invalid_entry;
unsigned long *invalid_ptes;
unsigned int *pte_shifts;
void *opaque;
/* free pte table page list */
struct list_head list;
};
kvm_pte_t *kvm_pgd_alloc(void);
static inline void kvm_set_pte(kvm_pte_t *ptep, kvm_pte_t val)
{
WRITE_ONCE(*ptep, val);
}
static inline int kvm_pte_young(kvm_pte_t pte) { return pte & _PAGE_ACCESSED; }
static inline int kvm_pte_huge(kvm_pte_t pte) { return pte & _PAGE_HUGE; }
static inline int kvm_pte_dirty(kvm_pte_t pte) { return pte & __WRITEABLE; }
static inline int kvm_pte_writeable(kvm_pte_t pte) { return pte & KVM_PAGE_WRITEABLE; }
static inline kvm_pte_t kvm_pte_mkyoung(kvm_pte_t pte)
{
return pte | _PAGE_ACCESSED;
}
static inline kvm_pte_t kvm_pte_mkold(kvm_pte_t pte)
{
return pte & ~_PAGE_ACCESSED;
}
static inline kvm_pte_t kvm_pte_mkdirty(kvm_pte_t pte)
{
return pte | __WRITEABLE;
}
static inline kvm_pte_t kvm_pte_mkclean(kvm_pte_t pte)
{
return pte & ~__WRITEABLE;
}
static inline kvm_pte_t kvm_pte_mkhuge(kvm_pte_t pte)
{
return pte | _PAGE_HUGE;
}
static inline kvm_pte_t kvm_pte_mksmall(kvm_pte_t pte)
{
return pte & ~_PAGE_HUGE;
}
static inline kvm_pte_t kvm_pte_mkwriteable(kvm_pte_t pte)
{
return pte | KVM_PAGE_WRITEABLE;
}
static inline int kvm_need_flush(kvm_ptw_ctx *ctx)
{
return ctx->flag & _KVM_FLUSH_PGTABLE;
}
static inline kvm_pte_t *kvm_pgtable_offset(kvm_ptw_ctx *ctx, kvm_pte_t *table,
phys_addr_t addr)
{
return table + ((addr >> ctx->pgtable_shift) & (PTRS_PER_PTE - 1));
}
static inline phys_addr_t kvm_pgtable_addr_end(kvm_ptw_ctx *ctx,
phys_addr_t addr, phys_addr_t end)
{
phys_addr_t boundary, size;
size = 0x1UL << ctx->pgtable_shift;
boundary = (addr + size) & ~(size - 1);
return (boundary - 1 < end - 1) ? boundary : end;
}
static inline int kvm_pte_present(kvm_ptw_ctx *ctx, kvm_pte_t *entry)
{
if (!ctx || ctx->level == 0)
return !!(*entry & _PAGE_PRESENT);
return *entry != ctx->invalid_entry;
}
static inline int kvm_pte_none(kvm_ptw_ctx *ctx, kvm_pte_t *entry)
{
return *entry == ctx->invalid_entry;
}
static inline void kvm_ptw_enter(kvm_ptw_ctx *ctx)
{
ctx->level--;
ctx->pgtable_shift = ctx->pte_shifts[ctx->level];
ctx->invalid_entry = ctx->invalid_ptes[ctx->level];
}
static inline void kvm_ptw_exit(kvm_ptw_ctx *ctx)
{
ctx->level++;
ctx->pgtable_shift = ctx->pte_shifts[ctx->level];
ctx->invalid_entry = ctx->invalid_ptes[ctx->level];
}
#endif /* __ASM_LOONGARCH_KVM_MMU_H__ */
@@ -0,0 +1,189 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_LOONGARCH_KVM_PARA_H
#define _ASM_LOONGARCH_KVM_PARA_H
#include <uapi/asm/kvm_para.h>
/*
* Hypercall code field
*/
#define HYPERVISOR_KVM 1
#define HYPERVISOR_VENDOR_SHIFT 8
#define HYPERCALL_ENCODE(vendor, code) ((vendor << HYPERVISOR_VENDOR_SHIFT) + code)
#define KVM_HCALL_CODE_SERVICE 0
#define KVM_HCALL_CODE_SWDBG 1
#define KVM_HCALL_CODE_USER_SERVICE 2
#define KVM_HCALL_SERVICE HYPERCALL_ENCODE(HYPERVISOR_KVM, KVM_HCALL_CODE_SERVICE)
#define KVM_HCALL_FUNC_IPI 1
#define KVM_HCALL_FUNC_NOTIFY 2
#define KVM_HCALL_SWDBG HYPERCALL_ENCODE(HYPERVISOR_KVM, KVM_HCALL_CODE_SWDBG)
#define KVM_HCALL_USER_SERVICE HYPERCALL_ENCODE(HYPERVISOR_KVM, KVM_HCALL_CODE_USER_SERVICE)
/*
* LoongArch hypercall return code
*/
#define KVM_HCALL_SUCCESS 0
#define KVM_HCALL_INVALID_CODE -1UL
#define KVM_HCALL_INVALID_PARAMETER -2UL
#define KVM_STEAL_PHYS_VALID BIT_ULL(0)
#define KVM_STEAL_PHYS_MASK GENMASK_ULL(63, 6)
struct kvm_steal_time {
__u64 steal;
__u32 version;
__u32 flags;
__u8 preempted;
__u8 pad[47];
};
#define KVM_VCPU_PREEMPTED (1 << 0)
/*
* Hypercall interface for KVM hypervisor
*
* a0: function identifier
* a1-a5: args
* Return value will be placed in a0.
* Up to 5 arguments are passed in a1, a2, a3, a4, a5.
*/
static __always_inline long kvm_hypercall0(u64 fid)
{
register long ret asm("a0");
register unsigned long fun asm("a0") = fid;
__asm__ __volatile__(
"hvcl "__stringify(KVM_HCALL_SERVICE)
: "=r" (ret)
: "r" (fun)
: "memory"
);
return ret;
}
static __always_inline long kvm_hypercall1(u64 fid, unsigned long arg0)
{
register long ret asm("a0");
register unsigned long fun asm("a0") = fid;
register unsigned long a1 asm("a1") = arg0;
__asm__ __volatile__(
"hvcl "__stringify(KVM_HCALL_SERVICE)
: "=r" (ret)
: "r" (fun), "r" (a1)
: "memory"
);
return ret;
}
static __always_inline long kvm_hypercall2(u64 fid,
unsigned long arg0, unsigned long arg1)
{
register long ret asm("a0");
register unsigned long fun asm("a0") = fid;
register unsigned long a1 asm("a1") = arg0;
register unsigned long a2 asm("a2") = arg1;
__asm__ __volatile__(
"hvcl "__stringify(KVM_HCALL_SERVICE)
: "=r" (ret)
: "r" (fun), "r" (a1), "r" (a2)
: "memory"
);
return ret;
}
static __always_inline long kvm_hypercall3(u64 fid,
unsigned long arg0, unsigned long arg1, unsigned long arg2)
{
register long ret asm("a0");
register unsigned long fun asm("a0") = fid;
register unsigned long a1 asm("a1") = arg0;
register unsigned long a2 asm("a2") = arg1;
register unsigned long a3 asm("a3") = arg2;
__asm__ __volatile__(
"hvcl "__stringify(KVM_HCALL_SERVICE)
: "=r" (ret)
: "r" (fun), "r" (a1), "r" (a2), "r" (a3)
: "memory"
);
return ret;
}
static __always_inline long kvm_hypercall4(u64 fid,
unsigned long arg0, unsigned long arg1,
unsigned long arg2, unsigned long arg3)
{
register long ret asm("a0");
register unsigned long fun asm("a0") = fid;
register unsigned long a1 asm("a1") = arg0;
register unsigned long a2 asm("a2") = arg1;
register unsigned long a3 asm("a3") = arg2;
register unsigned long a4 asm("a4") = arg3;
__asm__ __volatile__(
"hvcl "__stringify(KVM_HCALL_SERVICE)
: "=r" (ret)
: "r"(fun), "r" (a1), "r" (a2), "r" (a3), "r" (a4)
: "memory"
);
return ret;
}
static __always_inline long kvm_hypercall5(u64 fid,
unsigned long arg0, unsigned long arg1,
unsigned long arg2, unsigned long arg3, unsigned long arg4)
{
register long ret asm("a0");
register unsigned long fun asm("a0") = fid;
register unsigned long a1 asm("a1") = arg0;
register unsigned long a2 asm("a2") = arg1;
register unsigned long a3 asm("a3") = arg2;
register unsigned long a4 asm("a4") = arg3;
register unsigned long a5 asm("a5") = arg4;
__asm__ __volatile__(
"hvcl "__stringify(KVM_HCALL_SERVICE)
: "=r" (ret)
: "r"(fun), "r" (a1), "r" (a2), "r" (a3), "r" (a4), "r" (a5)
: "memory"
);
return ret;
}
#ifdef CONFIG_PARAVIRT
bool kvm_para_available(void);
unsigned int kvm_arch_para_features(void);
#else
static inline bool kvm_para_available(void)
{
return false;
}
static inline unsigned int kvm_arch_para_features(void)
{
return 0;
}
#endif
static inline unsigned int kvm_arch_para_hints(void)
{
return 0;
}
static inline bool kvm_check_and_clear_guest_paused(void)
{
return false;
}
#endif /* _ASM_LOONGARCH_KVM_PARA_H */
@@ -0,0 +1,76 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2024 Loongson Technology Corporation Limited
*/
#ifndef __ASM_KVM_PCH_PIC_H
#define __ASM_KVM_PCH_PIC_H
#include <kvm/iodev.h>
#define PCH_PIC_SIZE 0x3e8
#define PCH_PIC_INT_ID_START 0x0
#define PCH_PIC_INT_ID_END 0x7
#define PCH_PIC_MASK_START 0x20
#define PCH_PIC_MASK_END 0x27
#define PCH_PIC_HTMSI_EN_START 0x40
#define PCH_PIC_HTMSI_EN_END 0x47
#define PCH_PIC_EDGE_START 0x60
#define PCH_PIC_EDGE_END 0x67
#define PCH_PIC_CLEAR_START 0x80
#define PCH_PIC_CLEAR_END 0x87
#define PCH_PIC_AUTO_CTRL0_START 0xc0
#define PCH_PIC_AUTO_CTRL0_END 0xc7
#define PCH_PIC_AUTO_CTRL1_START 0xe0
#define PCH_PIC_AUTO_CTRL1_END 0xe7
#define PCH_PIC_ROUTE_ENTRY_START 0x100
#define PCH_PIC_ROUTE_ENTRY_END 0x13f
#define PCH_PIC_HTMSI_VEC_START 0x200
#define PCH_PIC_HTMSI_VEC_END 0x23f
#define PCH_PIC_INT_IRR_START 0x380
#define PCH_PIC_INT_IRR_END 0x38f
#define PCH_PIC_INT_ISR_START 0x3a0
#define PCH_PIC_INT_ISR_END 0x3af
#define PCH_PIC_POLARITY_START 0x3e0
#define PCH_PIC_POLARITY_END 0x3e7
#define PCH_PIC_INT_ID_VAL 0x7UL
#define PCH_PIC_INT_ID_VER 0x1UL
union pch_pic_id {
struct {
uint8_t reserved_0[3];
uint8_t id;
uint8_t version;
uint8_t reserved_1;
uint8_t irq_num;
uint8_t reserved_2;
} desc;
uint64_t data;
};
struct loongarch_pch_pic {
spinlock_t lock;
struct kvm *kvm;
struct kvm_io_device device;
union pch_pic_id id;
uint64_t mask; /* 1:disable irq, 0:enable irq */
uint64_t htmsi_en; /* 1:msi */
uint64_t edge; /* 1:edge triggered, 0:level triggered */
uint64_t auto_ctrl0; /* only use default value 00b */
uint64_t auto_ctrl1; /* only use default value 00b */
uint64_t last_intirr; /* edge detection */
uint64_t irr; /* interrupt request register */
uint64_t isr; /* interrupt service register */
uint64_t polarity; /* 0: high level trigger, 1: low level trigger */
uint8_t route_entry[64]; /* default value 0, route to int0: eiointc */
uint8_t htmsi_vector[64]; /* irq route table for routing to eiointc */
uint64_t pch_pic_base;
};
struct kvm_kernel_irq_routing_entry;
int kvm_loongarch_register_pch_pic_device(void);
void pch_pic_set_irq(struct loongarch_pch_pic *s, int irq, int level);
int pch_msi_set_irq(struct kvm *kvm, struct kvm_kernel_irq_routing_entry *e, int level);
#endif /* __ASM_KVM_PCH_PIC_H */
@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2023 Loongson Technology Corporation Limited
*/
#ifndef _ASM_LOONGARCH_KVM_TYPES_H
#define _ASM_LOONGARCH_KVM_TYPES_H
#define KVM_ARCH_NR_OBJS_PER_MEMORY_CACHE 40
#endif /* _ASM_LOONGARCH_KVM_TYPES_H */
@@ -0,0 +1,155 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2023 Loongson Technology Corporation Limited
*/
#ifndef __ASM_LOONGARCH_KVM_VCPU_H__
#define __ASM_LOONGARCH_KVM_VCPU_H__
#include <linux/kvm_host.h>
#include <asm/loongarch.h>
/* Controlled by 0x5 guest estat */
#define CPU_SIP0 BIT(INT_SWI0)
#define CPU_SIP1 BIT(INT_SWI1)
#define CPU_HWI0 BIT(INT_HWI0)
#define CPU_HWI1 BIT(INT_HWI1)
#define CPU_HWI2 BIT(INT_HWI2)
#define CPU_HWI3 BIT(INT_HWI3)
#define CPU_HWI4 BIT(INT_HWI4)
#define CPU_HWI5 BIT(INT_HWI5)
#define CPU_HWI6 BIT(INT_HWI6)
#define CPU_HWI7 BIT(INT_HWI7)
#define CPU_PMU BIT(INT_PCOV)
#define CPU_TIMER BIT(INT_TI)
#define CPU_IPI BIT(INT_IPI)
#define CPU_AVEC BIT(INT_AVEC)
#define KVM_ESTAT_INTI_MASK (CPU_SIP0 | CPU_SIP1 | CPU_PMU | CPU_TIMER \
| CPU_IPI | CPU_AVEC)
#define KVM_ESTAT_EXTI_MASK (CPU_HWI0 | CPU_HWI1 | CPU_HWI2 | CPU_HWI3 \
| CPU_HWI4 | CPU_HWI5 | CPU_HWI6 | CPU_HWI7)
/* Controlled by 0x52 guest exception VIP aligned to estat bit 5~12 */
#define VIP_DELTA (INT_HWI0 - CSR_GINTC_VIP_SHIFT)
#define CPU_IP0 BIT(INT_HWI0 - VIP_DELTA)
#define CPU_IP1 BIT(INT_HWI1 - VIP_DELTA)
#define CPU_IP2 BIT(INT_HWI2 - VIP_DELTA)
#define CPU_IP3 BIT(INT_HWI3 - VIP_DELTA)
#define CPU_IP4 BIT(INT_HWI4 - VIP_DELTA)
#define CPU_IP5 BIT(INT_HWI5 - VIP_DELTA)
#define CPU_IP6 BIT(INT_HWI6 - VIP_DELTA)
#define CPU_IP7 BIT(INT_HWI7 - VIP_DELTA)
#define KVM_GINTC_IRQ_MASK (CPU_IP0 | CPU_IP1 | CPU_IP2 | CPU_IP3 \
| CPU_IP4 | CPU_IP5 | CPU_IP6 | CPU_IP7)
#define MNSEC_PER_SEC (NSEC_PER_SEC >> 20)
/* KVM_IRQ_LINE irq field index values */
#define KVM_LOONGSON_IRQ_TYPE_SHIFT 24
#define KVM_LOONGSON_IRQ_TYPE_MASK 0xff
#define KVM_LOONGSON_IRQ_VCPU_SHIFT 16
#define KVM_LOONGSON_IRQ_VCPU_MASK 0xff
#define KVM_LOONGSON_IRQ_NUM_SHIFT 0
#define KVM_LOONGSON_IRQ_NUM_MASK 0xffff
typedef union loongarch_instruction larch_inst;
typedef int (*exit_handle_fn)(struct kvm_vcpu *, int);
int kvm_emu_mmio_read(struct kvm_vcpu *vcpu, larch_inst inst);
int kvm_emu_mmio_write(struct kvm_vcpu *vcpu, larch_inst inst);
int kvm_complete_mmio_read(struct kvm_vcpu *vcpu, struct kvm_run *run);
int kvm_complete_iocsr_read(struct kvm_vcpu *vcpu, struct kvm_run *run);
int kvm_complete_user_service(struct kvm_vcpu *vcpu, struct kvm_run *run);
int kvm_emu_idle(struct kvm_vcpu *vcpu);
int kvm_pending_timer(struct kvm_vcpu *vcpu);
int kvm_handle_fault(struct kvm_vcpu *vcpu, int fault);
void kvm_deliver_intr(struct kvm_vcpu *vcpu);
void kvm_deliver_exception(struct kvm_vcpu *vcpu);
void kvm_own_fpu(struct kvm_vcpu *vcpu);
void kvm_lose_fpu(struct kvm_vcpu *vcpu);
void kvm_save_fpu(struct loongarch_fpu *fpu);
void kvm_restore_fpu(struct loongarch_fpu *fpu);
void kvm_restore_fcsr(struct loongarch_fpu *fpu);
#ifdef CONFIG_CPU_HAS_LSX
int kvm_own_lsx(struct kvm_vcpu *vcpu);
void kvm_save_lsx(struct loongarch_fpu *fpu);
void kvm_restore_lsx(struct loongarch_fpu *fpu);
#else
static inline int kvm_own_lsx(struct kvm_vcpu *vcpu) { return -EINVAL; }
static inline void kvm_save_lsx(struct loongarch_fpu *fpu) { }
static inline void kvm_restore_lsx(struct loongarch_fpu *fpu) { }
#endif
#ifdef CONFIG_CPU_HAS_LASX
int kvm_own_lasx(struct kvm_vcpu *vcpu);
void kvm_save_lasx(struct loongarch_fpu *fpu);
void kvm_restore_lasx(struct loongarch_fpu *fpu);
#else
static inline int kvm_own_lasx(struct kvm_vcpu *vcpu) { return -EINVAL; }
static inline void kvm_save_lasx(struct loongarch_fpu *fpu) { }
static inline void kvm_restore_lasx(struct loongarch_fpu *fpu) { }
#endif
#ifdef CONFIG_CPU_HAS_LBT
int kvm_own_lbt(struct kvm_vcpu *vcpu);
#else
static inline int kvm_own_lbt(struct kvm_vcpu *vcpu) { return -EINVAL; }
#endif
void kvm_init_timer(struct kvm_vcpu *vcpu, unsigned long hz);
void kvm_save_timer(struct kvm_vcpu *vcpu);
void kvm_restore_timer(struct kvm_vcpu *vcpu);
int kvm_vcpu_ioctl_interrupt(struct kvm_vcpu *vcpu, struct kvm_interrupt *irq);
struct kvm_vcpu *kvm_get_vcpu_by_cpuid(struct kvm *kvm, int cpuid);
/*
* Loongarch KVM guest interrupt handling
*/
static inline void kvm_queue_irq(struct kvm_vcpu *vcpu, unsigned int irq)
{
set_bit(irq, &vcpu->arch.irq_pending);
clear_bit(irq, &vcpu->arch.irq_clear);
}
static inline void kvm_dequeue_irq(struct kvm_vcpu *vcpu, unsigned int irq)
{
clear_bit(irq, &vcpu->arch.irq_pending);
set_bit(irq, &vcpu->arch.irq_clear);
}
static inline int kvm_queue_exception(struct kvm_vcpu *vcpu,
unsigned int code, unsigned int subcode)
{
/* only one exception can be injected */
if (!vcpu->arch.exception_pending) {
set_bit(code, &vcpu->arch.exception_pending);
vcpu->arch.esubcode = subcode;
return 0;
} else
return -1;
}
static inline unsigned long kvm_read_reg(struct kvm_vcpu *vcpu, int num)
{
return vcpu->arch.gprs[num];
}
static inline void kvm_write_reg(struct kvm_vcpu *vcpu, int num, unsigned long val)
{
vcpu->arch.gprs[num] = val;
}
static inline bool kvm_pvtime_supported(void)
{
return !!sched_info_on();
}
static inline bool kvm_guest_has_pv_feature(struct kvm_vcpu *vcpu, unsigned int feature)
{
return vcpu->kvm->arch.pv_features & BIT(feature);
}
#endif /* __ASM_LOONGARCH_KVM_VCPU_H__ */
@@ -0,0 +1,113 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Author: Qi Hu <huqi@loongson.cn>
* Huacai Chen <chenhuacai@loongson.cn>
* Copyright (C) 2020-2023 Loongson Technology Corporation Limited
*/
#ifndef _ASM_LBT_H
#define _ASM_LBT_H
#include <asm/cpu.h>
#include <asm/current.h>
#include <asm/loongarch.h>
#include <asm/processor.h>
asmlinkage void _init_lbt(void);
asmlinkage void _save_lbt(struct loongarch_lbt *);
asmlinkage void _restore_lbt(struct loongarch_lbt *);
asmlinkage int _save_lbt_context(void __user *regs, void __user *eflags);
asmlinkage int _restore_lbt_context(void __user *regs, void __user *eflags);
asmlinkage int _save_ftop_context(void __user *ftop);
asmlinkage int _restore_ftop_context(void __user *ftop);
static inline int is_lbt_enabled(void)
{
if (!cpu_has_lbt)
return 0;
return (csr_read32(LOONGARCH_CSR_EUEN) & CSR_EUEN_LBTEN) ?
1 : 0;
}
static inline int is_lbt_owner(void)
{
return test_thread_flag(TIF_USEDLBT);
}
#ifdef CONFIG_CPU_HAS_LBT
static inline void enable_lbt(void)
{
if (cpu_has_lbt)
csr_xchg32(CSR_EUEN_LBTEN, CSR_EUEN_LBTEN, LOONGARCH_CSR_EUEN);
}
static inline void disable_lbt(void)
{
if (cpu_has_lbt)
csr_xchg32(0, CSR_EUEN_LBTEN, LOONGARCH_CSR_EUEN);
}
static inline void __own_lbt(void)
{
enable_lbt();
set_thread_flag(TIF_USEDLBT);
KSTK_EUEN(current) |= CSR_EUEN_LBTEN;
}
static inline void own_lbt_inatomic(int restore)
{
if (cpu_has_lbt && !is_lbt_owner()) {
__own_lbt();
if (restore)
_restore_lbt(&current->thread.lbt);
}
}
static inline void own_lbt(int restore)
{
preempt_disable();
own_lbt_inatomic(restore);
preempt_enable();
}
static inline void lose_lbt_inatomic(int save, struct task_struct *tsk)
{
if (cpu_has_lbt && is_lbt_owner()) {
if (save)
_save_lbt(&tsk->thread.lbt);
disable_lbt();
clear_tsk_thread_flag(tsk, TIF_USEDLBT);
}
KSTK_EUEN(tsk) &= ~(CSR_EUEN_LBTEN);
}
static inline void lose_lbt(int save)
{
preempt_disable();
lose_lbt_inatomic(save, current);
preempt_enable();
}
static inline void init_lbt(void)
{
__own_lbt();
_init_lbt();
}
#else
static inline void own_lbt_inatomic(int restore) {}
static inline void lose_lbt_inatomic(int save, struct task_struct *tsk) {}
static inline void init_lbt(void) {}
static inline void lose_lbt(int save) {}
#endif
static inline int thread_lbt_context_live(void)
{
if (!cpu_has_lbt)
return 0;
return test_thread_flag(TIF_LBT_CTX_LIVE);
}
#endif /* _ASM_LBT_H */
@@ -0,0 +1,80 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef __ASM_LINKAGE_H
#define __ASM_LINKAGE_H
#define __ALIGN .align 2
#define __ALIGN_STR __stringify(__ALIGN)
#define SYM_FUNC_START(name) \
SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN) \
.cfi_startproc;
#define SYM_FUNC_START_NOALIGN(name) \
SYM_START(name, SYM_L_GLOBAL, SYM_A_NONE) \
.cfi_startproc;
#define SYM_FUNC_START_LOCAL(name) \
SYM_START(name, SYM_L_LOCAL, SYM_A_ALIGN) \
.cfi_startproc;
#define SYM_FUNC_START_LOCAL_NOALIGN(name) \
SYM_START(name, SYM_L_LOCAL, SYM_A_NONE) \
.cfi_startproc;
#define SYM_FUNC_START_WEAK(name) \
SYM_START(name, SYM_L_WEAK, SYM_A_ALIGN) \
.cfi_startproc;
#define SYM_FUNC_START_WEAK_NOALIGN(name) \
SYM_START(name, SYM_L_WEAK, SYM_A_NONE) \
.cfi_startproc;
#define SYM_FUNC_END(name) \
.cfi_endproc; \
SYM_END(name, SYM_T_FUNC)
#define SYM_CODE_START(name) \
SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN) \
.cfi_startproc;
#define SYM_CODE_END(name) \
.cfi_endproc; \
SYM_END(name, SYM_T_NONE)
/*
* This is for the signal handler trampoline, which is used as the return
* address of the signal handlers in userspace instead of called normally.
* The long standing libgcc bug https://gcc.gnu.org/PR124050 requires a
* nop between .cfi_startproc and the actual address of the trampoline, so
* we cannot simply use SYM_FUNC_START.
*
* This wrapper also contains all the .cfi_* directives for recovering
* the content of the GPRs and the "return address" (where the rt_sigreturn
* syscall will jump to), assuming there is a struct rt_sigframe (where
* a struct sigcontext containing those information we need to recover) at
* $sp. The "DWARF for the LoongArch(TM) Architecture" manual states
* column 0 is for $zero, but it does not make too much sense to
* save/restore the hardware zero register. Repurpose this column here
* for the return address (here it's not the content of $ra we cannot use
* the default column 3).
*/
#define SYM_SIGFUNC_START(name) \
.cfi_startproc; \
.cfi_signal_frame; \
.cfi_def_cfa 3, RT_SIGFRAME_SC; \
.cfi_return_column 0; \
.cfi_offset 0, SC_PC; \
\
.irp num, 1, 2, 3, 4, 5, 6, 7, 8, \
9, 10, 11, 12, 13, 14, 15, 16, \
17, 18, 19, 20, 21, 22, 23, 24, \
25, 26, 27, 28, 29, 30, 31; \
.cfi_offset \num, SC_REGS + \num * 8; \
.endr; \
\
nop; \
SYM_START(name, SYM_L_GLOBAL, SYM_A_ALIGN)
#define SYM_SIGFUNC_END(name) SYM_FUNC_END(name)
#endif
@@ -0,0 +1,188 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ARCH_LOONGARCH_LOCAL_H
#define _ARCH_LOONGARCH_LOCAL_H
#include <linux/percpu.h>
#include <linux/bitops.h>
#include <linux/atomic.h>
#include <asm/asm.h>
#include <asm/cmpxchg.h>
typedef struct {
atomic_long_t a;
} local_t;
#define LOCAL_INIT(i) { ATOMIC_LONG_INIT(i) }
#define local_read(l) atomic_long_read(&(l)->a)
#define local_set(l, i) atomic_long_set(&(l)->a, (i))
#define local_add(i, l) atomic_long_add((i), (&(l)->a))
#define local_sub(i, l) atomic_long_sub((i), (&(l)->a))
#define local_inc(l) atomic_long_inc(&(l)->a)
#define local_dec(l) atomic_long_dec(&(l)->a)
/*
* Same as above, but return the result value
*/
#ifdef CONFIG_CPU_HAS_AMO
static inline long local_add_return(long i, local_t *l)
{
unsigned long result;
__asm__ __volatile__(
" " __AMADD " %1, %2, %0 \n"
: "+ZB" (l->a.counter), "=&r" (result)
: "r" (i)
: "memory");
result = result + i;
return result;
}
static inline long local_sub_return(long i, local_t *l)
{
unsigned long result;
__asm__ __volatile__(
" " __AMADD "%1, %2, %0 \n"
: "+ZB" (l->a.counter), "=&r" (result)
: "r" (-i)
: "memory");
result = result - i;
return result;
}
#else
static inline long local_add_return(long i, local_t *l)
{
unsigned long result, temp;
__asm__ __volatile__(
"1:" __LL "%1, %2 # local_add_return \n"
__stringify(LONG_ADD) " %0, %1, %3 \n"
__SC "%0, %2 \n"
" beq %0, $r0, 1b \n"
__stringify(LONG_ADD) " %0, %1, %3 \n"
: "=&r" (result), "=&r" (temp), "=ZC" (l->a.counter)
: "r" (i), "ZC" (l->a.counter)
: "memory");
return result;
}
static inline long local_sub_return(long i, local_t *l)
{
unsigned long result, temp;
__asm__ __volatile__(
"1:" __LL "%1, %2 # local_sub_return \n"
__stringify(LONG_SUB) " %0, %1, %3 \n"
__SC "%0, %2 \n"
" beq %0, $r0, 1b \n"
__stringify(LONG_SUB) " %0, %1, %3 \n"
: "=&r" (result), "=&r" (temp), "=ZC" (l->a.counter)
: "r" (i), "ZC" (l->a.counter)
: "memory");
return result;
}
#endif
static inline long local_cmpxchg(local_t *l, long old, long new)
{
return cmpxchg_local(&l->a.counter, old, new);
}
static inline bool local_try_cmpxchg(local_t *l, long *old, long new)
{
return try_cmpxchg_local(&l->a.counter,
(typeof(l->a.counter) *) old, new);
}
#define local_xchg(l, n) (atomic_long_xchg((&(l)->a), (n)))
/**
* local_add_unless - add unless the number is already a given value
* @l: pointer of type local_t
* @a: the amount to add to l...
* @u: ...unless l is equal to u.
*
* Atomically adds @a to @l, if @v was not already @u.
* Returns true if the addition was done.
*/
static inline bool
local_add_unless(local_t *l, long a, long u)
{
long c = local_read(l);
do {
if (unlikely(c == u))
return false;
} while (!local_try_cmpxchg(l, &c, c + a));
return true;
}
#define local_inc_not_zero(l) local_add_unless((l), 1, 0)
#define local_dec_return(l) local_sub_return(1, (l))
#define local_inc_return(l) local_add_return(1, (l))
/*
* local_sub_and_test - subtract value from variable and test result
* @i: integer value to subtract
* @l: pointer of type local_t
*
* Atomically subtracts @i from @l and returns
* true if the result is zero, or false for all
* other cases.
*/
#define local_sub_and_test(i, l) (local_sub_return((i), (l)) == 0)
/*
* local_inc_and_test - increment and test
* @l: pointer of type local_t
*
* Atomically increments @l by 1
* and returns true if the result is zero, or false for all
* other cases.
*/
#define local_inc_and_test(l) (local_inc_return(l) == 0)
/*
* local_dec_and_test - decrement by 1 and test
* @l: pointer of type local_t
*
* Atomically decrements @l by 1 and
* returns true if the result is 0, or false for all other
* cases.
*/
#define local_dec_and_test(l) (local_sub_return(1, (l)) == 0)
/*
* local_add_negative - add and test if negative
* @l: pointer of type local_t
* @i: integer value to add
*
* Atomically adds @i to @l and returns true
* if the result is negative, or false when
* result is greater than or equal to zero.
*/
#define local_add_negative(i, l) (local_add_return(i, (l)) < 0)
/* Use these for per-cpu local_t variables: on some archs they are
* much more efficient than these naive implementations. Note they take
* a variable, not an address.
*/
#define __local_inc(l) ((l)->a.counter++)
#define __local_dec(l) ((l)->a.counter++)
#define __local_add(i, l) ((l)->a.counter += (i))
#define __local_sub(i, l) ((l)->a.counter -= (i))
#endif /* _ARCH_LOONGARCH_LOCAL_H */
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,142 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Author: Huacai Chen <chenhuacai@loongson.cn>
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __ASM_LOONGSON_H
#define __ASM_LOONGSON_H
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/pci.h>
#include <asm/addrspace.h>
#include <asm/bootinfo.h>
#define LOONGSON_REG(x) \
(*(volatile u32 *)((char *)TO_UNCACHE(LOONGSON_REG_BASE) + (x)))
#define LOONGSON_LIO_BASE 0x18000000
#define LOONGSON_LIO_SIZE 0x00100000 /* 1M */
#define LOONGSON_LIO_TOP (LOONGSON_LIO_BASE+LOONGSON_LIO_SIZE-1)
#define LOONGSON_BOOT_BASE 0x1c000000
#define LOONGSON_BOOT_SIZE 0x02000000 /* 32M */
#define LOONGSON_BOOT_TOP (LOONGSON_BOOT_BASE+LOONGSON_BOOT_SIZE-1)
#define LOONGSON_REG_BASE 0x1fe00000
#define LOONGSON_REG_SIZE 0x00100000 /* 1M */
#define LOONGSON_REG_TOP (LOONGSON_REG_BASE+LOONGSON_REG_SIZE-1)
/* GPIO Regs - r/w */
#define LOONGSON_GPIODATA LOONGSON_REG(0x11c)
#define LOONGSON_GPIOIE LOONGSON_REG(0x120)
#define LOONGSON_REG_GPIO_BASE (LOONGSON_REG_BASE + 0x11c)
#define MAX_PACKAGES 16
#define xconf_readl(addr) readl(addr)
#define xconf_readq(addr) readq(addr)
static inline void xconf_writel(u32 val, volatile void __iomem *addr)
{
asm volatile (
" st.w %[v], %[hw], 0 \n"
" ld.b $zero, %[hw], 0 \n"
:
: [hw] "r" (addr), [v] "r" (val)
);
}
static inline void xconf_writeq(u64 val64, volatile void __iomem *addr)
{
asm volatile (
" st.d %[v], %[hw], 0 \n"
" ld.b $zero, %[hw], 0 \n"
:
: [hw] "r" (addr), [v] "r" (val64)
);
}
/* ============== LS7A registers =============== */
#define LS7A_PCH_REG_BASE 0x10000000UL
/* LPC regs */
#define LS7A_LPC_REG_BASE (LS7A_PCH_REG_BASE + 0x00002000)
/* CHIPCFG regs */
#define LS7A_CHIPCFG_REG_BASE (LS7A_PCH_REG_BASE + 0x00010000)
/* MISC reg base */
#define LS7A_MISC_REG_BASE (LS7A_PCH_REG_BASE + 0x00080000)
/* ACPI regs */
#define LS7A_ACPI_REG_BASE (LS7A_MISC_REG_BASE + 0x00050000)
/* RTC regs */
#define LS7A_RTC_REG_BASE (LS7A_MISC_REG_BASE + 0x00050100)
#define LS7A_DMA_CFG (volatile void *)TO_UNCACHE(LS7A_CHIPCFG_REG_BASE + 0x041c)
#define LS7A_DMA_NODE_SHF 8
#define LS7A_DMA_NODE_MASK 0x1F00
#define LS7A_INT_MASK_REG (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x020)
#define LS7A_INT_EDGE_REG (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x060)
#define LS7A_INT_CLEAR_REG (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x080)
#define LS7A_INT_HTMSI_EN_REG (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x040)
#define LS7A_INT_ROUTE_ENTRY_REG (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x100)
#define LS7A_INT_HTMSI_VEC_REG (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x200)
#define LS7A_INT_STATUS_REG (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x3a0)
#define LS7A_INT_POL_REG (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x3e0)
#define LS7A_LPC_INT_CTL (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x2000)
#define LS7A_LPC_INT_ENA (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x2004)
#define LS7A_LPC_INT_STS (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x2008)
#define LS7A_LPC_INT_CLR (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x200c)
#define LS7A_LPC_INT_POL (volatile void *)TO_UNCACHE(LS7A_PCH_REG_BASE + 0x2010)
#define LS7A_PMCON_SOC_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x000)
#define LS7A_PMCON_RESUME_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x004)
#define LS7A_PMCON_RTC_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x008)
#define LS7A_PM1_EVT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x00c)
#define LS7A_PM1_ENA_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x010)
#define LS7A_PM1_CNT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x014)
#define LS7A_PM1_TMR_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x018)
#define LS7A_P_CNT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x01c)
#define LS7A_GPE0_STS_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x028)
#define LS7A_GPE0_ENA_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x02c)
#define LS7A_RST_CNT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x030)
#define LS7A_WD_SET_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x034)
#define LS7A_WD_TIMER_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x038)
#define LS7A_THSENS_CNT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x04c)
#define LS7A_GEN_RTC_1_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x050)
#define LS7A_GEN_RTC_2_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x054)
#define LS7A_DPM_CFG_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x400)
#define LS7A_DPM_STS_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x404)
#define LS7A_DPM_CNT_REG (volatile void *)TO_UNCACHE(LS7A_ACPI_REG_BASE + 0x408)
typedef enum {
ACPI_PCI_HOTPLUG_STATUS = 1 << 1,
ACPI_CPU_HOTPLUG_STATUS = 1 << 2,
ACPI_MEM_HOTPLUG_STATUS = 1 << 3,
ACPI_POWERBUTTON_STATUS = 1 << 8,
ACPI_RTC_WAKE_STATUS = 1 << 10,
ACPI_PCI_WAKE_STATUS = 1 << 14,
ACPI_ANY_WAKE_STATUS = 1 << 15,
} AcpiEventStatusBits;
#define HT1LO_OFFSET 0xe0000000000UL
/* PCI Configuration Space Base */
#define MCFG_EXT_PCICFG_BASE 0xefe00000000UL
/* REG ACCESS*/
#define ls7a_readb(addr) (*(volatile unsigned char *)TO_UNCACHE(addr))
#define ls7a_readw(addr) (*(volatile unsigned short *)TO_UNCACHE(addr))
#define ls7a_readl(addr) (*(volatile unsigned int *)TO_UNCACHE(addr))
#define ls7a_readq(addr) (*(volatile unsigned long *)TO_UNCACHE(addr))
#define ls7a_writeb(val, addr) *(volatile unsigned char *)TO_UNCACHE(addr) = (val)
#define ls7a_writew(val, addr) *(volatile unsigned short *)TO_UNCACHE(addr) = (val)
#define ls7a_writel(val, addr) *(volatile unsigned int *)TO_UNCACHE(addr) = (val)
#define ls7a_writeq(val, addr) *(volatile unsigned long *)TO_UNCACHE(addr) = (val)
void enable_gpe_wakeup(void);
void enable_pci_wakeup(void);
#endif /* __ASM_LOONGSON_H */
@@ -0,0 +1,16 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __ASM_MMU_H
#define __ASM_MMU_H
#include <linux/atomic.h>
#include <linux/spinlock.h>
typedef struct {
u64 asid[NR_CPUS];
void *vdso;
} mm_context_t;
#endif /* __ASM_MMU_H */
@@ -0,0 +1,171 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Switch a MMU context.
*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_MMU_CONTEXT_H
#define _ASM_MMU_CONTEXT_H
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/mm_types.h>
#include <linux/smp.h>
#include <linux/slab.h>
#include <asm/cacheflush.h>
#include <asm/tlbflush.h>
#include <asm-generic/mm_hooks.h>
/*
* All unused by hardware upper bits will be considered
* as a software asid extension.
*/
static inline u64 asid_version_mask(unsigned int cpu)
{
return ~(u64)(cpu_asid_mask(&cpu_data[cpu]));
}
static inline u64 asid_first_version(unsigned int cpu)
{
return cpu_asid_mask(&cpu_data[cpu]) + 1;
}
#define cpu_context(cpu, mm) ((mm)->context.asid[cpu])
#define asid_cache(cpu) (cpu_data[cpu].asid_cache)
#define cpu_asid(cpu, mm) (cpu_context((cpu), (mm)) & cpu_asid_mask(&cpu_data[cpu]))
static inline int asid_valid(struct mm_struct *mm, unsigned int cpu)
{
if ((cpu_context(cpu, mm) ^ asid_cache(cpu)) & asid_version_mask(cpu))
return 0;
return 1;
}
static inline void enter_lazy_tlb(struct mm_struct *mm, struct task_struct *tsk)
{
}
/* Normal, classic get_new_mmu_context */
static inline void
get_new_mmu_context(struct mm_struct *mm, unsigned long cpu, bool *need_flush)
{
u64 asid = asid_cache(cpu);
if (!((++asid) & cpu_asid_mask(&cpu_data[cpu])))
*need_flush = true; /* start new asid cycle */
cpu_context(cpu, mm) = asid_cache(cpu) = asid;
}
/*
* Initialize the context related info for a new mm_struct
* instance.
*/
static inline int
init_new_context(struct task_struct *tsk, struct mm_struct *mm)
{
int i;
for_each_possible_cpu(i)
cpu_context(i, mm) = 0;
return 0;
}
static inline void atomic_update_pgd_asid(unsigned long asid, unsigned long pgdl)
{
__asm__ __volatile__(
"csrwr %[pgdl_val], %[pgdl_reg] \n\t"
"csrwr %[asid_val], %[asid_reg] \n\t"
: [asid_val] "+r" (asid), [pgdl_val] "+r" (pgdl)
: [asid_reg] "i" (LOONGARCH_CSR_ASID), [pgdl_reg] "i" (LOONGARCH_CSR_PGDL)
: "memory"
);
}
static inline void switch_mm_irqs_off(struct mm_struct *prev, struct mm_struct *next,
struct task_struct *tsk)
{
bool need_flush = false;
unsigned int cpu = smp_processor_id();
/* Check if our ASID is of an older version and thus invalid */
if (!asid_valid(next, cpu))
get_new_mmu_context(next, cpu, &need_flush);
if (next != &init_mm)
atomic_update_pgd_asid(cpu_asid(cpu, next), (unsigned long)next->pgd);
else
atomic_update_pgd_asid(cpu_asid(cpu, next), (unsigned long)invalid_pg_dir);
if (need_flush)
local_flush_tlb_user(); /* Flush tlb after update ASID */
/*
* Mark current->active_mm as not "active" anymore.
* We don't want to mislead possible IPI tlb flush routines.
*/
cpumask_set_cpu(cpu, mm_cpumask(next));
}
#define switch_mm_irqs_off switch_mm_irqs_off
static inline void switch_mm(struct mm_struct *prev, struct mm_struct *next,
struct task_struct *tsk)
{
unsigned long flags;
local_irq_save(flags);
switch_mm_irqs_off(prev, next, tsk);
local_irq_restore(flags);
}
/*
* Destroy context related info for an mm_struct that is about
* to be put to rest.
*/
static inline void destroy_context(struct mm_struct *mm)
{
}
#define activate_mm(prev, next) switch_mm(prev, next, current)
#define deactivate_mm(task, mm) do { } while (0)
/*
* If mm is currently active, we can't really drop it.
* Instead, we will get a new one for it.
*/
static inline void
drop_mmu_context(struct mm_struct *mm, unsigned int cpu)
{
int asid;
unsigned long flags;
local_irq_save(flags);
asid = read_csr_asid() & cpu_asid_mask(&current_cpu_data);
if (asid == cpu_asid(cpu, mm)) {
bool need_flush = false;
if (!current->mm || (current->mm == mm)) {
get_new_mmu_context(mm, cpu, &need_flush);
write_csr_asid(cpu_asid(cpu, mm));
if (need_flush)
local_flush_tlb_user(); /* Flush tlb after update ASID */
goto out;
}
}
/* Will get a new context next time */
cpu_context(cpu, mm) = 0;
cpumask_clear_cpu(cpu, mm_cpumask(mm));
out:
local_irq_restore(flags);
}
#endif /* _ASM_MMU_CONTEXT_H */
@@ -0,0 +1,126 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_MODULE_H
#define _ASM_MODULE_H
#include <asm/inst.h>
#include <asm/orc_types.h>
#include <asm-generic/module.h>
#define RELA_STACK_DEPTH 16
struct mod_section {
int shndx;
int num_entries;
int max_entries;
};
struct mod_arch_specific {
struct mod_section got;
struct mod_section plt;
struct mod_section plt_idx;
#ifdef CONFIG_UNWINDER_ORC
unsigned int num_orcs;
int *orc_unwind_ip;
struct orc_entry *orc_unwind;
#endif
/* For CONFIG_DYNAMIC_FTRACE */
struct plt_entry *ftrace_trampolines;
};
struct got_entry {
Elf_Addr symbol_addr;
};
struct plt_entry {
u32 inst_lu12iw;
#ifdef CONFIG_64BIT
u32 inst_lu32id;
u32 inst_lu52id;
#endif
u32 inst_jirl;
};
struct plt_idx_entry {
Elf_Addr symbol_addr;
};
Elf_Addr module_emit_got_entry(struct module *mod, Elf_Shdr *sechdrs, Elf_Addr val);
Elf_Addr module_emit_plt_entry(struct module *mod, Elf_Shdr *sechdrs, Elf_Addr val);
static inline struct got_entry emit_got_entry(Elf_Addr val)
{
return (struct got_entry) { val };
}
static inline struct plt_entry emit_plt_entry(unsigned long val)
{
#ifdef CONFIG_32BIT
u32 lu12iw, jirl;
lu12iw = larch_insn_gen_lu12iw(LOONGARCH_GPR_T1, ADDR_IMM(val, LU12IW));
jirl = larch_insn_gen_jirl(0, LOONGARCH_GPR_T1, ADDR_IMM(val, ORI));
return (struct plt_entry) { lu12iw, jirl };
#else
u32 lu12iw, lu32id, lu52id, jirl;
lu12iw = larch_insn_gen_lu12iw(LOONGARCH_GPR_T1, ADDR_IMM(val, LU12IW));
lu32id = larch_insn_gen_lu32id(LOONGARCH_GPR_T1, ADDR_IMM(val, LU32ID));
lu52id = larch_insn_gen_lu52id(LOONGARCH_GPR_T1, LOONGARCH_GPR_T1, ADDR_IMM(val, LU52ID));
jirl = larch_insn_gen_jirl(0, LOONGARCH_GPR_T1, ADDR_IMM(val, ORI));
return (struct plt_entry) { lu12iw, lu32id, lu52id, jirl };
#endif
}
static inline struct plt_idx_entry emit_plt_idx_entry(unsigned long val)
{
return (struct plt_idx_entry) { val };
}
static inline int get_plt_idx(unsigned long val, Elf_Shdr *sechdrs, const struct mod_section *sec)
{
int i;
struct plt_idx_entry *plt_idx = (struct plt_idx_entry *)sechdrs[sec->shndx].sh_addr;
for (i = 0; i < sec->num_entries; i++) {
if (plt_idx[i].symbol_addr == val)
return i;
}
return -1;
}
static inline struct plt_entry *get_plt_entry(unsigned long val,
Elf_Shdr *sechdrs,
const struct mod_section *sec_plt,
const struct mod_section *sec_plt_idx)
{
int plt_idx = get_plt_idx(val, sechdrs, sec_plt_idx);
struct plt_entry *plt = (struct plt_entry *)sechdrs[sec_plt->shndx].sh_addr;
if (plt_idx < 0)
return NULL;
return plt + plt_idx;
}
static inline struct got_entry *get_got_entry(Elf_Addr val,
Elf_Shdr *sechdrs,
const struct mod_section *sec)
{
int i;
struct got_entry *got = (struct got_entry *)sechdrs[sec->shndx].sh_addr;
for (i = 0; i < sec->num_entries; i++)
if (got[i].symbol_addr == val)
return &got[i];
return NULL;
}
#endif /* _ASM_MODULE_H */
@@ -0,0 +1,9 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* Copyright (C) 2020-2022 Loongson Technology Corporation Limited */
SECTIONS {
. = ALIGN(4);
.got 0 : { BYTE(0) }
.plt 0 : { BYTE(0) }
.plt.idx 0 : { BYTE(0) }
.ftrace_trampoline 0 : { BYTE(0) }
}
@@ -0,0 +1,54 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Author: Jianmin Lv <lvjianmin@loongson.cn>
* Huacai Chen <chenhuacai@loongson.cn>
*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_LOONGARCH_NUMA_H
#define _ASM_LOONGARCH_NUMA_H
#include <linux/nodemask.h>
#define NODE_ADDRSPACE_SHIFT 44
#define pa_to_nid(addr) (((addr) & 0xf00000000000) >> NODE_ADDRSPACE_SHIFT)
#define nid_to_addrbase(nid) (_ULCAST_(nid) << NODE_ADDRSPACE_SHIFT)
#ifdef CONFIG_NUMA
extern int numa_off;
extern s16 __cpuid_to_node[CONFIG_NR_CPUS];
extern nodemask_t numa_nodes_parsed __initdata;
extern void __init early_numa_add_cpu(int cpuid, s16 node);
extern void numa_add_cpu(unsigned int cpu);
extern void numa_remove_cpu(unsigned int cpu);
static inline void numa_clear_node(int cpu)
{
}
static inline void set_cpuid_to_node(int cpuid, s16 node)
{
__cpuid_to_node[cpuid] = node;
}
extern int early_cpu_to_node(int cpu);
#else
static inline void early_numa_add_cpu(int cpuid, s16 node) { }
static inline void numa_add_cpu(unsigned int cpu) { }
static inline void numa_remove_cpu(unsigned int cpu) { }
static inline void set_cpuid_to_node(int cpuid, s16 node) { }
static inline int early_cpu_to_node(int cpu)
{
return 0;
}
#endif /* CONFIG_NUMA */
#endif /* _ASM_LOONGARCH_NUMA_H */
@@ -0,0 +1,18 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _ORC_HEADER_H
#define _ORC_HEADER_H
#include <linux/types.h>
#include <linux/compiler.h>
#include <asm/orc_hash.h>
/*
* The header is currently a 20-byte hash of the ORC entry definition; see
* scripts/orc_hash.sh.
*/
#define ORC_HEADER \
__used __section(".orc_header") __aligned(4) \
static const u8 orc_header[] = { ORC_HASH }
#endif /* _ORC_HEADER_H */
@@ -0,0 +1,31 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _ORC_LOOKUP_H
#define _ORC_LOOKUP_H
/*
* This is a lookup table for speeding up access to the .orc_unwind table.
* Given an input address offset, the corresponding lookup table entry
* specifies a subset of the .orc_unwind table to search.
*
* Each block represents the end of the previous range and the start of the
* next range. An extra block is added to give the last range an end.
*
* The block size should be a power of 2 to avoid a costly 'div' instruction.
*
* A block size of 256 was chosen because it roughly doubles unwinder
* performance while only adding ~5% to the ORC data footprint.
*/
#define LOOKUP_BLOCK_ORDER 8
#define LOOKUP_BLOCK_SIZE (1 << LOOKUP_BLOCK_ORDER)
#ifndef LINKER_SCRIPT
extern unsigned int orc_lookup[];
extern unsigned int orc_lookup_end[];
#define LOOKUP_START_IP (unsigned long)_stext
#define LOOKUP_STOP_IP (unsigned long)_etext
#endif /* LINKER_SCRIPT */
#endif /* _ORC_LOOKUP_H */
@@ -0,0 +1,58 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _ORC_TYPES_H
#define _ORC_TYPES_H
#include <linux/types.h>
/*
* The ORC_REG_* registers are base registers which are used to find other
* registers on the stack.
*
* ORC_REG_PREV_SP, also known as DWARF Call Frame Address (CFA), is the
* address of the previous frame: the caller's SP before it called the current
* function.
*
* ORC_REG_UNDEFINED means the corresponding register's value didn't change in
* the current frame.
*
* The most commonly used base registers are SP and FP -- which the previous SP
* is usually based on -- and PREV_SP and UNDEFINED -- which the previous FP is
* usually based on.
*
* The rest of the base registers are needed for special cases like entry code
* and GCC realigned stacks.
*/
#define ORC_REG_UNDEFINED 0
#define ORC_REG_PREV_SP 1
#define ORC_REG_SP 2
#define ORC_REG_FP 3
#define ORC_REG_MAX 4
#define ORC_TYPE_UNDEFINED 0
#define ORC_TYPE_END_OF_STACK 1
#define ORC_TYPE_CALL 2
#define ORC_TYPE_REGS 3
#define ORC_TYPE_REGS_PARTIAL 4
#ifndef __ASSEMBLER__
/*
* This struct is more or less a vastly simplified version of the DWARF Call
* Frame Information standard. It contains only the necessary parts of DWARF
* CFI, simplified for ease of access by the in-kernel unwinder. It tells the
* unwinder how to find the previous SP and FP (and sometimes entry regs) on
* the stack for a given code address. Each instance of the struct corresponds
* to one or more code locations.
*/
struct orc_entry {
s16 sp_offset;
s16 fp_offset;
s16 ra_offset;
unsigned int sp_reg:4;
unsigned int fp_reg:4;
unsigned int ra_reg:4;
unsigned int type:3;
unsigned int signal:1;
};
#endif /* __ASSEMBLER__ */
#endif /* _ORC_TYPES_H */
@@ -0,0 +1,110 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_PAGE_H
#define _ASM_PAGE_H
#include <linux/const.h>
#include <asm/addrspace.h>
#include <vdso/page.h>
#define HPAGE_SHIFT (PAGE_SHIFT + PAGE_SHIFT - PTRLOG)
#define HPAGE_SIZE (_AC(1, UL) << HPAGE_SHIFT)
#define HPAGE_MASK (~(HPAGE_SIZE - 1))
#define HUGETLB_PAGE_ORDER (HPAGE_SHIFT - PAGE_SHIFT)
#ifndef __ASSEMBLER__
#include <linux/kernel.h>
#include <linux/pfn.h>
/*
* It's normally defined only for FLATMEM config but it's
* used in our early mem init code for all memory models.
* So always define it.
*/
#define ARCH_PFN_OFFSET PFN_UP(PHYS_OFFSET)
extern void clear_page(void *page);
extern void copy_page(void *to, void *from);
#define copy_user_page(to, from, vaddr, pg) copy_page(to, from)
extern unsigned long shm_align_mask;
struct page;
struct vm_area_struct;
typedef struct { unsigned long pte; } pte_t;
#define pte_val(x) ((x).pte)
#define __pte(x) ((pte_t) { (x) })
typedef struct page *pgtable_t;
typedef struct { unsigned long pgd; } pgd_t;
#define pgd_val(x) ((x).pgd)
#define __pgd(x) ((pgd_t) { (x) })
/*
* Manipulate page protection bits
*/
typedef struct { unsigned long pgprot; } pgprot_t;
#define pgprot_val(x) ((x).pgprot)
#define __pgprot(x) ((pgprot_t) { (x) })
#define pte_pgprot(x) __pgprot(pte_val(x) & ~_PFN_MASK)
#define ptep_buddy(x) ((pte_t *)((unsigned long)(x) ^ sizeof(pte_t)))
/*
* __pa()/__va() should be used only during mem init.
*/
#define __pa(x) PHYSADDR(x)
#define __va(x) ((void *)((unsigned long)(x) + PAGE_OFFSET - PHYS_OFFSET))
#define pfn_to_kaddr(pfn) __va((pfn) << PAGE_SHIFT)
#define sym_to_pfn(x) __phys_to_pfn(__pa_symbol(x))
struct page *dmw_virt_to_page(unsigned long kaddr);
struct page *tlb_virt_to_page(unsigned long kaddr);
#define pfn_to_phys(pfn) __pfn_to_phys(pfn)
#define phys_to_pfn(paddr) __phys_to_pfn(paddr)
#ifndef CONFIG_KFENCE
#define page_to_virt(page) __va(page_to_phys(page))
#define virt_to_page(kaddr) phys_to_page(__pa(kaddr))
#else
#define WANT_PAGE_VIRTUAL
#define page_to_virt(page) \
({ \
extern char *__kfence_pool; \
(__kfence_pool == NULL) ? __va(page_to_phys(page)) : page_address(page); \
})
#define virt_to_page(kaddr) \
({ \
(likely((unsigned long)kaddr < vm_map_base)) ? \
dmw_virt_to_page((unsigned long)kaddr) : tlb_virt_to_page((unsigned long)kaddr);\
})
#endif
#define pfn_to_virt(pfn) page_to_virt(pfn_to_page(pfn))
#define virt_to_pfn(kaddr) page_to_pfn(virt_to_page(kaddr))
extern int __virt_addr_valid(volatile void *kaddr);
#define virt_addr_valid(kaddr) __virt_addr_valid((volatile void *)(kaddr))
#define VMA_DATA_DEFAULT_FLAGS VMA_DATA_FLAGS_TSK_EXEC
#include <asm-generic/memory_model.h>
#include <asm-generic/getorder.h>
#endif /* !__ASSEMBLER__ */
#endif /* _ASM_PAGE_H */
@@ -0,0 +1,35 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef _ASM_LOONGARCH_PARAVIRT_H
#define _ASM_LOONGARCH_PARAVIRT_H
#ifdef CONFIG_PARAVIRT
#include <linux/jump_label.h>
DECLARE_STATIC_KEY_FALSE(virt_preempt_key);
DECLARE_STATIC_KEY_FALSE(virt_spin_lock_key);
DECLARE_PER_CPU(struct kvm_steal_time, steal_time);
int __init pv_ipi_init(void);
int __init pv_time_init(void);
int __init pv_spinlock_init(void);
#else
static inline int pv_ipi_init(void)
{
return 0;
}
static inline int pv_time_init(void)
{
return 0;
}
static inline int pv_spinlock_init(void)
{
return 0;
}
#endif // CONFIG_PARAVIRT
#endif
@@ -0,0 +1,25 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_PCI_H
#define _ASM_PCI_H
#include <linux/ioport.h>
#include <linux/list.h>
#include <linux/types.h>
#include <asm/io.h>
#define PCIBIOS_MIN_IO 0x4000
#define PCIBIOS_MIN_MEM 0x20000000
#define PCIBIOS_MIN_CARDBUS_IO 0x4000
#define HAVE_PCI_MMAP
#define pcibios_assign_all_busses() 0
extern phys_addr_t mcfg_addr_init(int node);
/* generic pci stuff */
#include <asm-generic/pci.h>
#endif /* _ASM_PCI_H */
@@ -0,0 +1,183 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __ASM_PERCPU_H
#define __ASM_PERCPU_H
#include <asm/loongarch.h>
/*
* The "address" (in fact, offset from $r21) of a per-CPU variable is close to
* the loading address of main kernel image, but far from where the modules are
* loaded. Tell the compiler this fact when using explicit relocs.
*/
#if defined(MODULE) && defined(CONFIG_AS_HAS_EXPLICIT_RELOCS) && defined(CONFIG_64BIT)
# if __has_attribute(model)
# define PER_CPU_ATTRIBUTES __attribute__((model("extreme")))
# else
# error compiler support for the model attribute is necessary when a recent assembler is used
# endif
#endif
/* Use r21 for fast access */
register unsigned long __my_cpu_offset __asm__("$r21");
static inline void set_my_cpu_offset(unsigned long off)
{
__my_cpu_offset = off;
csr_write(off, PERCPU_BASE_KS);
}
#define __my_cpu_offset \
({ \
__asm__ __volatile__("":"+r"(__my_cpu_offset)); \
__my_cpu_offset; \
})
#ifdef CONFIG_CPU_HAS_AMO
#define PERCPU_OP(op, asm_op, c_op) \
static __always_inline unsigned long __percpu_##op(void *ptr, \
unsigned long val, int size) \
{ \
unsigned long ret; \
\
switch (size) { \
case 4: \
__asm__ __volatile__( \
"am"#asm_op".w" " %[ret], %[val], %[ptr] \n" \
: [ret] "=&r" (ret), [ptr] "+ZB"(*(u32 *)ptr) \
: [val] "r" (val)); \
break; \
case 8: \
__asm__ __volatile__( \
"am"#asm_op".d" " %[ret], %[val], %[ptr] \n" \
: [ret] "=&r" (ret), [ptr] "+ZB"(*(u64 *)ptr) \
: [val] "r" (val)); \
break; \
default: \
ret = 0; \
BUILD_BUG(); \
} \
\
return ret c_op val; \
}
PERCPU_OP(add, add, +)
PERCPU_OP(and, and, &)
PERCPU_OP(or, or, |)
#undef PERCPU_OP
#endif
#ifdef CONFIG_64BIT
#define __pcpu_op_1(op) op ".b "
#define __pcpu_op_2(op) op ".h "
#define __pcpu_op_4(op) op ".w "
#define __pcpu_op_8(op) op ".d "
#define _percpu_read(size, _pcp) \
({ \
typeof(_pcp) __pcp_ret; \
\
__asm__ __volatile__( \
__pcpu_op_##size("ldx") "%[ret], $r21, %[ptr] \n" \
: [ret] "=&r"(__pcp_ret) \
: [ptr] "r"(&(_pcp)) \
: "memory"); \
\
__pcp_ret; \
})
#define _percpu_write(size, _pcp, _val) \
do { \
__asm__ __volatile__( \
__pcpu_op_##size("stx") "%[val], $r21, %[ptr] \n" \
: \
: [val] "r"(_val), [ptr] "r"(&(_pcp)) \
: "memory"); \
} while (0)
#endif
#define __percpu_xchg __arch_xchg
/* this_cpu_cmpxchg */
#define _protect_cmpxchg_local(pcp, o, n) \
({ \
typeof(*raw_cpu_ptr(&(pcp))) __ret; \
preempt_disable_notrace(); \
__ret = cmpxchg_local(raw_cpu_ptr(&(pcp)), o, n); \
preempt_enable_notrace(); \
__ret; \
})
#define _pcp_protect(operation, pcp, val) \
({ \
typeof(pcp) __retval; \
preempt_disable_notrace(); \
__retval = (typeof(pcp))operation(raw_cpu_ptr(&(pcp)), \
(val), sizeof(pcp)); \
preempt_enable_notrace(); \
__retval; \
})
#ifdef CONFIG_CPU_HAS_AMO
#define _percpu_add(pcp, val) \
_pcp_protect(__percpu_add, pcp, val)
#define _percpu_add_return(pcp, val) _percpu_add(pcp, val)
#define _percpu_and(pcp, val) \
_pcp_protect(__percpu_and, pcp, val)
#define _percpu_or(pcp, val) \
_pcp_protect(__percpu_or, pcp, val)
#define this_cpu_add_4(pcp, val) _percpu_add(pcp, val)
#define this_cpu_add_8(pcp, val) _percpu_add(pcp, val)
#define this_cpu_add_return_4(pcp, val) _percpu_add_return(pcp, val)
#define this_cpu_add_return_8(pcp, val) _percpu_add_return(pcp, val)
#define this_cpu_and_4(pcp, val) _percpu_and(pcp, val)
#define this_cpu_and_8(pcp, val) _percpu_and(pcp, val)
#define this_cpu_or_4(pcp, val) _percpu_or(pcp, val)
#define this_cpu_or_8(pcp, val) _percpu_or(pcp, val)
#endif
#ifdef CONFIG_64BIT
#define this_cpu_read_1(pcp) _percpu_read(1, pcp)
#define this_cpu_read_2(pcp) _percpu_read(2, pcp)
#define this_cpu_read_4(pcp) _percpu_read(4, pcp)
#define this_cpu_read_8(pcp) _percpu_read(8, pcp)
#define this_cpu_write_1(pcp, val) _percpu_write(1, pcp, val)
#define this_cpu_write_2(pcp, val) _percpu_write(2, pcp, val)
#define this_cpu_write_4(pcp, val) _percpu_write(4, pcp, val)
#define this_cpu_write_8(pcp, val) _percpu_write(8, pcp, val)
#endif
#define _percpu_xchg(pcp, val) ((typeof(pcp)) \
_pcp_protect(__percpu_xchg, pcp, (unsigned long)(val)))
#define this_cpu_xchg_1(pcp, val) _percpu_xchg(pcp, val)
#define this_cpu_xchg_2(pcp, val) _percpu_xchg(pcp, val)
#define this_cpu_xchg_4(pcp, val) _percpu_xchg(pcp, val)
#define this_cpu_xchg_8(pcp, val) _percpu_xchg(pcp, val)
#define this_cpu_cmpxchg_1(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
#define this_cpu_cmpxchg_2(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
#define this_cpu_cmpxchg_4(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
#define this_cpu_cmpxchg_8(ptr, o, n) _protect_cmpxchg_local(ptr, o, n)
#include <asm-generic/percpu.h>
#endif /* __ASM_PERCPU_H */
@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Author: Huacai Chen <chenhuacai@loongson.cn>
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __LOONGARCH_PERF_EVENT_H__
#define __LOONGARCH_PERF_EVENT_H__
#include <asm/ptrace.h>
#define perf_arch_bpf_user_pt_regs(regs) (struct user_pt_regs *)regs
#define perf_arch_fetch_caller_regs(regs, __ip) { \
(regs)->csr_era = (__ip); \
(regs)->regs[3] = (unsigned long) __builtin_frame_address(0); \
}
#endif /* __LOONGARCH_PERF_EVENT_H__ */
@@ -0,0 +1,106 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_PGALLOC_H
#define _ASM_PGALLOC_H
#include <linux/mm.h>
#include <linux/sched.h>
#define __HAVE_ARCH_PMD_ALLOC_ONE
#define __HAVE_ARCH_PUD_ALLOC_ONE
#define __HAVE_ARCH_PTE_ALLOC_ONE_KERNEL
#include <asm-generic/pgalloc.h>
static inline void pmd_populate_kernel(struct mm_struct *mm,
pmd_t *pmd, pte_t *pte)
{
set_pmd(pmd, __pmd((unsigned long)pte));
}
static inline void pmd_populate(struct mm_struct *mm, pmd_t *pmd, pgtable_t pte)
{
set_pmd(pmd, __pmd((unsigned long)page_address(pte)));
}
#ifndef __PAGETABLE_PMD_FOLDED
static inline void pud_populate(struct mm_struct *mm, pud_t *pud, pmd_t *pmd)
{
set_pud(pud, __pud((unsigned long)pmd));
}
#endif
#ifndef __PAGETABLE_PUD_FOLDED
static inline void p4d_populate(struct mm_struct *mm, p4d_t *p4d, pud_t *pud)
{
set_p4d(p4d, __p4d((unsigned long)pud));
}
#endif /* __PAGETABLE_PUD_FOLDED */
extern void pagetable_init(void);
extern pgd_t *pgd_alloc(struct mm_struct *mm);
static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm)
{
pte_t *pte = __pte_alloc_one_kernel(mm);
if (pte)
kernel_pte_init(pte);
return pte;
}
#define __pte_free_tlb(tlb, pte, address) tlb_remove_ptdesc((tlb), page_ptdesc(pte))
#ifndef __PAGETABLE_PMD_FOLDED
static inline pmd_t *pmd_alloc_one(struct mm_struct *mm, unsigned long address)
{
pmd_t *pmd;
struct ptdesc *ptdesc;
ptdesc = pagetable_alloc(GFP_KERNEL_ACCOUNT, 0);
if (!ptdesc)
return NULL;
if (!pagetable_pmd_ctor(mm, ptdesc)) {
pagetable_free(ptdesc);
return NULL;
}
pmd = ptdesc_address(ptdesc);
pmd_init(pmd);
return pmd;
}
#define __pmd_free_tlb(tlb, x, addr) tlb_remove_ptdesc((tlb), virt_to_ptdesc(x))
#endif
#ifndef __PAGETABLE_PUD_FOLDED
static inline pud_t *pud_alloc_one(struct mm_struct *mm, unsigned long address)
{
pud_t *pud;
struct ptdesc *ptdesc = pagetable_alloc(GFP_KERNEL, 0);
if (!ptdesc)
return NULL;
pagetable_pud_ctor(ptdesc);
pud = ptdesc_address(ptdesc);
pud_init(pud);
return pud;
}
#define __pud_free_tlb(tlb, x, addr) tlb_remove_ptdesc((tlb), virt_to_ptdesc(x))
#endif /* __PAGETABLE_PUD_FOLDED */
extern pte_t * __init populate_kernel_pte(unsigned long addr);
#endif /* _ASM_PGALLOC_H */
@@ -0,0 +1,162 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef _ASM_PGTABLE_BITS_H
#define _ASM_PGTABLE_BITS_H
/* Page table bits */
#ifdef CONFIG_32BIT
#define _PAGE_VALID_SHIFT 0
#define _PAGE_ACCESSED_SHIFT 0 /* Reuse Valid for Accessed */
#define _PAGE_DIRTY_SHIFT 1
#define _PAGE_PLV_SHIFT 2 /* 2~3, two bits */
#define _CACHE_SHIFT 4 /* 4~5, two bits */
#define _PAGE_GLOBAL_SHIFT 6
#define _PAGE_HUGE_SHIFT 6 /* HUGE is a PMD bit */
#define _PAGE_PRESENT_SHIFT 7
#define _PAGE_PFN_SHIFT 8
#define _PAGE_HGLOBAL_SHIFT 12 /* HGlobal is a PMD bit */
#define _PAGE_SWP_EXCLUSIVE_SHIFT 13
#define _PAGE_PFN_END_SHIFT 28
#define _PAGE_WRITE_SHIFT 29
#define _PAGE_MODIFIED_SHIFT 30
#define _PAGE_PRESENT_INVALID_SHIFT 31
#endif
#ifdef CONFIG_64BIT
#define _PAGE_VALID_SHIFT 0
#define _PAGE_ACCESSED_SHIFT 0 /* Reuse Valid for Accessed */
#define _PAGE_DIRTY_SHIFT 1
#define _PAGE_PLV_SHIFT 2 /* 2~3, two bits */
#define _CACHE_SHIFT 4 /* 4~5, two bits */
#define _PAGE_GLOBAL_SHIFT 6
#define _PAGE_HUGE_SHIFT 6 /* HUGE is a PMD bit */
#define _PAGE_PRESENT_SHIFT 7
#define _PAGE_WRITE_SHIFT 8
#define _PAGE_MODIFIED_SHIFT 9
#define _PAGE_PROTNONE_SHIFT 10
#define _PAGE_SPECIAL_SHIFT 11
#define _PAGE_PFN_SHIFT 12
#define _PAGE_HGLOBAL_SHIFT 12 /* HGlobal is a PMD bit */
#define _PAGE_SWP_EXCLUSIVE_SHIFT 23
#define _PAGE_PFN_END_SHIFT 48
#define _PAGE_PRESENT_INVALID_SHIFT 60
#define _PAGE_NO_READ_SHIFT 61
#define _PAGE_NO_EXEC_SHIFT 62
#define _PAGE_RPLV_SHIFT 63
#endif
/* Used by software */
#define _PAGE_PRESENT (_ULCAST_(1) << _PAGE_PRESENT_SHIFT)
#define _PAGE_PRESENT_INVALID (_ULCAST_(1) << _PAGE_PRESENT_INVALID_SHIFT)
#define _PAGE_WRITE (_ULCAST_(1) << _PAGE_WRITE_SHIFT)
#define _PAGE_ACCESSED (_ULCAST_(1) << _PAGE_ACCESSED_SHIFT)
#define _PAGE_MODIFIED (_ULCAST_(1) << _PAGE_MODIFIED_SHIFT)
#ifdef CONFIG_32BIT
#define _PAGE_PROTNONE 0
#define _PAGE_SPECIAL 0
#else
#define _PAGE_PROTNONE (_ULCAST_(1) << _PAGE_PROTNONE_SHIFT)
#define _PAGE_SPECIAL (_ULCAST_(1) << _PAGE_SPECIAL_SHIFT)
#endif
/* We borrow bit 13/23 to store the exclusive marker in swap PTEs. */
#define _PAGE_SWP_EXCLUSIVE (_ULCAST_(1) << _PAGE_SWP_EXCLUSIVE_SHIFT)
/* Used by TLB hardware (placed in EntryLo*) */
#define _PAGE_VALID (_ULCAST_(1) << _PAGE_VALID_SHIFT)
#define _PAGE_DIRTY (_ULCAST_(1) << _PAGE_DIRTY_SHIFT)
#define _PAGE_PLV (_ULCAST_(3) << _PAGE_PLV_SHIFT)
#define _PAGE_GLOBAL (_ULCAST_(1) << _PAGE_GLOBAL_SHIFT)
#define _PAGE_HUGE (_ULCAST_(1) << _PAGE_HUGE_SHIFT)
#define _PAGE_HGLOBAL (_ULCAST_(1) << _PAGE_HGLOBAL_SHIFT)
#ifdef CONFIG_32BIT
#define _PAGE_NO_READ 0
#define _PAGE_NO_EXEC 0
#define _PAGE_RPLV 0
#else
#define _PAGE_NO_READ (_ULCAST_(1) << _PAGE_NO_READ_SHIFT)
#define _PAGE_NO_EXEC (_ULCAST_(1) << _PAGE_NO_EXEC_SHIFT)
#define _PAGE_RPLV (_ULCAST_(1) << _PAGE_RPLV_SHIFT)
#endif
#define _CACHE_MASK (_ULCAST_(3) << _CACHE_SHIFT)
#define PFN_PTE_SHIFT (PAGE_SHIFT - 12 + _PAGE_PFN_SHIFT)
#define _PAGE_USER (PLV_USER << _PAGE_PLV_SHIFT)
#define _PAGE_KERN (PLV_KERN << _PAGE_PLV_SHIFT)
#define _PFN_MASK (~((_ULCAST_(1) << (PFN_PTE_SHIFT)) - 1) & \
((_ULCAST_(1) << (_PAGE_PFN_END_SHIFT)) - 1))
/*
* Cache attributes
*/
#ifndef _CACHE_SUC
#define _CACHE_SUC (0<<_CACHE_SHIFT) /* Strong-ordered UnCached */
#endif
#ifndef _CACHE_CC
#define _CACHE_CC (1<<_CACHE_SHIFT) /* Coherent Cached */
#endif
#ifndef _CACHE_WUC
#define _CACHE_WUC (2<<_CACHE_SHIFT) /* Weak-ordered UnCached */
#endif
#define __READABLE (_PAGE_VALID)
#define __WRITEABLE (_PAGE_DIRTY | _PAGE_WRITE)
#define _PAGE_CHG_MASK (_PAGE_MODIFIED | _PAGE_SPECIAL | _PFN_MASK | _CACHE_MASK | _PAGE_PLV)
#define _HPAGE_CHG_MASK (_PAGE_MODIFIED | _PAGE_SPECIAL | _PFN_MASK | _CACHE_MASK | _PAGE_PLV | _PAGE_HUGE)
#define PAGE_NONE __pgprot(_PAGE_PROTNONE | _PAGE_NO_READ | \
_PAGE_USER | _CACHE_CC)
#define PAGE_SHARED __pgprot(_PAGE_PRESENT | _PAGE_WRITE | \
_PAGE_USER | _CACHE_CC)
#define PAGE_READONLY __pgprot(_PAGE_PRESENT | _PAGE_USER | _CACHE_CC)
#define PAGE_KERNEL __pgprot(_PAGE_PRESENT | __READABLE | __WRITEABLE | \
_PAGE_GLOBAL | _PAGE_KERN | _CACHE_CC)
#define PAGE_KERNEL_SUC __pgprot(_PAGE_PRESENT | __READABLE | __WRITEABLE | \
_PAGE_GLOBAL | _PAGE_KERN | _CACHE_SUC)
#define PAGE_KERNEL_WUC __pgprot(_PAGE_PRESENT | __READABLE | __WRITEABLE | \
_PAGE_GLOBAL | _PAGE_KERN | _CACHE_WUC)
#ifndef __ASSEMBLER__
#define _PAGE_IOREMAP pgprot_val(PAGE_KERNEL_SUC)
#define pgprot_nx pgprot_nx
static inline pgprot_t pgprot_nx(pgprot_t _prot)
{
return __pgprot(pgprot_val(_prot) | _PAGE_NO_EXEC);
}
#define pgprot_noncached pgprot_noncached
static inline pgprot_t pgprot_noncached(pgprot_t _prot)
{
unsigned long prot = pgprot_val(_prot);
prot = (prot & ~_CACHE_MASK) | _CACHE_SUC;
return __pgprot(prot);
}
extern bool wc_enabled;
#define pgprot_writecombine pgprot_writecombine
static inline pgprot_t pgprot_writecombine(pgprot_t _prot)
{
unsigned long prot = pgprot_val(_prot);
prot = (prot & ~_CACHE_MASK) | (wc_enabled ? _CACHE_WUC : _CACHE_SUC);
return __pgprot(prot);
}
#endif /* !__ASSEMBLER__ */
#endif /* _ASM_PGTABLE_BITS_H */
@@ -0,0 +1,650 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*
* Derived from MIPS:
* Copyright (C) 1994, 95, 96, 97, 98, 99, 2000, 2003 Ralf Baechle
* Copyright (C) 1999, 2000, 2001 Silicon Graphics, Inc.
*/
#ifndef _ASM_PGTABLE_H
#define _ASM_PGTABLE_H
#include <linux/compiler.h>
#include <asm/addrspace.h>
#include <asm/asm.h>
#include <asm/page.h>
#include <asm/pgtable-bits.h>
#if CONFIG_PGTABLE_LEVELS == 2
#include <asm-generic/pgtable-nopmd.h>
#elif CONFIG_PGTABLE_LEVELS == 3
#include <asm-generic/pgtable-nopud.h>
#else
#include <asm-generic/pgtable-nop4d.h>
#endif
#ifdef CONFIG_HIGHMEM
#include <asm/highmem.h>
#endif
#if CONFIG_PGTABLE_LEVELS == 2
#define PGDIR_SHIFT (PAGE_SHIFT + (PAGE_SHIFT - PTRLOG))
#elif CONFIG_PGTABLE_LEVELS == 3
#define PMD_SHIFT (PAGE_SHIFT + (PAGE_SHIFT - PTRLOG))
#define PMD_SIZE (1UL << PMD_SHIFT)
#define PMD_MASK (~(PMD_SIZE-1))
#define PGDIR_SHIFT (PMD_SHIFT + (PAGE_SHIFT - PTRLOG))
#elif CONFIG_PGTABLE_LEVELS == 4
#define PMD_SHIFT (PAGE_SHIFT + (PAGE_SHIFT - PTRLOG))
#define PMD_SIZE (1UL << PMD_SHIFT)
#define PMD_MASK (~(PMD_SIZE-1))
#define PUD_SHIFT (PMD_SHIFT + (PAGE_SHIFT - PTRLOG))
#define PUD_SIZE (1UL << PUD_SHIFT)
#define PUD_MASK (~(PUD_SIZE-1))
#define PGDIR_SHIFT (PUD_SHIFT + (PAGE_SHIFT - PTRLOG))
#endif
#define PGDIR_SIZE (1UL << PGDIR_SHIFT)
#define PGDIR_MASK (~(PGDIR_SIZE-1))
#ifdef CONFIG_32BIT
#define VA_BITS 32
#else
#define VA_BITS (PGDIR_SHIFT + (PAGE_SHIFT - PTRLOG))
#endif
#define PTRS_PER_PGD (PAGE_SIZE >> PTRLOG)
#if CONFIG_PGTABLE_LEVELS > 3
#define PTRS_PER_PUD (PAGE_SIZE >> PTRLOG)
#endif
#if CONFIG_PGTABLE_LEVELS > 2
#define PTRS_PER_PMD (PAGE_SIZE >> PTRLOG)
#endif
#define PTRS_PER_PTE (PAGE_SIZE >> PTRLOG)
#ifdef CONFIG_32BIT
#define USER_PTRS_PER_PGD (TASK_SIZE / PGDIR_SIZE)
#else
#define USER_PTRS_PER_PGD ((TASK_SIZE64 / PGDIR_SIZE)?(TASK_SIZE64 / PGDIR_SIZE):1)
#endif
#ifndef __ASSEMBLER__
#include <linux/mm_types.h>
#include <linux/mmzone.h>
#include <asm/fixmap.h>
#include <asm/sparsemem.h>
struct mm_struct;
struct vm_area_struct;
#ifdef CONFIG_32BIT
#define VMALLOC_START (vm_map_base + PCI_IOSIZE + (2 * PAGE_SIZE))
#ifdef CONFIG_HIGHMEM
#define VMALLOC_END (PKMAP_BASE - (2 * PAGE_SIZE))
#else
#define VMALLOC_END (FIXADDR_START - (2 * PAGE_SIZE))
#endif
#define PKMAP_BASE (PKMAP_END - (PAGE_SIZE * LAST_PKMAP))
#define PKMAP_END ((FIXADDR_START) & ~((LAST_PKMAP << PAGE_SHIFT)-1))
#endif
#ifdef CONFIG_64BIT
#define MODULES_VADDR (vm_map_base + PCI_IOSIZE + (2 * PAGE_SIZE))
#define MODULES_END (MODULES_VADDR + SZ_256M)
#ifdef CONFIG_KFENCE
#define KFENCE_AREA_SIZE (((CONFIG_KFENCE_NUM_OBJECTS + 1) * 2 + 2) * PAGE_SIZE)
#else
#define KFENCE_AREA_SIZE 0
#endif
#define VMALLOC_START MODULES_END
#ifndef CONFIG_KASAN
#define VMALLOC_END \
(vm_map_base + \
min(PTRS_PER_PGD * PTRS_PER_PUD * PTRS_PER_PMD * PTRS_PER_PTE * PAGE_SIZE, (1UL << cpu_vabits)) - PMD_SIZE - VMEMMAP_SIZE - KFENCE_AREA_SIZE)
#else
#define VMALLOC_END \
(vm_map_base + \
min(PTRS_PER_PGD * PTRS_PER_PUD * PTRS_PER_PMD * PTRS_PER_PTE * PAGE_SIZE, (1UL << cpu_vabits) / 2) - PMD_SIZE - VMEMMAP_SIZE - KFENCE_AREA_SIZE)
#endif
#define VMEMMAP_ALIGN max(PMD_SIZE, MAX_FOLIO_VMEMMAP_ALIGN)
#define vmemmap ((struct page *)(ALIGN(VMALLOC_END, VMEMMAP_ALIGN)))
#define VMEMMAP_END ((unsigned long)vmemmap + VMEMMAP_SIZE - 1)
#define KFENCE_AREA_START (VMEMMAP_END + 1)
#define KFENCE_AREA_END (KFENCE_AREA_START + KFENCE_AREA_SIZE - 1)
#endif
#define ptep_get(ptep) READ_ONCE(*(ptep))
#define pmdp_get(pmdp) READ_ONCE(*(pmdp))
#define pte_ERROR(e) \
pr_err("%s:%d: bad pte %016lx.\n", __FILE__, __LINE__, pte_val(e))
#ifndef __PAGETABLE_PMD_FOLDED
#define pmd_ERROR(e) \
pr_err("%s:%d: bad pmd %016lx.\n", __FILE__, __LINE__, pmd_val(e))
#endif
#ifndef __PAGETABLE_PUD_FOLDED
#define pud_ERROR(e) \
pr_err("%s:%d: bad pud %016lx.\n", __FILE__, __LINE__, pud_val(e))
#endif
#define pgd_ERROR(e) \
pr_err("%s:%d: bad pgd %016lx.\n", __FILE__, __LINE__, pgd_val(e))
extern pte_t invalid_pte_table[PTRS_PER_PTE];
#ifndef __PAGETABLE_PUD_FOLDED
typedef struct { unsigned long pud; } pud_t;
#define pud_val(x) ((x).pud)
#define __pud(x) ((pud_t) { (x) })
extern pud_t invalid_pud_table[PTRS_PER_PUD];
/*
* Empty pgd/p4d entries point to the invalid_pud_table.
*/
static inline int p4d_none(p4d_t p4d)
{
return p4d_val(p4d) == (unsigned long)invalid_pud_table;
}
static inline int p4d_bad(p4d_t p4d)
{
return p4d_val(p4d) & ~PAGE_MASK;
}
static inline int p4d_present(p4d_t p4d)
{
return p4d_val(p4d) != (unsigned long)invalid_pud_table;
}
static inline pud_t *p4d_pgtable(p4d_t p4d)
{
return (pud_t *)p4d_val(p4d);
}
static inline void set_p4d(p4d_t *p4d, p4d_t p4dval)
{
WRITE_ONCE(*p4d, p4dval);
}
static inline void p4d_clear(p4d_t *p4dp)
{
set_p4d(p4dp, __p4d((unsigned long)invalid_pud_table));
}
#define p4d_phys(p4d) PHYSADDR(p4d_val(p4d))
#define p4d_page(p4d) (pfn_to_page(p4d_phys(p4d) >> PAGE_SHIFT))
#endif
#ifndef __PAGETABLE_PMD_FOLDED
typedef struct { unsigned long pmd; } pmd_t;
#define pmd_val(x) ((x).pmd)
#define __pmd(x) ((pmd_t) { (x) })
extern pmd_t invalid_pmd_table[PTRS_PER_PMD];
/*
* Empty pud entries point to the invalid_pmd_table.
*/
static inline int pud_none(pud_t pud)
{
return pud_val(pud) == (unsigned long)invalid_pmd_table;
}
static inline int pud_bad(pud_t pud)
{
return pud_val(pud) & ~PAGE_MASK;
}
static inline int pud_present(pud_t pud)
{
return pud_val(pud) != (unsigned long)invalid_pmd_table;
}
static inline pmd_t *pud_pgtable(pud_t pud)
{
return (pmd_t *)pud_val(pud);
}
static inline void set_pud(pud_t *pud, pud_t pudval)
{
WRITE_ONCE(*pud, pudval);
}
static inline void pud_clear(pud_t *pudp)
{
set_pud(pudp, __pud((unsigned long)invalid_pmd_table));
}
#define pud_phys(pud) PHYSADDR(pud_val(pud))
#define pud_page(pud) (pfn_to_page(pud_phys(pud) >> PAGE_SHIFT))
#endif
/*
* Empty pmd entries point to the invalid_pte_table.
*/
static inline int pmd_none(pmd_t pmd)
{
return pmd_val(pmd) == (unsigned long)invalid_pte_table;
}
static inline int pmd_bad(pmd_t pmd)
{
return (pmd_val(pmd) & ~PAGE_MASK);
}
static inline int pmd_present(pmd_t pmd)
{
if (unlikely(pmd_val(pmd) & _PAGE_HUGE))
return !!(pmd_val(pmd) & (_PAGE_PRESENT | _PAGE_PROTNONE | _PAGE_PRESENT_INVALID));
return pmd_val(pmd) != (unsigned long)invalid_pte_table;
}
static inline void set_pmd(pmd_t *pmd, pmd_t pmdval)
{
WRITE_ONCE(*pmd, pmdval);
}
static inline void pmd_clear(pmd_t *pmdp)
{
set_pmd(pmdp, __pmd((unsigned long)invalid_pte_table));
}
#define pmd_phys(pmd) PHYSADDR(pmd_val(pmd))
#ifndef CONFIG_TRANSPARENT_HUGEPAGE
#define pmd_page(pmd) (pfn_to_page(pmd_phys(pmd) >> PAGE_SHIFT))
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
#define pmd_page_vaddr(pmd) pmd_val(pmd)
extern void set_pmd_at(struct mm_struct *mm, unsigned long addr, pmd_t *pmdp, pmd_t pmd);
#define pte_page(x) pfn_to_page(pte_pfn(x))
#define pte_pfn(x) ((unsigned long)(((x).pte & _PFN_MASK) >> PFN_PTE_SHIFT))
#define pfn_pte(pfn, prot) __pte(((pfn) << PFN_PTE_SHIFT) | pgprot_val(prot))
#define pfn_pmd(pfn, prot) __pmd(((pfn) << PFN_PTE_SHIFT) | pgprot_val(prot))
/*
* Initialize a new pgd / pud / pmd table with invalid pointers.
*/
extern void pgd_init(void *addr);
extern void pud_init(void *addr);
#define pud_init pud_init
extern void pmd_init(void *addr);
#define pmd_init pmd_init
extern void kernel_pte_init(void *addr);
#define kernel_pte_init kernel_pte_init
/*
* Encode/decode swap entries and swap PTEs. Swap PTEs are all PTEs that
* are !pte_none() && !pte_present().
*
* Format of 32bit swap PTEs:
*
* 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
* <------------ offset -------------> E <- type -> <-- zeroes -->
*
* E is the exclusive marker that is not stored in swap entries.
* The zero'ed bits include _PAGE_PRESENT.
*
* Format of 64bit swap PTEs:
*
* 6 6 6 6 5 5 5 5 5 5 5 5 5 5 4 4 4 4 4 4 4 4 4 4 3 3 3 3 3 3 3 3
* 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2
* <--------------------------- offset ---------------------------
*
* 3 3 2 2 2 2 2 2 2 2 2 2 1 1 1 1 1 1 1 1 1 1
* 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0 9 8 7 6 5 4 3 2 1 0
* --------------> E <--- type ---> <---------- zeroes ---------->
*
* E is the exclusive marker that is not stored in swap entries.
* The zero'ed bits include _PAGE_PRESENT and _PAGE_PROTNONE.
*/
#define __SWP_TYPE_BITS (IS_ENABLED(CONFIG_32BIT) ? 5 : 7)
#define __SWP_TYPE_MASK ((1UL << __SWP_TYPE_BITS) - 1)
#define __SWP_TYPE_SHIFT (IS_ENABLED(CONFIG_32BIT) ? 8 : 16)
#define __SWP_OFFSET_SHIFT (__SWP_TYPE_BITS + __SWP_TYPE_SHIFT + 1)
static inline pte_t mk_swap_pte(unsigned long type, unsigned long offset)
{
pte_t pte;
pte_val(pte) = ((type & __SWP_TYPE_MASK) << __SWP_TYPE_SHIFT) | (offset << __SWP_OFFSET_SHIFT);
return pte;
}
#define __swp_type(x) (((x).val >> __SWP_TYPE_SHIFT) & __SWP_TYPE_MASK)
#define __swp_offset(x) ((x).val >> __SWP_OFFSET_SHIFT)
#define __swp_entry(type, offset) ((swp_entry_t) { pte_val(mk_swap_pte((type), (offset))) })
#define __swp_entry_to_pte(x) __pte((x).val)
#define __swp_entry_to_pmd(x) __pmd((x).val | _PAGE_HUGE)
#define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) })
#define __pmd_to_swp_entry(pmd) ((swp_entry_t) { pmd_val(pmd) })
static inline bool pte_swp_exclusive(pte_t pte)
{
return pte_val(pte) & _PAGE_SWP_EXCLUSIVE;
}
static inline pte_t pte_swp_mkexclusive(pte_t pte)
{
pte_val(pte) |= _PAGE_SWP_EXCLUSIVE;
return pte;
}
static inline pte_t pte_swp_clear_exclusive(pte_t pte)
{
pte_val(pte) &= ~_PAGE_SWP_EXCLUSIVE;
return pte;
}
#define pte_none(pte) (!(pte_val(pte) & ~_PAGE_GLOBAL))
#define pte_present(pte) (pte_val(pte) & (_PAGE_PRESENT | _PAGE_PROTNONE))
#define pte_no_exec(pte) (pte_val(pte) & _PAGE_NO_EXEC)
static inline void set_pte(pte_t *ptep, pte_t pteval)
{
WRITE_ONCE(*ptep, pteval);
#ifdef CONFIG_SMP
if (pte_val(pteval) & _PAGE_GLOBAL)
DBAR(0b11000); /* o_wrw = 0b11000 */
#endif
}
static inline void pte_clear(struct mm_struct *mm, unsigned long addr, pte_t *ptep)
{
pte_t pte = ptep_get(ptep);
pte_val(pte) &= _PAGE_GLOBAL;
set_pte(ptep, pte);
}
#define PGD_T_LOG2 (__builtin_ffs(sizeof(pgd_t)) - 1)
#define PMD_T_LOG2 (__builtin_ffs(sizeof(pmd_t)) - 1)
#define PTE_T_LOG2 (__builtin_ffs(sizeof(pte_t)) - 1)
extern pgd_t swapper_pg_dir[];
extern pgd_t invalid_pg_dir[];
/*
* The following only work if pte_present() is true.
* Undefined behaviour if not..
*/
static inline int pte_write(pte_t pte) { return pte_val(pte) & _PAGE_WRITE; }
static inline int pte_young(pte_t pte) { return pte_val(pte) & _PAGE_ACCESSED; }
static inline int pte_dirty(pte_t pte) { return pte_val(pte) & (_PAGE_DIRTY | _PAGE_MODIFIED); }
static inline pte_t pte_mkold(pte_t pte)
{
pte_val(pte) &= ~_PAGE_ACCESSED;
return pte;
}
static inline pte_t pte_mkyoung(pte_t pte)
{
pte_val(pte) |= _PAGE_ACCESSED;
return pte;
}
static inline pte_t pte_mkclean(pte_t pte)
{
pte_val(pte) &= ~(_PAGE_DIRTY | _PAGE_MODIFIED);
return pte;
}
static inline pte_t pte_mkdirty(pte_t pte)
{
pte_val(pte) |= _PAGE_MODIFIED;
if (pte_val(pte) & _PAGE_WRITE)
pte_val(pte) |= _PAGE_DIRTY;
return pte;
}
static inline pte_t pte_mkwrite_novma(pte_t pte)
{
pte_val(pte) |= _PAGE_WRITE;
if (pte_val(pte) & _PAGE_MODIFIED)
pte_val(pte) |= _PAGE_DIRTY;
return pte;
}
static inline pte_t pte_wrprotect(pte_t pte)
{
if (pte_val(pte) & _PAGE_DIRTY)
pte_val(pte) |= _PAGE_MODIFIED;
pte_val(pte) &= ~(_PAGE_WRITE | _PAGE_DIRTY);
return pte;
}
static inline int pte_huge(pte_t pte) { return pte_val(pte) & _PAGE_HUGE; }
static inline pte_t pte_mkhuge(pte_t pte)
{
pte_val(pte) |= _PAGE_HUGE;
return pte;
}
#if defined(CONFIG_ARCH_HAS_PTE_SPECIAL)
static inline int pte_special(pte_t pte) { return pte_val(pte) & _PAGE_SPECIAL; }
static inline pte_t pte_mkspecial(pte_t pte) { pte_val(pte) |= _PAGE_SPECIAL; return pte; }
#endif /* CONFIG_ARCH_HAS_PTE_SPECIAL */
#define pte_accessible pte_accessible
static inline unsigned long pte_accessible(struct mm_struct *mm, pte_t a)
{
if (pte_val(a) & _PAGE_PRESENT)
return true;
if ((pte_val(a) & _PAGE_PROTNONE) &&
atomic_read(&mm->tlb_flush_pending))
return true;
return false;
}
static inline pte_t pte_modify(pte_t pte, pgprot_t newprot)
{
if (pte_val(pte) & _PAGE_DIRTY)
pte_val(pte) |= _PAGE_MODIFIED;
return __pte((pte_val(pte) & _PAGE_CHG_MASK) |
(pgprot_val(newprot) & ~_PAGE_CHG_MASK));
}
extern void __update_tlb(struct vm_area_struct *vma,
unsigned long address, pte_t *ptep);
static inline void update_mmu_cache_range(struct vm_fault *vmf,
struct vm_area_struct *vma, unsigned long address,
pte_t *ptep, unsigned int nr)
{
for (;;) {
__update_tlb(vma, address, ptep);
if (--nr == 0)
break;
address += PAGE_SIZE;
ptep++;
}
}
#define update_mmu_cache(vma, addr, ptep) \
update_mmu_cache_range(NULL, vma, addr, ptep, 1)
#define update_mmu_tlb_range(vma, addr, ptep, nr) \
update_mmu_cache_range(NULL, vma, addr, ptep, nr)
static inline void update_mmu_cache_pmd(struct vm_area_struct *vma,
unsigned long address, pmd_t *pmdp)
{
__update_tlb(vma, address, (pte_t *)pmdp);
}
static inline unsigned long pmd_pfn(pmd_t pmd)
{
return (pmd_val(pmd) & _PFN_MASK) >> PFN_PTE_SHIFT;
}
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
/* We don't have hardware dirty/accessed bits, generic_pmdp_establish is fine.*/
#define pmdp_establish generic_pmdp_establish
static inline int pmd_trans_huge(pmd_t pmd)
{
return !!(pmd_val(pmd) & _PAGE_HUGE) && pmd_present(pmd);
}
static inline pmd_t pmd_mkhuge(pmd_t pmd)
{
pmd_val(pmd) = (pmd_val(pmd) & ~(_PAGE_GLOBAL)) |
((pmd_val(pmd) & _PAGE_GLOBAL) << (_PAGE_HGLOBAL_SHIFT - _PAGE_GLOBAL_SHIFT));
pmd_val(pmd) |= _PAGE_HUGE;
return pmd;
}
#define pmd_write pmd_write
static inline int pmd_write(pmd_t pmd)
{
return !!(pmd_val(pmd) & _PAGE_WRITE);
}
static inline pmd_t pmd_mkwrite_novma(pmd_t pmd)
{
pmd_val(pmd) |= _PAGE_WRITE;
if (pmd_val(pmd) & _PAGE_MODIFIED)
pmd_val(pmd) |= _PAGE_DIRTY;
return pmd;
}
static inline pmd_t pmd_wrprotect(pmd_t pmd)
{
if (pmd_val(pmd) & _PAGE_DIRTY)
pmd_val(pmd) |= _PAGE_MODIFIED;
pmd_val(pmd) &= ~(_PAGE_WRITE | _PAGE_DIRTY);
return pmd;
}
#define pmd_dirty pmd_dirty
static inline int pmd_dirty(pmd_t pmd)
{
return !!(pmd_val(pmd) & (_PAGE_DIRTY | _PAGE_MODIFIED));
}
static inline pmd_t pmd_mkclean(pmd_t pmd)
{
pmd_val(pmd) &= ~(_PAGE_DIRTY | _PAGE_MODIFIED);
return pmd;
}
static inline pmd_t pmd_mkdirty(pmd_t pmd)
{
pmd_val(pmd) |= _PAGE_MODIFIED;
if (pmd_val(pmd) & _PAGE_WRITE)
pmd_val(pmd) |= _PAGE_DIRTY;
return pmd;
}
#define pmd_young pmd_young
static inline int pmd_young(pmd_t pmd)
{
return !!(pmd_val(pmd) & _PAGE_ACCESSED);
}
static inline pmd_t pmd_mkold(pmd_t pmd)
{
pmd_val(pmd) &= ~_PAGE_ACCESSED;
return pmd;
}
static inline pmd_t pmd_mkyoung(pmd_t pmd)
{
pmd_val(pmd) |= _PAGE_ACCESSED;
return pmd;
}
static inline struct page *pmd_page(pmd_t pmd)
{
if (pmd_trans_huge(pmd))
return pfn_to_page(pmd_pfn(pmd));
return pfn_to_page(pmd_phys(pmd) >> PAGE_SHIFT);
}
static inline pmd_t pmd_modify(pmd_t pmd, pgprot_t newprot)
{
if (pmd_val(pmd) & _PAGE_DIRTY)
pmd_val(pmd) |= _PAGE_MODIFIED;
return __pmd((pmd_val(pmd) & _HPAGE_CHG_MASK) |
(pgprot_val(newprot) & ~_HPAGE_CHG_MASK));
}
static inline pmd_t pmd_mkinvalid(pmd_t pmd)
{
pmd_val(pmd) |= _PAGE_PRESENT_INVALID;
pmd_val(pmd) &= ~(_PAGE_PRESENT | _PAGE_VALID | _PAGE_DIRTY | _PAGE_PROTNONE);
return pmd;
}
/*
* The generic version pmdp_huge_get_and_clear uses a version of pmd_clear() with a
* different prototype.
*/
#define __HAVE_ARCH_PMDP_HUGE_GET_AND_CLEAR
static inline pmd_t pmdp_huge_get_and_clear(struct mm_struct *mm,
unsigned long address, pmd_t *pmdp)
{
pmd_t old = pmdp_get(pmdp);
pmd_clear(pmdp);
return old;
}
#endif /* CONFIG_TRANSPARENT_HUGEPAGE */
#ifdef CONFIG_NUMA_BALANCING
static inline long pte_protnone(pte_t pte)
{
return (pte_val(pte) & _PAGE_PROTNONE);
}
static inline long pmd_protnone(pmd_t pmd)
{
return (pmd_val(pmd) & _PAGE_PROTNONE);
}
#endif /* CONFIG_NUMA_BALANCING */
#define pmd_leaf(pmd) ((pmd_val(pmd) & _PAGE_HUGE) != 0)
#define pud_leaf(pud) ((pud_val(pud) & _PAGE_HUGE) != 0)
/*
* We provide our own get_unmapped area to cope with the virtual aliasing
* constraints placed on us by the cache architecture.
*/
#define HAVE_ARCH_UNMAPPED_AREA
#define HAVE_ARCH_UNMAPPED_AREA_TOPDOWN
#endif /* !__ASSEMBLER__ */
#endif /* _ASM_PGTABLE_H */
@@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2020-2022 Loongson Technology Corporation Limited
*/
#ifndef __ASM_PREFETCH_H
#define __ASM_PREFETCH_H
#define Pref_Load 0
#define Pref_Store 8
#ifdef __ASSEMBLER__
.macro __pref hint addr
#ifdef CONFIG_CPU_HAS_PREFETCH
preld \hint, \addr, 0
#endif
.endm
.macro pref_load addr
__pref Pref_Load, \addr
.endm
.macro pref_store addr
__pref Pref_Store, \addr
.endm
#endif
#endif /* __ASM_PREFETCH_H */

Some files were not shown because too many files have changed in this diff Show More