mirror of
https://gitee.com/bianbu-linux/linux-6.6
synced 2025-04-24 14:07:52 -04:00
RISC-V: Generic library routines and assembly
This patch contains code that is more specific to the RISC-V ISA than it is to Linux. It contains string and math operations, C wrappers for various assembly instructions, stack walking code, and uaccess. Signed-off-by: Palmer Dabbelt <palmer@dabbelt.com>
This commit is contained in:
parent
fab957c11e
commit
5d8544e2d0
11 changed files with 1389 additions and 0 deletions
76
arch/riscv/include/asm/asm.h
Normal file
76
arch/riscv/include/asm/asm.h
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Regents of the University of California
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation, version 2.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ASM_RISCV_ASM_H
|
||||||
|
#define _ASM_RISCV_ASM_H
|
||||||
|
|
||||||
|
#ifdef __ASSEMBLY__
|
||||||
|
#define __ASM_STR(x) x
|
||||||
|
#else
|
||||||
|
#define __ASM_STR(x) #x
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if __riscv_xlen == 64
|
||||||
|
#define __REG_SEL(a, b) __ASM_STR(a)
|
||||||
|
#elif __riscv_xlen == 32
|
||||||
|
#define __REG_SEL(a, b) __ASM_STR(b)
|
||||||
|
#else
|
||||||
|
#error "Unexpected __riscv_xlen"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define REG_L __REG_SEL(ld, lw)
|
||||||
|
#define REG_S __REG_SEL(sd, sw)
|
||||||
|
#define SZREG __REG_SEL(8, 4)
|
||||||
|
#define LGREG __REG_SEL(3, 2)
|
||||||
|
|
||||||
|
#if __SIZEOF_POINTER__ == 8
|
||||||
|
#ifdef __ASSEMBLY__
|
||||||
|
#define RISCV_PTR .dword
|
||||||
|
#define RISCV_SZPTR 8
|
||||||
|
#define RISCV_LGPTR 3
|
||||||
|
#else
|
||||||
|
#define RISCV_PTR ".dword"
|
||||||
|
#define RISCV_SZPTR "8"
|
||||||
|
#define RISCV_LGPTR "3"
|
||||||
|
#endif
|
||||||
|
#elif __SIZEOF_POINTER__ == 4
|
||||||
|
#ifdef __ASSEMBLY__
|
||||||
|
#define RISCV_PTR .word
|
||||||
|
#define RISCV_SZPTR 4
|
||||||
|
#define RISCV_LGPTR 2
|
||||||
|
#else
|
||||||
|
#define RISCV_PTR ".word"
|
||||||
|
#define RISCV_SZPTR "4"
|
||||||
|
#define RISCV_LGPTR "2"
|
||||||
|
#endif
|
||||||
|
#else
|
||||||
|
#error "Unexpected __SIZEOF_POINTER__"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (__SIZEOF_INT__ == 4)
|
||||||
|
#define INT __ASM_STR(.word)
|
||||||
|
#define SZINT __ASM_STR(4)
|
||||||
|
#define LGINT __ASM_STR(2)
|
||||||
|
#else
|
||||||
|
#error "Unexpected __SIZEOF_INT__"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if (__SIZEOF_SHORT__ == 2)
|
||||||
|
#define SHORT __ASM_STR(.half)
|
||||||
|
#define SZSHORT __ASM_STR(2)
|
||||||
|
#define LGSHORT __ASM_STR(1)
|
||||||
|
#else
|
||||||
|
#error "Unexpected __SIZEOF_SHORT__"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* _ASM_RISCV_ASM_H */
|
132
arch/riscv/include/asm/csr.h
Normal file
132
arch/riscv/include/asm/csr.h
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Regents of the University of California
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation, version 2.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ASM_RISCV_CSR_H
|
||||||
|
#define _ASM_RISCV_CSR_H
|
||||||
|
|
||||||
|
#include <linux/const.h>
|
||||||
|
|
||||||
|
/* Status register flags */
|
||||||
|
#define SR_IE _AC(0x00000002, UL) /* Interrupt Enable */
|
||||||
|
#define SR_PIE _AC(0x00000020, UL) /* Previous IE */
|
||||||
|
#define SR_PS _AC(0x00000100, UL) /* Previously Supervisor */
|
||||||
|
#define SR_SUM _AC(0x00040000, UL) /* Supervisor may access User Memory */
|
||||||
|
|
||||||
|
#define SR_FS _AC(0x00006000, UL) /* Floating-point Status */
|
||||||
|
#define SR_FS_OFF _AC(0x00000000, UL)
|
||||||
|
#define SR_FS_INITIAL _AC(0x00002000, UL)
|
||||||
|
#define SR_FS_CLEAN _AC(0x00004000, UL)
|
||||||
|
#define SR_FS_DIRTY _AC(0x00006000, UL)
|
||||||
|
|
||||||
|
#define SR_XS _AC(0x00018000, UL) /* Extension Status */
|
||||||
|
#define SR_XS_OFF _AC(0x00000000, UL)
|
||||||
|
#define SR_XS_INITIAL _AC(0x00008000, UL)
|
||||||
|
#define SR_XS_CLEAN _AC(0x00010000, UL)
|
||||||
|
#define SR_XS_DIRTY _AC(0x00018000, UL)
|
||||||
|
|
||||||
|
#ifndef CONFIG_64BIT
|
||||||
|
#define SR_SD _AC(0x80000000, UL) /* FS/XS dirty */
|
||||||
|
#else
|
||||||
|
#define SR_SD _AC(0x8000000000000000, UL) /* FS/XS dirty */
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* SPTBR flags */
|
||||||
|
#if __riscv_xlen == 32
|
||||||
|
#define SPTBR_PPN _AC(0x003FFFFF, UL)
|
||||||
|
#define SPTBR_MODE_32 _AC(0x80000000, UL)
|
||||||
|
#define SPTBR_MODE SPTBR_MODE_32
|
||||||
|
#else
|
||||||
|
#define SPTBR_PPN _AC(0x00000FFFFFFFFFFF, UL)
|
||||||
|
#define SPTBR_MODE_39 _AC(0x8000000000000000, UL)
|
||||||
|
#define SPTBR_MODE SPTBR_MODE_39
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Interrupt Enable and Interrupt Pending flags */
|
||||||
|
#define SIE_SSIE _AC(0x00000002, UL) /* Software Interrupt Enable */
|
||||||
|
#define SIE_STIE _AC(0x00000020, UL) /* Timer Interrupt Enable */
|
||||||
|
|
||||||
|
#define EXC_INST_MISALIGNED 0
|
||||||
|
#define EXC_INST_ACCESS 1
|
||||||
|
#define EXC_BREAKPOINT 3
|
||||||
|
#define EXC_LOAD_ACCESS 5
|
||||||
|
#define EXC_STORE_ACCESS 7
|
||||||
|
#define EXC_SYSCALL 8
|
||||||
|
#define EXC_INST_PAGE_FAULT 12
|
||||||
|
#define EXC_LOAD_PAGE_FAULT 13
|
||||||
|
#define EXC_STORE_PAGE_FAULT 15
|
||||||
|
|
||||||
|
#ifndef __ASSEMBLY__
|
||||||
|
|
||||||
|
#define csr_swap(csr, val) \
|
||||||
|
({ \
|
||||||
|
unsigned long __v = (unsigned long)(val); \
|
||||||
|
__asm__ __volatile__ ("csrrw %0, " #csr ", %1" \
|
||||||
|
: "=r" (__v) : "rK" (__v) \
|
||||||
|
: "memory"); \
|
||||||
|
__v; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define csr_read(csr) \
|
||||||
|
({ \
|
||||||
|
register unsigned long __v; \
|
||||||
|
__asm__ __volatile__ ("csrr %0, " #csr \
|
||||||
|
: "=r" (__v) : \
|
||||||
|
: "memory"); \
|
||||||
|
__v; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define csr_write(csr, val) \
|
||||||
|
({ \
|
||||||
|
unsigned long __v = (unsigned long)(val); \
|
||||||
|
__asm__ __volatile__ ("csrw " #csr ", %0" \
|
||||||
|
: : "rK" (__v) \
|
||||||
|
: "memory"); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define csr_read_set(csr, val) \
|
||||||
|
({ \
|
||||||
|
unsigned long __v = (unsigned long)(val); \
|
||||||
|
__asm__ __volatile__ ("csrrs %0, " #csr ", %1" \
|
||||||
|
: "=r" (__v) : "rK" (__v) \
|
||||||
|
: "memory"); \
|
||||||
|
__v; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define csr_set(csr, val) \
|
||||||
|
({ \
|
||||||
|
unsigned long __v = (unsigned long)(val); \
|
||||||
|
__asm__ __volatile__ ("csrs " #csr ", %0" \
|
||||||
|
: : "rK" (__v) \
|
||||||
|
: "memory"); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define csr_read_clear(csr, val) \
|
||||||
|
({ \
|
||||||
|
unsigned long __v = (unsigned long)(val); \
|
||||||
|
__asm__ __volatile__ ("csrrc %0, " #csr ", %1" \
|
||||||
|
: "=r" (__v) : "rK" (__v) \
|
||||||
|
: "memory"); \
|
||||||
|
__v; \
|
||||||
|
})
|
||||||
|
|
||||||
|
#define csr_clear(csr, val) \
|
||||||
|
({ \
|
||||||
|
unsigned long __v = (unsigned long)(val); \
|
||||||
|
__asm__ __volatile__ ("csrc " #csr ", %0" \
|
||||||
|
: : "rK" (__v) \
|
||||||
|
: "memory"); \
|
||||||
|
})
|
||||||
|
|
||||||
|
#endif /* __ASSEMBLY__ */
|
||||||
|
|
||||||
|
#endif /* _ASM_RISCV_CSR_H */
|
20
arch/riscv/include/asm/linkage.h
Normal file
20
arch/riscv/include/asm/linkage.h
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2015 Regents of the University of California
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation, version 2.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ASM_RISCV_LINKAGE_H
|
||||||
|
#define _ASM_RISCV_LINKAGE_H
|
||||||
|
|
||||||
|
#define __ALIGN .balign 4
|
||||||
|
#define __ALIGN_STR ".balign 4"
|
||||||
|
|
||||||
|
#endif /* _ASM_RISCV_LINKAGE_H */
|
26
arch/riscv/include/asm/string.h
Normal file
26
arch/riscv/include/asm/string.h
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Regents of the University of California
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation, version 2.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ASM_RISCV_STRING_H
|
||||||
|
#define _ASM_RISCV_STRING_H
|
||||||
|
|
||||||
|
#include <linux/types.h>
|
||||||
|
#include <linux/linkage.h>
|
||||||
|
|
||||||
|
#define __HAVE_ARCH_MEMSET
|
||||||
|
extern asmlinkage void *memset(void *, int, size_t);
|
||||||
|
|
||||||
|
#define __HAVE_ARCH_MEMCPY
|
||||||
|
extern asmlinkage void *memcpy(void *, const void *, size_t);
|
||||||
|
|
||||||
|
#endif /* _ASM_RISCV_STRING_H */
|
513
arch/riscv/include/asm/uaccess.h
Normal file
513
arch/riscv/include/asm/uaccess.h
Normal file
|
@ -0,0 +1,513 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Regents of the University of California
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation, version 2.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* This file was copied from include/asm-generic/uaccess.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ASM_RISCV_UACCESS_H
|
||||||
|
#define _ASM_RISCV_UACCESS_H
|
||||||
|
|
||||||
|
/*
|
||||||
|
* User space memory access functions
|
||||||
|
*/
|
||||||
|
#include <linux/errno.h>
|
||||||
|
#include <linux/compiler.h>
|
||||||
|
#include <linux/thread_info.h>
|
||||||
|
#include <asm/byteorder.h>
|
||||||
|
#include <asm/asm.h>
|
||||||
|
|
||||||
|
#define __enable_user_access() \
|
||||||
|
__asm__ __volatile__ ("csrs sstatus, %0" : : "r" (SR_SUM) : "memory")
|
||||||
|
#define __disable_user_access() \
|
||||||
|
__asm__ __volatile__ ("csrc sstatus, %0" : : "r" (SR_SUM) : "memory")
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The fs value determines whether argument validity checking should be
|
||||||
|
* performed or not. If get_fs() == USER_DS, checking is performed, with
|
||||||
|
* get_fs() == KERNEL_DS, checking is bypassed.
|
||||||
|
*
|
||||||
|
* For historical reasons, these macros are grossly misnamed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define KERNEL_DS (~0UL)
|
||||||
|
#define USER_DS (TASK_SIZE)
|
||||||
|
|
||||||
|
#define get_ds() (KERNEL_DS)
|
||||||
|
#define get_fs() (current_thread_info()->addr_limit)
|
||||||
|
|
||||||
|
static inline void set_fs(mm_segment_t fs)
|
||||||
|
{
|
||||||
|
current_thread_info()->addr_limit = fs;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define segment_eq(a, b) ((a) == (b))
|
||||||
|
|
||||||
|
#define user_addr_max() (get_fs())
|
||||||
|
|
||||||
|
|
||||||
|
#define VERIFY_READ 0
|
||||||
|
#define VERIFY_WRITE 1
|
||||||
|
|
||||||
|
/**
|
||||||
|
* access_ok: - Checks if a user space pointer is valid
|
||||||
|
* @type: Type of access: %VERIFY_READ or %VERIFY_WRITE. Note that
|
||||||
|
* %VERIFY_WRITE is a superset of %VERIFY_READ - if it is safe
|
||||||
|
* to write to a block, it is always safe to read from it.
|
||||||
|
* @addr: User space pointer to start of block to check
|
||||||
|
* @size: Size of block to check
|
||||||
|
*
|
||||||
|
* Context: User context only. This function may sleep.
|
||||||
|
*
|
||||||
|
* Checks if a pointer to a block of memory in user space is valid.
|
||||||
|
*
|
||||||
|
* Returns true (nonzero) if the memory block may be valid, false (zero)
|
||||||
|
* if it is definitely invalid.
|
||||||
|
*
|
||||||
|
* Note that, depending on architecture, this function probably just
|
||||||
|
* checks that the pointer is in the user space range - after calling
|
||||||
|
* this function, memory access functions may still return -EFAULT.
|
||||||
|
*/
|
||||||
|
#define access_ok(type, addr, size) ({ \
|
||||||
|
__chk_user_ptr(addr); \
|
||||||
|
likely(__access_ok((unsigned long __force)(addr), (size))); \
|
||||||
|
})
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Ensure that the range [addr, addr+size) is within the process's
|
||||||
|
* address space
|
||||||
|
*/
|
||||||
|
static inline int __access_ok(unsigned long addr, unsigned long size)
|
||||||
|
{
|
||||||
|
const mm_segment_t fs = get_fs();
|
||||||
|
|
||||||
|
return (size <= fs) && (addr <= (fs - size));
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The exception table consists of pairs of addresses: the first is the
|
||||||
|
* address of an instruction that is allowed to fault, and the second is
|
||||||
|
* the address 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 {
|
||||||
|
unsigned long insn, fixup;
|
||||||
|
};
|
||||||
|
|
||||||
|
extern int fixup_exception(struct pt_regs *state);
|
||||||
|
|
||||||
|
#if defined(__LITTLE_ENDIAN)
|
||||||
|
#define __MSW 1
|
||||||
|
#define __LSW 0
|
||||||
|
#elif defined(__BIG_ENDIAN)
|
||||||
|
#define __MSW 0
|
||||||
|
#define __LSW 1
|
||||||
|
#else
|
||||||
|
#error "Unknown endianness"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The "__xxx" versions of the user access functions do not verify the address
|
||||||
|
* space - it must have been done previously with a separate "access_ok()"
|
||||||
|
* call.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifdef CONFIG_MMU
|
||||||
|
#define __get_user_asm(insn, x, ptr, err) \
|
||||||
|
do { \
|
||||||
|
uintptr_t __tmp; \
|
||||||
|
__typeof__(x) __x; \
|
||||||
|
__enable_user_access(); \
|
||||||
|
__asm__ __volatile__ ( \
|
||||||
|
"1:\n" \
|
||||||
|
" " insn " %1, %3\n" \
|
||||||
|
"2:\n" \
|
||||||
|
" .section .fixup,\"ax\"\n" \
|
||||||
|
" .balign 4\n" \
|
||||||
|
"3:\n" \
|
||||||
|
" li %0, %4\n" \
|
||||||
|
" li %1, 0\n" \
|
||||||
|
" jump 2b, %2\n" \
|
||||||
|
" .previous\n" \
|
||||||
|
" .section __ex_table,\"a\"\n" \
|
||||||
|
" .balign " RISCV_SZPTR "\n" \
|
||||||
|
" " RISCV_PTR " 1b, 3b\n" \
|
||||||
|
" .previous" \
|
||||||
|
: "+r" (err), "=&r" (__x), "=r" (__tmp) \
|
||||||
|
: "m" (*(ptr)), "i" (-EFAULT)); \
|
||||||
|
__disable_user_access(); \
|
||||||
|
(x) = __x; \
|
||||||
|
} while (0)
|
||||||
|
#endif /* CONFIG_MMU */
|
||||||
|
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
#define __get_user_8(x, ptr, err) \
|
||||||
|
__get_user_asm("ld", x, ptr, err)
|
||||||
|
#else /* !CONFIG_64BIT */
|
||||||
|
#ifdef CONFIG_MMU
|
||||||
|
#define __get_user_8(x, ptr, err) \
|
||||||
|
do { \
|
||||||
|
u32 __user *__ptr = (u32 __user *)(ptr); \
|
||||||
|
u32 __lo, __hi; \
|
||||||
|
uintptr_t __tmp; \
|
||||||
|
__enable_user_access(); \
|
||||||
|
__asm__ __volatile__ ( \
|
||||||
|
"1:\n" \
|
||||||
|
" lw %1, %4\n" \
|
||||||
|
"2:\n" \
|
||||||
|
" lw %2, %5\n" \
|
||||||
|
"3:\n" \
|
||||||
|
" .section .fixup,\"ax\"\n" \
|
||||||
|
" .balign 4\n" \
|
||||||
|
"4:\n" \
|
||||||
|
" li %0, %6\n" \
|
||||||
|
" li %1, 0\n" \
|
||||||
|
" li %2, 0\n" \
|
||||||
|
" jump 3b, %3\n" \
|
||||||
|
" .previous\n" \
|
||||||
|
" .section __ex_table,\"a\"\n" \
|
||||||
|
" .balign " RISCV_SZPTR "\n" \
|
||||||
|
" " RISCV_PTR " 1b, 4b\n" \
|
||||||
|
" " RISCV_PTR " 2b, 4b\n" \
|
||||||
|
" .previous" \
|
||||||
|
: "+r" (err), "=&r" (__lo), "=r" (__hi), \
|
||||||
|
"=r" (__tmp) \
|
||||||
|
: "m" (__ptr[__LSW]), "m" (__ptr[__MSW]), \
|
||||||
|
"i" (-EFAULT)); \
|
||||||
|
__disable_user_access(); \
|
||||||
|
(x) = (__typeof__(x))((__typeof__((x)-(x)))( \
|
||||||
|
(((u64)__hi << 32) | __lo))); \
|
||||||
|
} while (0)
|
||||||
|
#endif /* CONFIG_MMU */
|
||||||
|
#endif /* CONFIG_64BIT */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __get_user: - Get a simple variable from user space, with less checking.
|
||||||
|
* @x: Variable to store result.
|
||||||
|
* @ptr: Source address, in user space.
|
||||||
|
*
|
||||||
|
* Context: User context only. This function may sleep.
|
||||||
|
*
|
||||||
|
* This macro copies a single simple variable from user space to kernel
|
||||||
|
* space. It supports simple types like char and int, but not larger
|
||||||
|
* data types like structures or arrays.
|
||||||
|
*
|
||||||
|
* @ptr must have pointer-to-simple-variable type, and the result of
|
||||||
|
* dereferencing @ptr must be assignable to @x without a cast.
|
||||||
|
*
|
||||||
|
* Caller must check the pointer with access_ok() before calling this
|
||||||
|
* function.
|
||||||
|
*
|
||||||
|
* Returns zero on success, or -EFAULT on error.
|
||||||
|
* On error, the variable @x is set to zero.
|
||||||
|
*/
|
||||||
|
#define __get_user(x, ptr) \
|
||||||
|
({ \
|
||||||
|
register long __gu_err = 0; \
|
||||||
|
const __typeof__(*(ptr)) __user *__gu_ptr = (ptr); \
|
||||||
|
__chk_user_ptr(__gu_ptr); \
|
||||||
|
switch (sizeof(*__gu_ptr)) { \
|
||||||
|
case 1: \
|
||||||
|
__get_user_asm("lb", (x), __gu_ptr, __gu_err); \
|
||||||
|
break; \
|
||||||
|
case 2: \
|
||||||
|
__get_user_asm("lh", (x), __gu_ptr, __gu_err); \
|
||||||
|
break; \
|
||||||
|
case 4: \
|
||||||
|
__get_user_asm("lw", (x), __gu_ptr, __gu_err); \
|
||||||
|
break; \
|
||||||
|
case 8: \
|
||||||
|
__get_user_8((x), __gu_ptr, __gu_err); \
|
||||||
|
break; \
|
||||||
|
default: \
|
||||||
|
BUILD_BUG(); \
|
||||||
|
} \
|
||||||
|
__gu_err; \
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get_user: - Get a simple variable from user space.
|
||||||
|
* @x: Variable to store result.
|
||||||
|
* @ptr: Source address, in user space.
|
||||||
|
*
|
||||||
|
* Context: User context only. This function may sleep.
|
||||||
|
*
|
||||||
|
* This macro copies a single simple variable from user space to kernel
|
||||||
|
* space. It supports simple types like char and int, but not larger
|
||||||
|
* data types like structures or arrays.
|
||||||
|
*
|
||||||
|
* @ptr must have pointer-to-simple-variable type, and the result of
|
||||||
|
* dereferencing @ptr must be assignable to @x without a cast.
|
||||||
|
*
|
||||||
|
* Returns zero on success, or -EFAULT on error.
|
||||||
|
* On error, the variable @x is set to zero.
|
||||||
|
*/
|
||||||
|
#define get_user(x, ptr) \
|
||||||
|
({ \
|
||||||
|
const __typeof__(*(ptr)) __user *__p = (ptr); \
|
||||||
|
might_fault(); \
|
||||||
|
access_ok(VERIFY_READ, __p, sizeof(*__p)) ? \
|
||||||
|
__get_user((x), __p) : \
|
||||||
|
((x) = 0, -EFAULT); \
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_MMU
|
||||||
|
#define __put_user_asm(insn, x, ptr, err) \
|
||||||
|
do { \
|
||||||
|
uintptr_t __tmp; \
|
||||||
|
__typeof__(*(ptr)) __x = x; \
|
||||||
|
__enable_user_access(); \
|
||||||
|
__asm__ __volatile__ ( \
|
||||||
|
"1:\n" \
|
||||||
|
" " insn " %z3, %2\n" \
|
||||||
|
"2:\n" \
|
||||||
|
" .section .fixup,\"ax\"\n" \
|
||||||
|
" .balign 4\n" \
|
||||||
|
"3:\n" \
|
||||||
|
" li %0, %4\n" \
|
||||||
|
" jump 2b, %1\n" \
|
||||||
|
" .previous\n" \
|
||||||
|
" .section __ex_table,\"a\"\n" \
|
||||||
|
" .balign " RISCV_SZPTR "\n" \
|
||||||
|
" " RISCV_PTR " 1b, 3b\n" \
|
||||||
|
" .previous" \
|
||||||
|
: "+r" (err), "=r" (__tmp), "=m" (*(ptr)) \
|
||||||
|
: "rJ" (__x), "i" (-EFAULT)); \
|
||||||
|
__disable_user_access(); \
|
||||||
|
} while (0)
|
||||||
|
#endif /* CONFIG_MMU */
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
#define __put_user_8(x, ptr, err) \
|
||||||
|
__put_user_asm("sd", x, ptr, err)
|
||||||
|
#else /* !CONFIG_64BIT */
|
||||||
|
#ifdef CONFIG_MMU
|
||||||
|
#define __put_user_8(x, ptr, err) \
|
||||||
|
do { \
|
||||||
|
u32 __user *__ptr = (u32 __user *)(ptr); \
|
||||||
|
u64 __x = (__typeof__((x)-(x)))(x); \
|
||||||
|
uintptr_t __tmp; \
|
||||||
|
__enable_user_access(); \
|
||||||
|
__asm__ __volatile__ ( \
|
||||||
|
"1:\n" \
|
||||||
|
" sw %z4, %2\n" \
|
||||||
|
"2:\n" \
|
||||||
|
" sw %z5, %3\n" \
|
||||||
|
"3:\n" \
|
||||||
|
" .section .fixup,\"ax\"\n" \
|
||||||
|
" .balign 4\n" \
|
||||||
|
"4:\n" \
|
||||||
|
" li %0, %6\n" \
|
||||||
|
" jump 2b, %1\n" \
|
||||||
|
" .previous\n" \
|
||||||
|
" .section __ex_table,\"a\"\n" \
|
||||||
|
" .balign " RISCV_SZPTR "\n" \
|
||||||
|
" " RISCV_PTR " 1b, 4b\n" \
|
||||||
|
" " RISCV_PTR " 2b, 4b\n" \
|
||||||
|
" .previous" \
|
||||||
|
: "+r" (err), "=r" (__tmp), \
|
||||||
|
"=m" (__ptr[__LSW]), \
|
||||||
|
"=m" (__ptr[__MSW]) \
|
||||||
|
: "rJ" (__x), "rJ" (__x >> 32), "i" (-EFAULT)); \
|
||||||
|
__disable_user_access(); \
|
||||||
|
} while (0)
|
||||||
|
#endif /* CONFIG_MMU */
|
||||||
|
#endif /* CONFIG_64BIT */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __put_user: - Write a simple value into user space, with less checking.
|
||||||
|
* @x: Value to copy to user space.
|
||||||
|
* @ptr: Destination address, in user space.
|
||||||
|
*
|
||||||
|
* Context: User context only. This function may sleep.
|
||||||
|
*
|
||||||
|
* This macro copies a single simple value from kernel space to user
|
||||||
|
* space. It supports simple types like char and int, but not larger
|
||||||
|
* data types like structures or arrays.
|
||||||
|
*
|
||||||
|
* @ptr must have pointer-to-simple-variable type, and @x must be assignable
|
||||||
|
* to the result of dereferencing @ptr.
|
||||||
|
*
|
||||||
|
* Caller must check the pointer with access_ok() before calling this
|
||||||
|
* function.
|
||||||
|
*
|
||||||
|
* Returns zero on success, or -EFAULT on error.
|
||||||
|
*/
|
||||||
|
#define __put_user(x, ptr) \
|
||||||
|
({ \
|
||||||
|
register long __pu_err = 0; \
|
||||||
|
__typeof__(*(ptr)) __user *__gu_ptr = (ptr); \
|
||||||
|
__chk_user_ptr(__gu_ptr); \
|
||||||
|
switch (sizeof(*__gu_ptr)) { \
|
||||||
|
case 1: \
|
||||||
|
__put_user_asm("sb", (x), __gu_ptr, __pu_err); \
|
||||||
|
break; \
|
||||||
|
case 2: \
|
||||||
|
__put_user_asm("sh", (x), __gu_ptr, __pu_err); \
|
||||||
|
break; \
|
||||||
|
case 4: \
|
||||||
|
__put_user_asm("sw", (x), __gu_ptr, __pu_err); \
|
||||||
|
break; \
|
||||||
|
case 8: \
|
||||||
|
__put_user_8((x), __gu_ptr, __pu_err); \
|
||||||
|
break; \
|
||||||
|
default: \
|
||||||
|
BUILD_BUG(); \
|
||||||
|
} \
|
||||||
|
__pu_err; \
|
||||||
|
})
|
||||||
|
|
||||||
|
/**
|
||||||
|
* put_user: - Write a simple value into user space.
|
||||||
|
* @x: Value to copy to user space.
|
||||||
|
* @ptr: Destination address, in user space.
|
||||||
|
*
|
||||||
|
* Context: User context only. This function may sleep.
|
||||||
|
*
|
||||||
|
* This macro copies a single simple value from kernel space to user
|
||||||
|
* space. It supports simple types like char and int, but not larger
|
||||||
|
* data types like structures or arrays.
|
||||||
|
*
|
||||||
|
* @ptr must have pointer-to-simple-variable type, and @x must be assignable
|
||||||
|
* to the result of dereferencing @ptr.
|
||||||
|
*
|
||||||
|
* Returns zero on success, or -EFAULT on error.
|
||||||
|
*/
|
||||||
|
#define put_user(x, ptr) \
|
||||||
|
({ \
|
||||||
|
__typeof__(*(ptr)) __user *__p = (ptr); \
|
||||||
|
might_fault(); \
|
||||||
|
access_ok(VERIFY_WRITE, __p, sizeof(*__p)) ? \
|
||||||
|
__put_user((x), __p) : \
|
||||||
|
-EFAULT; \
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
extern unsigned long __must_check __copy_user(void __user *to,
|
||||||
|
const void __user *from, unsigned long n);
|
||||||
|
|
||||||
|
static inline unsigned long
|
||||||
|
raw_copy_from_user(void *to, const void __user *from, unsigned long n)
|
||||||
|
{
|
||||||
|
return __copy_user(to, from, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long
|
||||||
|
raw_copy_to_user(void __user *to, const void *from, unsigned long n)
|
||||||
|
{
|
||||||
|
return __copy_user(to, from, n);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern long strncpy_from_user(char *dest, const char __user *src, long count);
|
||||||
|
|
||||||
|
extern long __must_check strlen_user(const char __user *str);
|
||||||
|
extern long __must_check strnlen_user(const char __user *str, long n);
|
||||||
|
|
||||||
|
extern
|
||||||
|
unsigned long __must_check __clear_user(void __user *addr, unsigned long n);
|
||||||
|
|
||||||
|
static inline
|
||||||
|
unsigned long __must_check clear_user(void __user *to, unsigned long n)
|
||||||
|
{
|
||||||
|
might_fault();
|
||||||
|
return access_ok(VERIFY_WRITE, to, n) ?
|
||||||
|
__clear_user(to, n) : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Atomic compare-and-exchange, but with a fixup for userspace faults. Faults
|
||||||
|
* will set "err" to -EFAULT, while successful accesses return the previous
|
||||||
|
* value.
|
||||||
|
*/
|
||||||
|
#ifdef CONFIG_MMU
|
||||||
|
#define __cmpxchg_user(ptr, old, new, err, size, lrb, scb) \
|
||||||
|
({ \
|
||||||
|
__typeof__(ptr) __ptr = (ptr); \
|
||||||
|
__typeof__(*(ptr)) __old = (old); \
|
||||||
|
__typeof__(*(ptr)) __new = (new); \
|
||||||
|
__typeof__(*(ptr)) __ret; \
|
||||||
|
__typeof__(err) __err = 0; \
|
||||||
|
register unsigned int __rc; \
|
||||||
|
__enable_user_access(); \
|
||||||
|
switch (size) { \
|
||||||
|
case 4: \
|
||||||
|
__asm__ __volatile__ ( \
|
||||||
|
"0:\n" \
|
||||||
|
" lr.w" #scb " %[ret], %[ptr]\n" \
|
||||||
|
" bne %[ret], %z[old], 1f\n" \
|
||||||
|
" sc.w" #lrb " %[rc], %z[new], %[ptr]\n" \
|
||||||
|
" bnez %[rc], 0b\n" \
|
||||||
|
"1:\n" \
|
||||||
|
".section .fixup,\"ax\"\n" \
|
||||||
|
".balign 4\n" \
|
||||||
|
"2:\n" \
|
||||||
|
" li %[err], %[efault]\n" \
|
||||||
|
" jump 1b, %[rc]\n" \
|
||||||
|
".previous\n" \
|
||||||
|
".section __ex_table,\"a\"\n" \
|
||||||
|
".balign " RISCV_SZPTR "\n" \
|
||||||
|
" " RISCV_PTR " 1b, 2b\n" \
|
||||||
|
".previous\n" \
|
||||||
|
: [ret] "=&r" (__ret), \
|
||||||
|
[rc] "=&r" (__rc), \
|
||||||
|
[ptr] "+A" (*__ptr), \
|
||||||
|
[err] "=&r" (__err) \
|
||||||
|
: [old] "rJ" (__old), \
|
||||||
|
[new] "rJ" (__new), \
|
||||||
|
[efault] "i" (-EFAULT)); \
|
||||||
|
break; \
|
||||||
|
case 8: \
|
||||||
|
__asm__ __volatile__ ( \
|
||||||
|
"0:\n" \
|
||||||
|
" lr.d" #scb " %[ret], %[ptr]\n" \
|
||||||
|
" bne %[ret], %z[old], 1f\n" \
|
||||||
|
" sc.d" #lrb " %[rc], %z[new], %[ptr]\n" \
|
||||||
|
" bnez %[rc], 0b\n" \
|
||||||
|
"1:\n" \
|
||||||
|
".section .fixup,\"ax\"\n" \
|
||||||
|
".balign 4\n" \
|
||||||
|
"2:\n" \
|
||||||
|
" li %[err], %[efault]\n" \
|
||||||
|
" jump 1b, %[rc]\n" \
|
||||||
|
".previous\n" \
|
||||||
|
".section __ex_table,\"a\"\n" \
|
||||||
|
".balign " RISCV_SZPTR "\n" \
|
||||||
|
" " RISCV_PTR " 1b, 2b\n" \
|
||||||
|
".previous\n" \
|
||||||
|
: [ret] "=&r" (__ret), \
|
||||||
|
[rc] "=&r" (__rc), \
|
||||||
|
[ptr] "+A" (*__ptr), \
|
||||||
|
[err] "=&r" (__err) \
|
||||||
|
: [old] "rJ" (__old), \
|
||||||
|
[new] "rJ" (__new), \
|
||||||
|
[efault] "i" (-EFAULT)); \
|
||||||
|
break; \
|
||||||
|
default: \
|
||||||
|
BUILD_BUG(); \
|
||||||
|
} \
|
||||||
|
__disable_user_access(); \
|
||||||
|
(err) = __err; \
|
||||||
|
__ret; \
|
||||||
|
})
|
||||||
|
#endif /* CONFIG_MMU */
|
||||||
|
|
||||||
|
#endif /* _ASM_RISCV_UACCESS_H */
|
55
arch/riscv/include/asm/word-at-a-time.h
Normal file
55
arch/riscv/include/asm/word-at-a-time.h
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2012 Regents of the University of California
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation, version 2.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
* Derived from arch/x86/include/asm/word-at-a-time.h
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _ASM_RISCV_WORD_AT_A_TIME_H
|
||||||
|
#define _ASM_RISCV_WORD_AT_A_TIME_H
|
||||||
|
|
||||||
|
|
||||||
|
#include <linux/kernel.h>
|
||||||
|
|
||||||
|
struct word_at_a_time {
|
||||||
|
const unsigned long one_bits, high_bits;
|
||||||
|
};
|
||||||
|
|
||||||
|
#define WORD_AT_A_TIME_CONSTANTS { REPEAT_BYTE(0x01), REPEAT_BYTE(0x80) }
|
||||||
|
|
||||||
|
static inline unsigned long has_zero(unsigned long val,
|
||||||
|
unsigned long *bits, const struct word_at_a_time *c)
|
||||||
|
{
|
||||||
|
unsigned long mask = ((val - c->one_bits) & ~val) & c->high_bits;
|
||||||
|
*bits = mask;
|
||||||
|
return mask;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long prep_zero_mask(unsigned long val,
|
||||||
|
unsigned long bits, const struct word_at_a_time *c)
|
||||||
|
{
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long create_zero_mask(unsigned long bits)
|
||||||
|
{
|
||||||
|
bits = (bits - 1) & ~bits;
|
||||||
|
return bits >> 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline unsigned long find_zero(unsigned long mask)
|
||||||
|
{
|
||||||
|
return fls64(mask) >> 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The mask we created is directly usable as a bytemask */
|
||||||
|
#define zero_bytemask(mask) (mask)
|
||||||
|
|
||||||
|
#endif /* _ASM_RISCV_WORD_AT_A_TIME_H */
|
177
arch/riscv/kernel/stacktrace.c
Normal file
177
arch/riscv/kernel/stacktrace.c
Normal file
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2008 ARM Limited
|
||||||
|
* Copyright (C) 2014 Regents of the University of California
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License version 2 as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/export.h>
|
||||||
|
#include <linux/kallsyms.h>
|
||||||
|
#include <linux/sched.h>
|
||||||
|
#include <linux/sched/debug.h>
|
||||||
|
#include <linux/sched/task_stack.h>
|
||||||
|
#include <linux/stacktrace.h>
|
||||||
|
|
||||||
|
#ifdef CONFIG_FRAME_POINTER
|
||||||
|
|
||||||
|
struct stackframe {
|
||||||
|
unsigned long fp;
|
||||||
|
unsigned long ra;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void notrace walk_stackframe(struct task_struct *task,
|
||||||
|
struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg)
|
||||||
|
{
|
||||||
|
unsigned long fp, sp, pc;
|
||||||
|
|
||||||
|
if (regs) {
|
||||||
|
fp = GET_FP(regs);
|
||||||
|
sp = GET_USP(regs);
|
||||||
|
pc = GET_IP(regs);
|
||||||
|
} else if (task == NULL || task == current) {
|
||||||
|
const register unsigned long current_sp __asm__ ("sp");
|
||||||
|
fp = (unsigned long)__builtin_frame_address(0);
|
||||||
|
sp = current_sp;
|
||||||
|
pc = (unsigned long)walk_stackframe;
|
||||||
|
} else {
|
||||||
|
/* task blocked in __switch_to */
|
||||||
|
fp = task->thread.s[0];
|
||||||
|
sp = task->thread.sp;
|
||||||
|
pc = task->thread.ra;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
unsigned long low, high;
|
||||||
|
struct stackframe *frame;
|
||||||
|
|
||||||
|
if (unlikely(!__kernel_text_address(pc) || fn(pc, arg)))
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* Validate frame pointer */
|
||||||
|
low = sp + sizeof(struct stackframe);
|
||||||
|
high = ALIGN(sp, THREAD_SIZE);
|
||||||
|
if (unlikely(fp < low || fp > high || fp & 0x7))
|
||||||
|
break;
|
||||||
|
/* Unwind stack frame */
|
||||||
|
frame = (struct stackframe *)fp - 1;
|
||||||
|
sp = fp;
|
||||||
|
fp = frame->fp;
|
||||||
|
pc = frame->ra - 0x4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#else /* !CONFIG_FRAME_POINTER */
|
||||||
|
|
||||||
|
static void notrace walk_stackframe(struct task_struct *task,
|
||||||
|
struct pt_regs *regs, bool (*fn)(unsigned long, void *), void *arg)
|
||||||
|
{
|
||||||
|
unsigned long sp, pc;
|
||||||
|
unsigned long *ksp;
|
||||||
|
|
||||||
|
if (regs) {
|
||||||
|
sp = GET_USP(regs);
|
||||||
|
pc = GET_IP(regs);
|
||||||
|
} else if (task == NULL || task == current) {
|
||||||
|
const register unsigned long current_sp __asm__ ("sp");
|
||||||
|
sp = current_sp;
|
||||||
|
pc = (unsigned long)walk_stackframe;
|
||||||
|
} else {
|
||||||
|
/* task blocked in __switch_to */
|
||||||
|
sp = task->thread.sp;
|
||||||
|
pc = task->thread.ra;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (unlikely(sp & 0x7))
|
||||||
|
return;
|
||||||
|
|
||||||
|
ksp = (unsigned long *)sp;
|
||||||
|
while (!kstack_end(ksp)) {
|
||||||
|
if (__kernel_text_address(pc) && unlikely(fn(pc, arg)))
|
||||||
|
break;
|
||||||
|
pc = (*ksp++) - 0x4;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* CONFIG_FRAME_POINTER */
|
||||||
|
|
||||||
|
|
||||||
|
static bool print_trace_address(unsigned long pc, void *arg)
|
||||||
|
{
|
||||||
|
print_ip_sym(pc);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void show_stack(struct task_struct *task, unsigned long *sp)
|
||||||
|
{
|
||||||
|
pr_cont("Call Trace:\n");
|
||||||
|
walk_stackframe(task, NULL, print_trace_address, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static bool save_wchan(unsigned long pc, void *arg)
|
||||||
|
{
|
||||||
|
if (!in_sched_functions(pc)) {
|
||||||
|
unsigned long *p = arg;
|
||||||
|
*p = pc;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned long get_wchan(struct task_struct *task)
|
||||||
|
{
|
||||||
|
unsigned long pc = 0;
|
||||||
|
|
||||||
|
if (likely(task && task != current && task->state != TASK_RUNNING))
|
||||||
|
walk_stackframe(task, NULL, save_wchan, &pc);
|
||||||
|
return pc;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#ifdef CONFIG_STACKTRACE
|
||||||
|
|
||||||
|
static bool __save_trace(unsigned long pc, void *arg, bool nosched)
|
||||||
|
{
|
||||||
|
struct stack_trace *trace = arg;
|
||||||
|
|
||||||
|
if (unlikely(nosched && in_sched_functions(pc)))
|
||||||
|
return false;
|
||||||
|
if (unlikely(trace->skip > 0)) {
|
||||||
|
trace->skip--;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
trace->entries[trace->nr_entries++] = pc;
|
||||||
|
return (trace->nr_entries >= trace->max_entries);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool save_trace(unsigned long pc, void *arg)
|
||||||
|
{
|
||||||
|
return __save_trace(pc, arg, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Save stack-backtrace addresses into a stack_trace buffer.
|
||||||
|
*/
|
||||||
|
void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace)
|
||||||
|
{
|
||||||
|
walk_stackframe(tsk, NULL, save_trace, trace);
|
||||||
|
if (trace->nr_entries < trace->max_entries)
|
||||||
|
trace->entries[trace->nr_entries++] = ULONG_MAX;
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(save_stack_trace_tsk);
|
||||||
|
|
||||||
|
void save_stack_trace(struct stack_trace *trace)
|
||||||
|
{
|
||||||
|
save_stack_trace_tsk(NULL, trace);
|
||||||
|
}
|
||||||
|
EXPORT_SYMBOL_GPL(save_stack_trace);
|
||||||
|
|
||||||
|
#endif /* CONFIG_STACKTRACE */
|
115
arch/riscv/lib/memcpy.S
Normal file
115
arch/riscv/lib/memcpy.S
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Regents of the University of California
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation, version 2.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <linux/linkage.h>
|
||||||
|
#include <asm/asm.h>
|
||||||
|
|
||||||
|
/* void *memcpy(void *, const void *, size_t) */
|
||||||
|
ENTRY(memcpy)
|
||||||
|
move t6, a0 /* Preserve return value */
|
||||||
|
|
||||||
|
/* Defer to byte-oriented copy for small sizes */
|
||||||
|
sltiu a3, a2, 128
|
||||||
|
bnez a3, 4f
|
||||||
|
/* Use word-oriented copy only if low-order bits match */
|
||||||
|
andi a3, t6, SZREG-1
|
||||||
|
andi a4, a1, SZREG-1
|
||||||
|
bne a3, a4, 4f
|
||||||
|
|
||||||
|
beqz a3, 2f /* Skip if already aligned */
|
||||||
|
/*
|
||||||
|
* Round to nearest double word-aligned address
|
||||||
|
* greater than or equal to start address
|
||||||
|
*/
|
||||||
|
andi a3, a1, ~(SZREG-1)
|
||||||
|
addi a3, a3, SZREG
|
||||||
|
/* Handle initial misalignment */
|
||||||
|
sub a4, a3, a1
|
||||||
|
1:
|
||||||
|
lb a5, 0(a1)
|
||||||
|
addi a1, a1, 1
|
||||||
|
sb a5, 0(t6)
|
||||||
|
addi t6, t6, 1
|
||||||
|
bltu a1, a3, 1b
|
||||||
|
sub a2, a2, a4 /* Update count */
|
||||||
|
|
||||||
|
2:
|
||||||
|
andi a4, a2, ~((16*SZREG)-1)
|
||||||
|
beqz a4, 4f
|
||||||
|
add a3, a1, a4
|
||||||
|
3:
|
||||||
|
REG_L a4, 0(a1)
|
||||||
|
REG_L a5, SZREG(a1)
|
||||||
|
REG_L a6, 2*SZREG(a1)
|
||||||
|
REG_L a7, 3*SZREG(a1)
|
||||||
|
REG_L t0, 4*SZREG(a1)
|
||||||
|
REG_L t1, 5*SZREG(a1)
|
||||||
|
REG_L t2, 6*SZREG(a1)
|
||||||
|
REG_L t3, 7*SZREG(a1)
|
||||||
|
REG_L t4, 8*SZREG(a1)
|
||||||
|
REG_L t5, 9*SZREG(a1)
|
||||||
|
REG_S a4, 0(t6)
|
||||||
|
REG_S a5, SZREG(t6)
|
||||||
|
REG_S a6, 2*SZREG(t6)
|
||||||
|
REG_S a7, 3*SZREG(t6)
|
||||||
|
REG_S t0, 4*SZREG(t6)
|
||||||
|
REG_S t1, 5*SZREG(t6)
|
||||||
|
REG_S t2, 6*SZREG(t6)
|
||||||
|
REG_S t3, 7*SZREG(t6)
|
||||||
|
REG_S t4, 8*SZREG(t6)
|
||||||
|
REG_S t5, 9*SZREG(t6)
|
||||||
|
REG_L a4, 10*SZREG(a1)
|
||||||
|
REG_L a5, 11*SZREG(a1)
|
||||||
|
REG_L a6, 12*SZREG(a1)
|
||||||
|
REG_L a7, 13*SZREG(a1)
|
||||||
|
REG_L t0, 14*SZREG(a1)
|
||||||
|
REG_L t1, 15*SZREG(a1)
|
||||||
|
addi a1, a1, 16*SZREG
|
||||||
|
REG_S a4, 10*SZREG(t6)
|
||||||
|
REG_S a5, 11*SZREG(t6)
|
||||||
|
REG_S a6, 12*SZREG(t6)
|
||||||
|
REG_S a7, 13*SZREG(t6)
|
||||||
|
REG_S t0, 14*SZREG(t6)
|
||||||
|
REG_S t1, 15*SZREG(t6)
|
||||||
|
addi t6, t6, 16*SZREG
|
||||||
|
bltu a1, a3, 3b
|
||||||
|
andi a2, a2, (16*SZREG)-1 /* Update count */
|
||||||
|
|
||||||
|
4:
|
||||||
|
/* Handle trailing misalignment */
|
||||||
|
beqz a2, 6f
|
||||||
|
add a3, a1, a2
|
||||||
|
|
||||||
|
/* Use word-oriented copy if co-aligned to word boundary */
|
||||||
|
or a5, a1, t6
|
||||||
|
or a5, a5, a3
|
||||||
|
andi a5, a5, 3
|
||||||
|
bnez a5, 5f
|
||||||
|
7:
|
||||||
|
lw a4, 0(a1)
|
||||||
|
addi a1, a1, 4
|
||||||
|
sw a4, 0(t6)
|
||||||
|
addi t6, t6, 4
|
||||||
|
bltu a1, a3, 7b
|
||||||
|
|
||||||
|
ret
|
||||||
|
|
||||||
|
5:
|
||||||
|
lb a4, 0(a1)
|
||||||
|
addi a1, a1, 1
|
||||||
|
sb a4, 0(t6)
|
||||||
|
addi t6, t6, 1
|
||||||
|
bltu a1, a3, 5b
|
||||||
|
6:
|
||||||
|
ret
|
||||||
|
END(memcpy)
|
120
arch/riscv/lib/memset.S
Normal file
120
arch/riscv/lib/memset.S
Normal file
|
@ -0,0 +1,120 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2013 Regents of the University of California
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation, version 2.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <linux/linkage.h>
|
||||||
|
#include <asm/asm.h>
|
||||||
|
|
||||||
|
/* void *memset(void *, int, size_t) */
|
||||||
|
ENTRY(memset)
|
||||||
|
move t0, a0 /* Preserve return value */
|
||||||
|
|
||||||
|
/* Defer to byte-oriented fill for small sizes */
|
||||||
|
sltiu a3, a2, 16
|
||||||
|
bnez a3, 4f
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Round to nearest XLEN-aligned address
|
||||||
|
* greater than or equal to start address
|
||||||
|
*/
|
||||||
|
addi a3, t0, SZREG-1
|
||||||
|
andi a3, a3, ~(SZREG-1)
|
||||||
|
beq a3, t0, 2f /* Skip if already aligned */
|
||||||
|
/* Handle initial misalignment */
|
||||||
|
sub a4, a3, t0
|
||||||
|
1:
|
||||||
|
sb a1, 0(t0)
|
||||||
|
addi t0, t0, 1
|
||||||
|
bltu t0, a3, 1b
|
||||||
|
sub a2, a2, a4 /* Update count */
|
||||||
|
|
||||||
|
2: /* Duff's device with 32 XLEN stores per iteration */
|
||||||
|
/* Broadcast value into all bytes */
|
||||||
|
andi a1, a1, 0xff
|
||||||
|
slli a3, a1, 8
|
||||||
|
or a1, a3, a1
|
||||||
|
slli a3, a1, 16
|
||||||
|
or a1, a3, a1
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
slli a3, a1, 32
|
||||||
|
or a1, a3, a1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Calculate end address */
|
||||||
|
andi a4, a2, ~(SZREG-1)
|
||||||
|
add a3, t0, a4
|
||||||
|
|
||||||
|
andi a4, a4, 31*SZREG /* Calculate remainder */
|
||||||
|
beqz a4, 3f /* Shortcut if no remainder */
|
||||||
|
neg a4, a4
|
||||||
|
addi a4, a4, 32*SZREG /* Calculate initial offset */
|
||||||
|
|
||||||
|
/* Adjust start address with offset */
|
||||||
|
sub t0, t0, a4
|
||||||
|
|
||||||
|
/* Jump into loop body */
|
||||||
|
/* Assumes 32-bit instruction lengths */
|
||||||
|
la a5, 3f
|
||||||
|
#ifdef CONFIG_64BIT
|
||||||
|
srli a4, a4, 1
|
||||||
|
#endif
|
||||||
|
add a5, a5, a4
|
||||||
|
jr a5
|
||||||
|
3:
|
||||||
|
REG_S a1, 0(t0)
|
||||||
|
REG_S a1, SZREG(t0)
|
||||||
|
REG_S a1, 2*SZREG(t0)
|
||||||
|
REG_S a1, 3*SZREG(t0)
|
||||||
|
REG_S a1, 4*SZREG(t0)
|
||||||
|
REG_S a1, 5*SZREG(t0)
|
||||||
|
REG_S a1, 6*SZREG(t0)
|
||||||
|
REG_S a1, 7*SZREG(t0)
|
||||||
|
REG_S a1, 8*SZREG(t0)
|
||||||
|
REG_S a1, 9*SZREG(t0)
|
||||||
|
REG_S a1, 10*SZREG(t0)
|
||||||
|
REG_S a1, 11*SZREG(t0)
|
||||||
|
REG_S a1, 12*SZREG(t0)
|
||||||
|
REG_S a1, 13*SZREG(t0)
|
||||||
|
REG_S a1, 14*SZREG(t0)
|
||||||
|
REG_S a1, 15*SZREG(t0)
|
||||||
|
REG_S a1, 16*SZREG(t0)
|
||||||
|
REG_S a1, 17*SZREG(t0)
|
||||||
|
REG_S a1, 18*SZREG(t0)
|
||||||
|
REG_S a1, 19*SZREG(t0)
|
||||||
|
REG_S a1, 20*SZREG(t0)
|
||||||
|
REG_S a1, 21*SZREG(t0)
|
||||||
|
REG_S a1, 22*SZREG(t0)
|
||||||
|
REG_S a1, 23*SZREG(t0)
|
||||||
|
REG_S a1, 24*SZREG(t0)
|
||||||
|
REG_S a1, 25*SZREG(t0)
|
||||||
|
REG_S a1, 26*SZREG(t0)
|
||||||
|
REG_S a1, 27*SZREG(t0)
|
||||||
|
REG_S a1, 28*SZREG(t0)
|
||||||
|
REG_S a1, 29*SZREG(t0)
|
||||||
|
REG_S a1, 30*SZREG(t0)
|
||||||
|
REG_S a1, 31*SZREG(t0)
|
||||||
|
addi t0, t0, 32*SZREG
|
||||||
|
bltu t0, a3, 3b
|
||||||
|
andi a2, a2, SZREG-1 /* Update count */
|
||||||
|
|
||||||
|
4:
|
||||||
|
/* Handle trailing misalignment */
|
||||||
|
beqz a2, 6f
|
||||||
|
add a3, t0, a2
|
||||||
|
5:
|
||||||
|
sb a1, 0(t0)
|
||||||
|
addi t0, t0, 1
|
||||||
|
bltu t0, a3, 5b
|
||||||
|
6:
|
||||||
|
ret
|
||||||
|
END(memset)
|
117
arch/riscv/lib/uaccess.S
Normal file
117
arch/riscv/lib/uaccess.S
Normal file
|
@ -0,0 +1,117 @@
|
||||||
|
#include <linux/linkage.h>
|
||||||
|
#include <asm/asm.h>
|
||||||
|
#include <asm/csr.h>
|
||||||
|
|
||||||
|
.altmacro
|
||||||
|
.macro fixup op reg addr lbl
|
||||||
|
LOCAL _epc
|
||||||
|
_epc:
|
||||||
|
\op \reg, \addr
|
||||||
|
.section __ex_table,"a"
|
||||||
|
.balign RISCV_SZPTR
|
||||||
|
RISCV_PTR _epc, \lbl
|
||||||
|
.previous
|
||||||
|
.endm
|
||||||
|
|
||||||
|
ENTRY(__copy_user)
|
||||||
|
|
||||||
|
/* Enable access to user memory */
|
||||||
|
li t6, SR_SUM
|
||||||
|
csrs sstatus, t6
|
||||||
|
|
||||||
|
add a3, a1, a2
|
||||||
|
/* Use word-oriented copy only if low-order bits match */
|
||||||
|
andi t0, a0, SZREG-1
|
||||||
|
andi t1, a1, SZREG-1
|
||||||
|
bne t0, t1, 2f
|
||||||
|
|
||||||
|
addi t0, a1, SZREG-1
|
||||||
|
andi t1, a3, ~(SZREG-1)
|
||||||
|
andi t0, t0, ~(SZREG-1)
|
||||||
|
/*
|
||||||
|
* a3: terminal address of source region
|
||||||
|
* t0: lowest XLEN-aligned address in source
|
||||||
|
* t1: highest XLEN-aligned address in source
|
||||||
|
*/
|
||||||
|
bgeu t0, t1, 2f
|
||||||
|
bltu a1, t0, 4f
|
||||||
|
1:
|
||||||
|
fixup REG_L, t2, (a1), 10f
|
||||||
|
fixup REG_S, t2, (a0), 10f
|
||||||
|
addi a1, a1, SZREG
|
||||||
|
addi a0, a0, SZREG
|
||||||
|
bltu a1, t1, 1b
|
||||||
|
2:
|
||||||
|
bltu a1, a3, 5f
|
||||||
|
|
||||||
|
3:
|
||||||
|
/* Disable access to user memory */
|
||||||
|
csrc sstatus, t6
|
||||||
|
li a0, 0
|
||||||
|
ret
|
||||||
|
4: /* Edge case: unalignment */
|
||||||
|
fixup lbu, t2, (a1), 10f
|
||||||
|
fixup sb, t2, (a0), 10f
|
||||||
|
addi a1, a1, 1
|
||||||
|
addi a0, a0, 1
|
||||||
|
bltu a1, t0, 4b
|
||||||
|
j 1b
|
||||||
|
5: /* Edge case: remainder */
|
||||||
|
fixup lbu, t2, (a1), 10f
|
||||||
|
fixup sb, t2, (a0), 10f
|
||||||
|
addi a1, a1, 1
|
||||||
|
addi a0, a0, 1
|
||||||
|
bltu a1, a3, 5b
|
||||||
|
j 3b
|
||||||
|
ENDPROC(__copy_user)
|
||||||
|
|
||||||
|
|
||||||
|
ENTRY(__clear_user)
|
||||||
|
|
||||||
|
/* Enable access to user memory */
|
||||||
|
li t6, SR_SUM
|
||||||
|
csrs sstatus, t6
|
||||||
|
|
||||||
|
add a3, a0, a1
|
||||||
|
addi t0, a0, SZREG-1
|
||||||
|
andi t1, a3, ~(SZREG-1)
|
||||||
|
andi t0, t0, ~(SZREG-1)
|
||||||
|
/*
|
||||||
|
* a3: terminal address of target region
|
||||||
|
* t0: lowest doubleword-aligned address in target region
|
||||||
|
* t1: highest doubleword-aligned address in target region
|
||||||
|
*/
|
||||||
|
bgeu t0, t1, 2f
|
||||||
|
bltu a0, t0, 4f
|
||||||
|
1:
|
||||||
|
fixup REG_S, zero, (a0), 10f
|
||||||
|
addi a0, a0, SZREG
|
||||||
|
bltu a0, t1, 1b
|
||||||
|
2:
|
||||||
|
bltu a0, a3, 5f
|
||||||
|
|
||||||
|
3:
|
||||||
|
/* Disable access to user memory */
|
||||||
|
csrc sstatus, t6
|
||||||
|
li a0, 0
|
||||||
|
ret
|
||||||
|
4: /* Edge case: unalignment */
|
||||||
|
fixup sb, zero, (a0), 10f
|
||||||
|
addi a0, a0, 1
|
||||||
|
bltu a0, t0, 4b
|
||||||
|
j 1b
|
||||||
|
5: /* Edge case: remainder */
|
||||||
|
fixup sb, zero, (a0), 10f
|
||||||
|
addi a0, a0, 1
|
||||||
|
bltu a0, a3, 5b
|
||||||
|
j 3b
|
||||||
|
ENDPROC(__clear_user)
|
||||||
|
|
||||||
|
.section .fixup,"ax"
|
||||||
|
.balign 4
|
||||||
|
10:
|
||||||
|
/* Disable access to user memory */
|
||||||
|
csrs sstatus, t6
|
||||||
|
sub a0, a3, a0
|
||||||
|
ret
|
||||||
|
.previous
|
38
arch/riscv/lib/udivdi3.S
Normal file
38
arch/riscv/lib/udivdi3.S
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016-2017 Free Software Foundation, Inc.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or
|
||||||
|
* modify it under the terms of the GNU General Public License
|
||||||
|
* as published by the Free Software Foundation, version 2.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.globl __udivdi3
|
||||||
|
__udivdi3:
|
||||||
|
mv a2, a1
|
||||||
|
mv a1, a0
|
||||||
|
li a0, -1
|
||||||
|
beqz a2, .L5
|
||||||
|
li a3, 1
|
||||||
|
bgeu a2, a1, .L2
|
||||||
|
.L1:
|
||||||
|
blez a2, .L2
|
||||||
|
slli a2, a2, 1
|
||||||
|
slli a3, a3, 1
|
||||||
|
bgtu a1, a2, .L1
|
||||||
|
.L2:
|
||||||
|
li a0, 0
|
||||||
|
.L3:
|
||||||
|
bltu a1, a2, .L4
|
||||||
|
sub a1, a1, a2
|
||||||
|
or a0, a0, a3
|
||||||
|
.L4:
|
||||||
|
srli a3, a3, 1
|
||||||
|
srli a2, a2, 1
|
||||||
|
bnez a3, .L3
|
||||||
|
.L5:
|
||||||
|
ret
|
Loading…
Add table
Add a link
Reference in a new issue