From 4ccf4beab8c447f8cd33d46afb6e10e1aa3befc6 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Wed, 31 Aug 2011 20:35:40 +0200 Subject: lib: add support for stmp-style devices MX23/28 use IP cores which follow a register layout I have first seen on STMP3xxx SoCs. In this layout, every register actually has four u32: 1.) to store a value directly 2.) a SET register where every 1-bit sets the corresponding bit, others are unaffected 3.) same with a CLR register 4.) same with a TOG (toggle) register Also, the 2 MSBs in register 0 are always the same and can be used to reset the IP core. All this is strictly speaking not mach-specific (but IP core specific) and, thus, doesn't need to be in mach-mxs/include. At least mx6 also uses IP cores following this stmp-style. So: Introduce a stmp-style device, put the code and defines for that in a public place (lib/), and let drivers for stmp-style devices select that code. To avoid regressions and ease reviewing, the actual code is simply copied from mach-mxs. It definately wants updates, but those need a seperate patch series. Voila, mach dependency gone, reusable code introduced. Note that I didn't remove the duplicated code from mach-mxs yet, first the drivers have to be converted. Signed-off-by: Wolfram Sang Acked-by: Shawn Guo Acked-by: Dong Aisheng --- lib/Kconfig | 3 +++ 1 file changed, 3 insertions(+) (limited to 'lib/Kconfig') diff --git a/lib/Kconfig b/lib/Kconfig index 4a8aba2e5cc..c5da1548b96 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -33,6 +33,9 @@ config GENERIC_IO boolean default n +config STMP_DEVICE + bool + config CRC_CCITT tristate "CRC-CCITT functions" help -- cgit v1.2.3-70-g09d2 From 9c1c21a0533aa37a475e8e8cce7ee064ed771881 Mon Sep 17 00:00:00 2001 From: Aneesh V Date: Fri, 27 Apr 2012 17:54:03 +0530 Subject: ddr: add LPDDR2 data from JESD209-2 add LPDDR2 data from the JEDEC spec JESD209-2. The data includes: 1. Addressing information for LPDDR2 memories of different densities and types(S2/S4) 2. AC timing data. This data will useful for memory controller device drivers. Right now this is used by the TI EMIF SDRAM controller driver. Signed-off-by: Aneesh V Reviewed-by: Santosh Shilimkar Reviewed-by: Benoit Cousson [santosh.shilimkar@ti.com: Moved to drivers/memory from drivers/misc] Signed-off-by: Santosh Shilimkar Tested-by: Lokesh Vutla Signed-off-by: Greg Kroah-Hartman --- include/memory/jedec_ddr.h | 175 +++++++++++++++++++++++++++++++++++++++++++++ lib/Kconfig | 8 +++ lib/Makefile | 2 + lib/jedec_ddr_data.c | 135 ++++++++++++++++++++++++++++++++++ 4 files changed, 320 insertions(+) create mode 100644 include/memory/jedec_ddr.h create mode 100644 lib/jedec_ddr_data.c (limited to 'lib/Kconfig') diff --git a/include/memory/jedec_ddr.h b/include/memory/jedec_ddr.h new file mode 100644 index 00000000000..ddad0f870e5 --- /dev/null +++ b/include/memory/jedec_ddr.h @@ -0,0 +1,175 @@ +/* + * Definitions for DDR memories based on JEDEC specs + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Aneesh V + * + * 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. + */ +#ifndef __LINUX_JEDEC_DDR_H +#define __LINUX_JEDEC_DDR_H + +#include + +/* DDR Densities */ +#define DDR_DENSITY_64Mb 1 +#define DDR_DENSITY_128Mb 2 +#define DDR_DENSITY_256Mb 3 +#define DDR_DENSITY_512Mb 4 +#define DDR_DENSITY_1Gb 5 +#define DDR_DENSITY_2Gb 6 +#define DDR_DENSITY_4Gb 7 +#define DDR_DENSITY_8Gb 8 +#define DDR_DENSITY_16Gb 9 +#define DDR_DENSITY_32Gb 10 + +/* DDR type */ +#define DDR_TYPE_DDR2 1 +#define DDR_TYPE_DDR3 2 +#define DDR_TYPE_LPDDR2_S4 3 +#define DDR_TYPE_LPDDR2_S2 4 +#define DDR_TYPE_LPDDR2_NVM 5 + +/* DDR IO width */ +#define DDR_IO_WIDTH_4 1 +#define DDR_IO_WIDTH_8 2 +#define DDR_IO_WIDTH_16 3 +#define DDR_IO_WIDTH_32 4 + +/* Number of Row bits */ +#define R9 9 +#define R10 10 +#define R11 11 +#define R12 12 +#define R13 13 +#define R14 14 +#define R15 15 +#define R16 16 + +/* Number of Column bits */ +#define C7 7 +#define C8 8 +#define C9 9 +#define C10 10 +#define C11 11 +#define C12 12 + +/* Number of Banks */ +#define B1 0 +#define B2 1 +#define B4 2 +#define B8 3 + +/* Refresh rate in nano-seconds */ +#define T_REFI_15_6 15600 +#define T_REFI_7_8 7800 +#define T_REFI_3_9 3900 + +/* tRFC values */ +#define T_RFC_90 90000 +#define T_RFC_110 110000 +#define T_RFC_130 130000 +#define T_RFC_160 160000 +#define T_RFC_210 210000 +#define T_RFC_300 300000 +#define T_RFC_350 350000 + +/* Mode register numbers */ +#define DDR_MR0 0 +#define DDR_MR1 1 +#define DDR_MR2 2 +#define DDR_MR3 3 +#define DDR_MR4 4 +#define DDR_MR5 5 +#define DDR_MR6 6 +#define DDR_MR7 7 +#define DDR_MR8 8 +#define DDR_MR9 9 +#define DDR_MR10 10 +#define DDR_MR11 11 +#define DDR_MR16 16 +#define DDR_MR17 17 +#define DDR_MR18 18 + +/* + * LPDDR2 related defines + */ + +/* MR4 register fields */ +#define MR4_SDRAM_REF_RATE_SHIFT 0 +#define MR4_SDRAM_REF_RATE_MASK 7 +#define MR4_TUF_SHIFT 7 +#define MR4_TUF_MASK (1 << 7) + +/* MR4 SDRAM Refresh Rate field values */ +#define SDRAM_TEMP_NOMINAL 0x3 +#define SDRAM_TEMP_RESERVED_4 0x4 +#define SDRAM_TEMP_HIGH_DERATE_REFRESH 0x5 +#define SDRAM_TEMP_HIGH_DERATE_REFRESH_AND_TIMINGS 0x6 +#define SDRAM_TEMP_VERY_HIGH_SHUTDOWN 0x7 + +#define NUM_DDR_ADDR_TABLE_ENTRIES 11 +#define NUM_DDR_TIMING_TABLE_ENTRIES 4 + +/* Structure for DDR addressing info from the JEDEC spec */ +struct lpddr2_addressing { + u32 num_banks; + u32 tREFI_ns; + u32 tRFCab_ps; +}; + +/* + * Structure for timings from the LPDDR2 datasheet + * All parameters are in pico seconds(ps) unless explicitly indicated + * with a suffix like tRAS_max_ns below + */ +struct lpddr2_timings { + u32 max_freq; + u32 min_freq; + u32 tRPab; + u32 tRCD; + u32 tWR; + u32 tRAS_min; + u32 tRRD; + u32 tWTR; + u32 tXP; + u32 tRTP; + u32 tCKESR; + u32 tDQSCK_max; + u32 tDQSCK_max_derated; + u32 tFAW; + u32 tZQCS; + u32 tZQCL; + u32 tZQinit; + u32 tRAS_max_ns; +}; + +/* + * Min value for some parameters in terms of number of tCK cycles(nCK) + * Please set to zero parameters that are not valid for a given memory + * type + */ +struct lpddr2_min_tck { + u32 tRPab; + u32 tRCD; + u32 tWR; + u32 tRASmin; + u32 tRRD; + u32 tWTR; + u32 tXP; + u32 tRTP; + u32 tCKE; + u32 tCKESR; + u32 tFAW; +}; + +extern const struct lpddr2_addressing + lpddr2_jedec_addressing_table[NUM_DDR_ADDR_TABLE_ENTRIES]; +extern const struct lpddr2_timings + lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES]; +extern const struct lpddr2_min_tck lpddr2_jedec_min_tck; + +#endif /* __LINUX_JEDEC_DDR_H */ diff --git a/lib/Kconfig b/lib/Kconfig index 4a8aba2e5cc..0e25c03939e 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -353,6 +353,14 @@ config CORDIC This option provides an implementation of the CORDIC algorithm; calculations are in fixed point. Module will be called cordic. +config DDR + bool "JEDEC DDR data" + help + Data from JEDEC specs for DDR SDRAM memories, + particularly the AC timing parameters and addressing + information. This data is useful for drivers handling + DDR SDRAM controllers. + config MPILIB tristate select CLZ_TAB diff --git a/lib/Makefile b/lib/Makefile index 18515f0267c..74290c9e286 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -123,6 +123,8 @@ obj-$(CONFIG_SIGNATURE) += digsig.o obj-$(CONFIG_CLZ_TAB) += clz_tab.o +obj-$(CONFIG_DDR) += jedec_ddr_data.o + hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/jedec_ddr_data.c b/lib/jedec_ddr_data.c new file mode 100644 index 00000000000..6d2cbf1d567 --- /dev/null +++ b/lib/jedec_ddr_data.c @@ -0,0 +1,135 @@ +/* + * DDR addressing details and AC timing parameters from JEDEC specs + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * Aneesh V + * + * 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. + */ + +#include +#include + +/* LPDDR2 addressing details from JESD209-2 section 2.4 */ +const struct lpddr2_addressing + lpddr2_jedec_addressing_table[NUM_DDR_ADDR_TABLE_ENTRIES] = { + {B4, T_REFI_15_6, T_RFC_90}, /* 64M */ + {B4, T_REFI_15_6, T_RFC_90}, /* 128M */ + {B4, T_REFI_7_8, T_RFC_90}, /* 256M */ + {B4, T_REFI_7_8, T_RFC_90}, /* 512M */ + {B8, T_REFI_7_8, T_RFC_130}, /* 1GS4 */ + {B8, T_REFI_3_9, T_RFC_130}, /* 2GS4 */ + {B8, T_REFI_3_9, T_RFC_130}, /* 4G */ + {B8, T_REFI_3_9, T_RFC_210}, /* 8G */ + {B4, T_REFI_7_8, T_RFC_130}, /* 1GS2 */ + {B4, T_REFI_3_9, T_RFC_130}, /* 2GS2 */ +}; +EXPORT_SYMBOL_GPL(lpddr2_jedec_addressing_table); + +/* LPDDR2 AC timing parameters from JESD209-2 section 12 */ +const struct lpddr2_timings + lpddr2_jedec_timings[NUM_DDR_TIMING_TABLE_ENTRIES] = { + /* Speed bin 400(200 MHz) */ + [0] = { + .max_freq = 200000000, + .min_freq = 10000000, + .tRPab = 21000, + .tRCD = 18000, + .tWR = 15000, + .tRAS_min = 42000, + .tRRD = 10000, + .tWTR = 10000, + .tXP = 7500, + .tRTP = 7500, + .tCKESR = 15000, + .tDQSCK_max = 5500, + .tFAW = 50000, + .tZQCS = 90000, + .tZQCL = 360000, + .tZQinit = 1000000, + .tRAS_max_ns = 70000, + .tDQSCK_max_derated = 6000, + }, + /* Speed bin 533(266 MHz) */ + [1] = { + .max_freq = 266666666, + .min_freq = 10000000, + .tRPab = 21000, + .tRCD = 18000, + .tWR = 15000, + .tRAS_min = 42000, + .tRRD = 10000, + .tWTR = 7500, + .tXP = 7500, + .tRTP = 7500, + .tCKESR = 15000, + .tDQSCK_max = 5500, + .tFAW = 50000, + .tZQCS = 90000, + .tZQCL = 360000, + .tZQinit = 1000000, + .tRAS_max_ns = 70000, + .tDQSCK_max_derated = 6000, + }, + /* Speed bin 800(400 MHz) */ + [2] = { + .max_freq = 400000000, + .min_freq = 10000000, + .tRPab = 21000, + .tRCD = 18000, + .tWR = 15000, + .tRAS_min = 42000, + .tRRD = 10000, + .tWTR = 7500, + .tXP = 7500, + .tRTP = 7500, + .tCKESR = 15000, + .tDQSCK_max = 5500, + .tFAW = 50000, + .tZQCS = 90000, + .tZQCL = 360000, + .tZQinit = 1000000, + .tRAS_max_ns = 70000, + .tDQSCK_max_derated = 6000, + }, + /* Speed bin 1066(533 MHz) */ + [3] = { + .max_freq = 533333333, + .min_freq = 10000000, + .tRPab = 21000, + .tRCD = 18000, + .tWR = 15000, + .tRAS_min = 42000, + .tRRD = 10000, + .tWTR = 7500, + .tXP = 7500, + .tRTP = 7500, + .tCKESR = 15000, + .tDQSCK_max = 5500, + .tFAW = 50000, + .tZQCS = 90000, + .tZQCL = 360000, + .tZQinit = 1000000, + .tRAS_max_ns = 70000, + .tDQSCK_max_derated = 5620, + }, +}; +EXPORT_SYMBOL_GPL(lpddr2_jedec_timings); + +const struct lpddr2_min_tck lpddr2_jedec_min_tck = { + .tRPab = 3, + .tRCD = 3, + .tWR = 3, + .tRASmin = 3, + .tRRD = 2, + .tWTR = 2, + .tXP = 2, + .tRTP = 2, + .tCKE = 3, + .tCKESR = 3, + .tFAW = 8 +}; +EXPORT_SYMBOL_GPL(lpddr2_jedec_min_tck); -- cgit v1.2.3-70-g09d2 From 2922585b93294d47172a765115e0dbc1bfe1be19 Mon Sep 17 00:00:00 2001 From: "David S. Miller" Date: Thu, 24 May 2012 13:12:28 -0700 Subject: lib: Sparc's strncpy_from_user is generic enough, move under lib/ To use this, an architecture simply needs to: 1) Provide a user_addr_max() implementation via asm/uaccess.h 2) Add "select GENERIC_STRNCPY_FROM_USER" to their arch Kcnfig 3) Remove the existing strncpy_from_user() implementation and symbol exports their architecture had. Signed-off-by: David S. Miller Acked-by: David Howells --- arch/sparc/Kconfig | 1 + arch/sparc/lib/usercopy.c | 144 --------------------------------------------- lib/Kconfig | 3 + lib/Makefile | 2 + lib/strncpy_from_user.c | 146 ++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 152 insertions(+), 144 deletions(-) create mode 100644 lib/strncpy_from_user.c (limited to 'lib/Kconfig') diff --git a/arch/sparc/Kconfig b/arch/sparc/Kconfig index 051af37f2b4..22474233205 100644 --- a/arch/sparc/Kconfig +++ b/arch/sparc/Kconfig @@ -32,6 +32,7 @@ config SPARC select HAVE_NMI_WATCHDOG if SPARC64 select HAVE_BPF_JIT select GENERIC_SMP_IDLE_THREAD + select GENERIC_STRNCPY_FROM_USER config SPARC32 def_bool !64BIT diff --git a/arch/sparc/lib/usercopy.c b/arch/sparc/lib/usercopy.c index 0b12e91d6cc..5c4284ce1c0 100644 --- a/arch/sparc/lib/usercopy.c +++ b/arch/sparc/lib/usercopy.c @@ -1,153 +1,9 @@ #include -#include #include -#include #include -#include - void copy_from_user_overflow(void) { WARN(1, "Buffer overflow detected!\n"); } EXPORT_SYMBOL(copy_from_user_overflow); - -static inline long find_zero(unsigned long mask) -{ - long byte = 0; - -#ifdef __BIG_ENDIAN -#ifdef CONFIG_64BIT - if (mask >> 32) - mask >>= 32; - else - byte = 4; -#endif - if (mask >> 16) - mask >>= 16; - else - byte += 2; - return (mask >> 8) ? byte : byte + 1; -#else -#ifdef CONFIG_64BIT - if (!((unsigned int) mask)) { - mask >>= 32; - byte = 4; - } -#endif - if (!(mask & 0xffff)) { - mask >>= 16; - byte += 2; - } - return (mask & 0xff) ? byte : byte + 1; -#endif -} - -#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS -#define IS_UNALIGNED(src, dst) 0 -#else -#define IS_UNALIGNED(src, dst) \ - (((long) dst | (long) src) & (sizeof(long) - 1)) -#endif - -/* - * Do a strncpy, return length of string without final '\0'. - * 'count' is the user-supplied count (return 'count' if we - * hit it), 'max' is the address space maximum (and we return - * -EFAULT if we hit it). - */ -static inline long do_strncpy_from_user(char *dst, const char __user *src, long count, unsigned long max) -{ - const unsigned long high_bits = REPEAT_BYTE(0xfe) + 1; - const unsigned long low_bits = REPEAT_BYTE(0x7f); - long res = 0; - - /* - * Truncate 'max' to the user-specified limit, so that - * we only have one limit we need to check in the loop - */ - if (max > count) - max = count; - - if (IS_UNALIGNED(src, dst)) - goto byte_at_a_time; - - while (max >= sizeof(unsigned long)) { - unsigned long c, v, rhs; - - /* Fall back to byte-at-a-time if we get a page fault */ - if (unlikely(__get_user(c,(unsigned long __user *)(src+res)))) - break; - rhs = c | low_bits; - v = (c + high_bits) & ~rhs; - *(unsigned long *)(dst+res) = c; - if (v) { - v = (c & low_bits) + low_bits; - v = ~(v | rhs); - return res + find_zero(v); - } - res += sizeof(unsigned long); - max -= sizeof(unsigned long); - } - -byte_at_a_time: - while (max) { - char c; - - if (unlikely(__get_user(c,src+res))) - return -EFAULT; - dst[res] = c; - if (!c) - return res; - res++; - max--; - } - - /* - * Uhhuh. We hit 'max'. But was that the user-specified maximum - * too? If so, that's ok - we got as much as the user asked for. - */ - if (res >= count) - return res; - - /* - * Nope: we hit the address space limit, and we still had more - * characters the caller would have wanted. That's an EFAULT. - */ - return -EFAULT; -} - -/** - * strncpy_from_user: - Copy a NUL terminated string from userspace. - * @dst: Destination address, in kernel space. This buffer must be at - * least @count bytes long. - * @src: Source address, in user space. - * @count: Maximum number of bytes to copy, including the trailing NUL. - * - * Copies a NUL-terminated string from userspace to kernel space. - * - * On success, returns the length of the string (not including the trailing - * NUL). - * - * If access to userspace fails, returns -EFAULT (some data may have been - * copied). - * - * If @count is smaller than the length of the string, copies @count bytes - * and returns @count. - */ -long strncpy_from_user(char *dst, const char __user *src, long count) -{ - unsigned long max_addr, src_addr; - - if (unlikely(count <= 0)) - return 0; - - max_addr = user_addr_max(); - src_addr = (unsigned long)src; - if (likely(src_addr < max_addr)) { - unsigned long max = max_addr - src_addr; - return do_strncpy_from_user(dst, src, count, max); - } - return -EFAULT; -} -EXPORT_SYMBOL(strncpy_from_user); diff --git a/lib/Kconfig b/lib/Kconfig index 4a8aba2e5cc..49cb46337db 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -16,6 +16,9 @@ config BITREVERSE config RATIONAL boolean +config GENERIC_STRNCPY_FROM_USER + bool + config GENERIC_FIND_FIRST_BIT bool diff --git a/lib/Makefile b/lib/Makefile index 18515f0267c..57538b4d01d 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -123,6 +123,8 @@ obj-$(CONFIG_SIGNATURE) += digsig.o obj-$(CONFIG_CLZ_TAB) += clz_tab.o +obj-$(CONFIG_GENERIC_STRNCPY_FROM_USER) += strncpy_from_user.o + hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/strncpy_from_user.c b/lib/strncpy_from_user.c new file mode 100644 index 00000000000..c4c09b0e96b --- /dev/null +++ b/lib/strncpy_from_user.c @@ -0,0 +1,146 @@ +#include +#include +#include +#include + +#include + +static inline long find_zero(unsigned long mask) +{ + long byte = 0; + +#ifdef __BIG_ENDIAN +#ifdef CONFIG_64BIT + if (mask >> 32) + mask >>= 32; + else + byte = 4; +#endif + if (mask >> 16) + mask >>= 16; + else + byte += 2; + return (mask >> 8) ? byte : byte + 1; +#else +#ifdef CONFIG_64BIT + if (!((unsigned int) mask)) { + mask >>= 32; + byte = 4; + } +#endif + if (!(mask & 0xffff)) { + mask >>= 16; + byte += 2; + } + return (mask & 0xff) ? byte : byte + 1; +#endif +} + +#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS +#define IS_UNALIGNED(src, dst) 0 +#else +#define IS_UNALIGNED(src, dst) \ + (((long) dst | (long) src) & (sizeof(long) - 1)) +#endif + +/* + * Do a strncpy, return length of string without final '\0'. + * 'count' is the user-supplied count (return 'count' if we + * hit it), 'max' is the address space maximum (and we return + * -EFAULT if we hit it). + */ +static inline long do_strncpy_from_user(char *dst, const char __user *src, long count, unsigned long max) +{ + const unsigned long high_bits = REPEAT_BYTE(0xfe) + 1; + const unsigned long low_bits = REPEAT_BYTE(0x7f); + long res = 0; + + /* + * Truncate 'max' to the user-specified limit, so that + * we only have one limit we need to check in the loop + */ + if (max > count) + max = count; + + if (IS_UNALIGNED(src, dst)) + goto byte_at_a_time; + + while (max >= sizeof(unsigned long)) { + unsigned long c, v, rhs; + + /* Fall back to byte-at-a-time if we get a page fault */ + if (unlikely(__get_user(c,(unsigned long __user *)(src+res)))) + break; + rhs = c | low_bits; + v = (c + high_bits) & ~rhs; + *(unsigned long *)(dst+res) = c; + if (v) { + v = (c & low_bits) + low_bits; + v = ~(v | rhs); + return res + find_zero(v); + } + res += sizeof(unsigned long); + max -= sizeof(unsigned long); + } + +byte_at_a_time: + while (max) { + char c; + + if (unlikely(__get_user(c,src+res))) + return -EFAULT; + dst[res] = c; + if (!c) + return res; + res++; + max--; + } + + /* + * Uhhuh. We hit 'max'. But was that the user-specified maximum + * too? If so, that's ok - we got as much as the user asked for. + */ + if (res >= count) + return res; + + /* + * Nope: we hit the address space limit, and we still had more + * characters the caller would have wanted. That's an EFAULT. + */ + return -EFAULT; +} + +/** + * strncpy_from_user: - Copy a NUL terminated string from userspace. + * @dst: Destination address, in kernel space. This buffer must be at + * least @count bytes long. + * @src: Source address, in user space. + * @count: Maximum number of bytes to copy, including the trailing NUL. + * + * Copies a NUL-terminated string from userspace to kernel space. + * + * On success, returns the length of the string (not including the trailing + * NUL). + * + * If access to userspace fails, returns -EFAULT (some data may have been + * copied). + * + * If @count is smaller than the length of the string, copies @count bytes + * and returns @count. + */ +long strncpy_from_user(char *dst, const char __user *src, long count) +{ + unsigned long max_addr, src_addr; + + if (unlikely(count <= 0)) + return 0; + + max_addr = user_addr_max(); + src_addr = (unsigned long)src; + if (likely(src_addr < max_addr)) { + unsigned long max = max_addr - src_addr; + return do_strncpy_from_user(dst, src, count, max); + } + return -EFAULT; +} +EXPORT_SYMBOL(strncpy_from_user); -- cgit v1.2.3-70-g09d2 From a08c5356a3aaf638c41897ae4169de18db89595e Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 26 May 2012 11:06:38 -0700 Subject: lib: add generic strnlen_user() function This adds a new generic optimized strnlen_user() function that uses the infrastructure to portably do efficient string handling. In many ways, strnlen is much simpler than strncpy, and in particular we can always pre-align the words we load from memory. That means that all the worries about alignment etc are a non-issue, so this one can easily be used on any architecture. You obviously do have to do the appropriate word-at-a-time.h macros. Signed-off-by: Linus Torvalds --- lib/Kconfig | 3 ++ lib/Makefile | 1 + lib/strnlen_user.c | 138 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 142 insertions(+) create mode 100644 lib/strnlen_user.c (limited to 'lib/Kconfig') diff --git a/lib/Kconfig b/lib/Kconfig index 98230ac3db2..64ddc44d0b8 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -19,6 +19,9 @@ config RATIONAL config GENERIC_STRNCPY_FROM_USER bool +config GENERIC_STRNLEN_USER + bool + config GENERIC_FIND_FIRST_BIT bool diff --git a/lib/Makefile b/lib/Makefile index b98df505f33..77937a7dd5c 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -126,6 +126,7 @@ obj-$(CONFIG_CLZ_TAB) += clz_tab.o obj-$(CONFIG_DDR) += jedec_ddr_data.o obj-$(CONFIG_GENERIC_STRNCPY_FROM_USER) += strncpy_from_user.o +obj-$(CONFIG_GENERIC_STRNLEN_USER) += strnlen_user.o hostprogs-y := gen_crc32table clean-files := crc32table.h diff --git a/lib/strnlen_user.c b/lib/strnlen_user.c new file mode 100644 index 00000000000..90900ecfeb5 --- /dev/null +++ b/lib/strnlen_user.c @@ -0,0 +1,138 @@ +#include +#include +#include + +#include + +/* Set bits in the first 'n' bytes when loaded from memory */ +#ifdef __LITTLE_ENDIAN +# define aligned_byte_mask(n) ((1ul << 8*(n))-1) +#else +# define aligned_byte_mask(n) (~0xfful << 8*(7-(n))) +#endif + +/* + * Do a strnlen, return length of string *with* final '\0'. + * 'count' is the user-supplied count, while 'max' is the + * address space maximum. + * + * Return 0 for exceptions (which includes hitting the address + * space maximum), or 'count+1' if hitting the user-supplied + * maximum count. + * + * NOTE! We can sometimes overshoot the user-supplied maximum + * if it fits in a aligned 'long'. The caller needs to check + * the return value against "> max". + */ +static inline long do_strnlen_user(const char __user *src, unsigned long count, unsigned long max) +{ + const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS; + long align, res = 0; + unsigned long c; + + /* + * Truncate 'max' to the user-specified limit, so that + * we only have one limit we need to check in the loop + */ + if (max > count) + max = count; + + /* + * Do everything aligned. But that means that we + * need to also expand the maximum.. + */ + align = (sizeof(long) - 1) & (unsigned long)src; + src -= align; + max += align; + + if (unlikely(__get_user(c,(unsigned long __user *)src))) + return 0; + c |= aligned_byte_mask(align); + + for (;;) { + unsigned long data; + if (has_zero(c, &data, &constants)) { + data = prep_zero_mask(c, data, &constants); + data = create_zero_mask(data); + return res + find_zero(data) + 1 - align; + } + res += sizeof(unsigned long); + if (unlikely(max < sizeof(unsigned long))) + break; + max -= sizeof(unsigned long); + if (unlikely(__get_user(c,(unsigned long __user *)(src+res)))) + return 0; + } + res -= align; + + /* + * Uhhuh. We hit 'max'. But was that the user-specified maximum + * too? If so, return the marker for "too long". + */ + if (res >= count) + return count+1; + + /* + * Nope: we hit the address space limit, and we still had more + * characters the caller would have wanted. That's 0. + */ + return 0; +} + +/** + * strnlen_user: - Get the size of a user string INCLUDING final NUL. + * @str: The string to measure. + * @count: Maximum count (including NUL character) + * + * Context: User context only. This function may sleep. + * + * Get the size of a NUL-terminated string in user space. + * + * Returns the size of the string INCLUDING the terminating NUL. + * If the string is too long, returns 'count+1'. + * On exception (or invalid count), returns 0. + */ +long strnlen_user(const char __user *str, long count) +{ + unsigned long max_addr, src_addr; + + if (unlikely(count <= 0)) + return 0; + + max_addr = user_addr_max(); + src_addr = (unsigned long)str; + if (likely(src_addr < max_addr)) { + unsigned long max = max_addr - src_addr; + return do_strnlen_user(str, count, max); + } + return 0; +} +EXPORT_SYMBOL(strnlen_user); + +/** + * strlen_user: - Get the size of a user string INCLUDING final NUL. + * @str: The string to measure. + * + * Context: User context only. This function may sleep. + * + * Get the size of a NUL-terminated string in user space. + * + * Returns the size of the string INCLUDING the terminating NUL. + * On exception, returns 0. + * + * If there is a limit on the length of a valid string, you may wish to + * consider using strnlen_user() instead. + */ +long strlen_user(const char __user *str) +{ + unsigned long max_addr, src_addr; + + max_addr = user_addr_max(); + src_addr = (unsigned long)str; + if (likely(src_addr < max_addr)) { + unsigned long max = max_addr - src_addr; + return do_strnlen_user(str, ~0ul, max); + } + return 0; +} +EXPORT_SYMBOL(strlen_user); -- cgit v1.2.3-70-g09d2