[Turn from: http://blog.chinaunix.net/uid-20543672-id-3195249.html] When learning about Linux kernel drivers, you will encounter Copy_from_user and Copy_to_ from the start User of these two commonly used functions. These two functions are used very frequently in the kernel and are responsible for copying data from the user space to the kernel space and copying the data from the kernel space to the user space. When I first learned about Linux kernel drivers before 4.5, I just knew how to use them and did not analyze them in depth. This time when the kernel modules were mounted, they were encountered. Decide whether to follow the function carefully. First, the prototypes of these two functions are in the Arch/arm/include/asm/uaccess.h file:
- Static inline unsigned long __must_check copy_from_user (void *to, const void __user *from, unsigned long N)
- {
- if (Access_ok (Verify_read, from, N))
- n = __copy_from_user (to, from, N);
- else/* Security Hole-plug it */
- memset (to, 0, N);
- return n;
- }
- Static inline unsigned long __must_check copy_to_user (void __user *to, const void *from, unsigned long N)
- {
- if (Access_ok (Verify_write, to, N))
- n = __copy_to_user (to, from, N);
- return n;
- }
These two functions from the structure of analysis, in fact, can be divided into two parts: 1, first check the user space address pointer is valid (difficulty) 2, call __copy_from_user and __copy_to_user function in this analysis, we first easy later difficult. First look at the __copy_from_user and __copy_to_user functions of the specific data copy function for the ARM architecture, there is no separate implementation of these two functions, so their code is located in the Include/asm-generic/uaccess.h
- /*
- * The framework with the MMU should cover these two functions
- */
- #ifndef __copy_from_user
- Static inline __must_check long __copy_from_user (void *to,
- const void __user * FROM, unsigned long n)
- {
- if (__builtin_constant_p (n)) {
- Switch (n) {
- Case 1:
- * (U8 *) to = * (U8 __force *) from;
- return 0;
- Case 2:
- * (u16 *) to = * (U16 __force *) from;
- return 0;
- Case 4:
- * (U32 *) to = * (u32 __force *) from;
- return 0;
- #ifdef Config_64bit
- Case 8:
- * (U64 *) to = * (u64 __force *) from;
- return 0;
- #endif
- Default
- Break
- }
- }
- memcpy (To, (const void __force *) from, n);
- return 0;
- }
- #endif
- #ifndef __copy_to_user
- Static inline __must_check long __copy_to_user (void __user *to,
- const void *from, unsigned long N)
- {
- if (__builtin_constant_p (n)) {
- Switch (n) {
- Case 1:
- * (U8 __force *) to = * (U8 *) from;
- return 0;
- Case 2:
- * (U16 __force *) to = * (U16 *) from;
- return 0;
- Case 4:
- * (u32 __force *) to = * (U32 *) from;
- return 0;
- #ifdef Config_64bit
- Case 8:
- * (U64 __force *) to = * (U64 *) from;
- return 0;
- #endif
- Default
- Break
- }
- }
- memcpy (void __force *) to, from, N);
- return 0;
- }
- #endif
Click (here) to collapse or open
- GCC's built-in function, __builtin_constant_p, is used to determine whether a value is a compile-time number, and if the parameter value is a constant, the function returns 1, otherwise returns 0.
From the two functions can be seen in fact the structure is the same, first to see if n is a constant, if it is 1, 2, 4, 8 (64bit) directly with an assignment statement copy data. If it is not a constant or n is too large, use the memcpy function. And this memcpy function is located in LIB/STRING.C:
- #ifndef __have_arch_memcpy
- /**
- * Memcpy-copy One area's memory to another
- * @dest: Where to copy to
- * @src: Where to copy from
- * @count: The size of the area.
- *
- * You should don't use this function to access IO space, use Memcpy_toio ()
- * or Memcpy_fromio () instead.
- */
- void *memcpy (void *dest, const void *SRC, size_t count)
- {
- char *tmp = dest;
- const char *s = SRC;
- while (count--)
- *tmp++ = *s++;
- return dest;
- }
- Export_symbol (memcpy);
- #endif
This function is actually a simple use of loops to copy data, very simple. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ How to copy data we already know, now let's take a look at the previous user space pointer detection function ACCESS_OK, which is actually a macro definition, located in arch/arm/ In the Include/asm/uaccess.h file:
- /* We use 33-bit arithmetic ... * *
- #define __RANGE_OK (addr,size) ({\
- unsigned long flag, roksum; \
- __chk_user_ptr (addr); \
- __asm__ ("adds% 1,% 2,% 3; Sbcccs% 1,% 1,% 0; MOVCC%0, #0 "\
- : "=&r" (flag), "=&r" (roksum) \
- : "R" (addr), "Ir" (size), "0" (Current_thread_info ()->addr_limit) \
- : "CC"); \
- Flag })
- ......
- #define ACCESS_OK (Type,addr,size) (__RANGE_OK (addr,size) = = 0)
- ......
This is more troublesome, involving the C language in the inline assembly, if you are not familiar with the friend can see "ARM GCC Embedded Assembler manual", I am not very familiar.
Now let's examine the macro __RANGE_OK:
(1) unsigned long flag, roksum;\\ define two variables
- flag: The variable that holds the result: the non-0 represents an invalid address, and 0 means that the address can be accessed. Initially holds a non-0 value (Current_thread_info ()->addr_limit), which is the upper address value of the current process.
- roksum: Saves the end of the address range to be accessed for comparison with the current process address space limit data
(2) __chk_user_ptr (addr); \ \ definition is an empty function
However, this function involves the __CHECKER__ macro, which is defined by the __CHECKER__ macro when it checks the kernel code through the sparse (Semantic Parser for C) tool. The tool is called when using make C=1 or c=2, which examines the kernel functions and variables that declare the related properties that sparse can inspect in code.
If __checker__ is defined, this is explained in the online material: __chk_user_ptr and __chk_io_ptr only declare functions here, there is no function body, the purpose is to compile the process sparse be able to catch the compilation error, check the parameters of the type.
If __checker__ is not defined, this is an empty function.
(3) The next compilation, I translate the following appropriately:
adds%1,%2,%3
Roksum = addr + Size This operation affects the status bit (the purpose is to influence the rounding flag C)
The following two instructions have conditional cc, which is when c=0 is executed.
If the addition instruction above is carried (c=1), the following instruction is not executed, and flag is the initial value current_thread_info ()->addr_limit (not 0 value) and returns.
If there is no carry (c=0), execute the following instruction
Sbcccs% 1,% 1,%0
Roksum = Roksum-flag, i.e. (addr + size)-(Current_thread_info ()->addr_limit), the operation affects the sign bit.
if (addr + size) >= (Current_thread_info ()->addr_limit), then c=1
if (addr + size) < (Current_thread_info ()->addr_limit), the c=0
execute the following command when C=0, otherwise skip (flag not 0).
MOVCC%0, #0flag = 0, assigning a value of 0 to flag
(4) Flag;
Return flag value
The __RANGE_OK macro is actually equivalent to:
if (addr + size) >= (Current_thread_info ()->addr_limit), returns a value other than 0
if (addr + size) < (Current_thread_info ()->addr_limit), returns zero
The ACCESS_OK is to verify that the address range of the user space that will be manipulated is in the user address space limit of the current process. The function of this macro is very simple, it can be implemented in C, it is not necessary to use the assembly. Personal understanding: Because these two functions are used frequently, the assembly is used to achieve some of the functions to increase efficiency.
once again, it can be realized thatthe use of Copy_from_user and Copy_to_user is in the context of the process, because they want to access "user" Memory space, this "user" must be a specific process. It is known through the source code above, where Current_thread_info () is used to check if the space is accessible. If you use these two functions in a drive, you must use them in the function that implements the system call and not in the function that implements the interrupt processing. if used in an interrupt context, the code is likely to operate a process address space that is not related at all.
Second, because the manipulated page may be swapped out, the two functions may hibernate, so it is also not available in the interrupt context.
Detailed analysis of Copy_from_user and Copy_to_user based on ARM architecture (with MMU)