diff options
Diffstat (limited to 'arch/microblaze/include/asm')
-rw-r--r-- | arch/microblaze/include/asm/cache.h | 2 | ||||
-rw-r--r-- | arch/microblaze/include/asm/dma.h | 6 | ||||
-rw-r--r-- | arch/microblaze/include/asm/exceptions.h | 6 | ||||
-rw-r--r-- | arch/microblaze/include/asm/futex.h | 2 | ||||
-rw-r--r-- | arch/microblaze/include/asm/io.h | 7 | ||||
-rw-r--r-- | arch/microblaze/include/asm/page.h | 12 | ||||
-rw-r--r-- | arch/microblaze/include/asm/pci.h | 8 | ||||
-rw-r--r-- | arch/microblaze/include/asm/pgalloc.h | 16 | ||||
-rw-r--r-- | arch/microblaze/include/asm/pgtable.h | 35 | ||||
-rw-r--r-- | arch/microblaze/include/asm/processor.h | 1 | ||||
-rw-r--r-- | arch/microblaze/include/asm/segment.h | 49 | ||||
-rw-r--r-- | arch/microblaze/include/asm/thread_info.h | 5 | ||||
-rw-r--r-- | arch/microblaze/include/asm/tlbflush.h | 3 | ||||
-rw-r--r-- | arch/microblaze/include/asm/uaccess.h | 447 |
14 files changed, 269 insertions, 330 deletions
diff --git a/arch/microblaze/include/asm/cache.h b/arch/microblaze/include/asm/cache.h index e52210891d7..4efe96a036f 100644 --- a/arch/microblaze/include/asm/cache.h +++ b/arch/microblaze/include/asm/cache.h @@ -15,7 +15,7 @@ #include <asm/registers.h> -#define L1_CACHE_SHIFT 2 +#define L1_CACHE_SHIFT 5 /* word-granular cache in microblaze */ #define L1_CACHE_BYTES (1 << L1_CACHE_SHIFT) diff --git a/arch/microblaze/include/asm/dma.h b/arch/microblaze/include/asm/dma.h index 08c073badf1..0d73d0c6de3 100644 --- a/arch/microblaze/include/asm/dma.h +++ b/arch/microblaze/include/asm/dma.h @@ -18,4 +18,10 @@ #define MAX_DMA_ADDRESS (CONFIG_KERNEL_START + memory_size - 1) #endif +#ifdef CONFIG_PCI +extern int isa_dma_bridge_buggy; +#else +#define isa_dma_bridge_buggy (0) +#endif + #endif /* _ASM_MICROBLAZE_DMA_H */ diff --git a/arch/microblaze/include/asm/exceptions.h b/arch/microblaze/include/asm/exceptions.h index 90731df9e57..4c7b5d037c8 100644 --- a/arch/microblaze/include/asm/exceptions.h +++ b/arch/microblaze/include/asm/exceptions.h @@ -64,12 +64,6 @@ asmlinkage void full_exception(struct pt_regs *regs, unsigned int type, void die(const char *str, struct pt_regs *fp, long err); void _exception(int signr, struct pt_regs *regs, int code, unsigned long addr); -#ifdef CONFIG_MMU -void __bug(const char *file, int line, void *data); -int bad_trap(int trap_num, struct pt_regs *regs); -int debug_trap(struct pt_regs *regs); -#endif /* CONFIG_MMU */ - #if defined(CONFIG_KGDB) void (*debugger)(struct pt_regs *regs); int (*debugger_bpt)(struct pt_regs *regs); diff --git a/arch/microblaze/include/asm/futex.h b/arch/microblaze/include/asm/futex.h index 8dbb6e7a03a..ad3fd61b2fe 100644 --- a/arch/microblaze/include/asm/futex.h +++ b/arch/microblaze/include/asm/futex.h @@ -55,7 +55,7 @@ futex_atomic_op_inuser(int encoded_op, int __user *uaddr) __futex_atomic_op("or %1,%0,%4;", ret, oldval, uaddr, oparg); break; case FUTEX_OP_ANDN: - __futex_atomic_op("and %1,%0,%4;", ret, oldval, uaddr, oparg); + __futex_atomic_op("andn %1,%0,%4;", ret, oldval, uaddr, oparg); break; case FUTEX_OP_XOR: __futex_atomic_op("xor %1,%0,%4;", ret, oldval, uaddr, oparg); diff --git a/arch/microblaze/include/asm/io.h b/arch/microblaze/include/asm/io.h index 32d621a56ae..00b5398d08c 100644 --- a/arch/microblaze/include/asm/io.h +++ b/arch/microblaze/include/asm/io.h @@ -108,6 +108,11 @@ static inline void writel(unsigned int v, volatile void __iomem *addr) #define iowrite16(v, addr) __raw_writew((u16)(v), (u16 *)(addr)) #define iowrite32(v, addr) __raw_writel((u32)(v), (u32 *)(addr)) +#define ioread16be(addr) __raw_readw((u16 *)(addr)) +#define ioread32be(addr) __raw_readl((u32 *)(addr)) +#define iowrite16be(v, addr) __raw_writew((u16)(v), (u16 *)(addr)) +#define iowrite32be(v, addr) __raw_writel((u32)(v), (u32 *)(addr)) + /* These are the definitions for the x86 IO instructions * inb/inw/inl/outb/outw/outl, the "string" versions * insb/insw/insl/outsb/outsw/outsl, and the "pausing" versions @@ -134,8 +139,6 @@ static inline void writel(unsigned int v, volatile void __iomem *addr) #ifdef CONFIG_MMU -#define mm_ptov(addr) ((void *)__phys_to_virt(addr)) -#define mm_vtop(addr) ((unsigned long)__virt_to_phys(addr)) #define phys_to_virt(addr) ((void *)__phys_to_virt(addr)) #define virt_to_phys(addr) ((unsigned long)__virt_to_phys(addr)) #define virt_to_bus(addr) ((unsigned long)__virt_to_phys(addr)) diff --git a/arch/microblaze/include/asm/page.h b/arch/microblaze/include/asm/page.h index 2dd1d04129e..de493f86d28 100644 --- a/arch/microblaze/include/asm/page.h +++ b/arch/microblaze/include/asm/page.h @@ -31,6 +31,9 @@ #ifndef __ASSEMBLY__ +/* MS be sure that SLAB allocates aligned objects */ +#define ARCH_KMALLOC_MINALIGN L1_CACHE_BYTES + #define PAGE_UP(addr) (((addr)+((PAGE_SIZE)-1))&(~((PAGE_SIZE)-1))) #define PAGE_DOWN(addr) ((addr)&(~((PAGE_SIZE)-1))) @@ -70,14 +73,7 @@ typedef unsigned long pte_basic_t; #endif /* CONFIG_MMU */ -# ifndef CONFIG_MMU -# define copy_page(to, from) memcpy((to), (from), PAGE_SIZE) -# define get_user_page(vaddr) __get_free_page(GFP_KERNEL) -# define free_user_page(page, addr) free_page(addr) -# else /* CONFIG_MMU */ -extern void copy_page(void *to, void *from); -# endif /* CONFIG_MMU */ - +# define copy_page(to, from) memcpy((to), (from), PAGE_SIZE) # define clear_page(pgaddr) memset((pgaddr), 0, PAGE_SIZE) # define clear_user_page(pgaddr, vaddr, page) memset((pgaddr), 0, PAGE_SIZE) diff --git a/arch/microblaze/include/asm/pci.h b/arch/microblaze/include/asm/pci.h index bdd65aaee30..5a388eeeb28 100644 --- a/arch/microblaze/include/asm/pci.h +++ b/arch/microblaze/include/asm/pci.h @@ -94,14 +94,6 @@ extern int pci_mmap_legacy_page_range(struct pci_bus *bus, #define HAVE_PCI_LEGACY 1 -/* pci_unmap_{page,single} is a nop so... */ -#define DECLARE_PCI_UNMAP_ADDR(ADDR_NAME) -#define DECLARE_PCI_UNMAP_LEN(LEN_NAME) -#define pci_unmap_addr(PTR, ADDR_NAME) (0) -#define pci_unmap_addr_set(PTR, ADDR_NAME, VAL) do { } while (0) -#define pci_unmap_len(PTR, LEN_NAME) (0) -#define pci_unmap_len_set(PTR, LEN_NAME, VAL) do { } while (0) - /* The PCI address space does equal the physical memory * address space (no IOMMU). The IDE and SCSI device layers use * this boolean for bounce buffer decisions. diff --git a/arch/microblaze/include/asm/pgalloc.h b/arch/microblaze/include/asm/pgalloc.h index f44b0d696fe..c614a893f8a 100644 --- a/arch/microblaze/include/asm/pgalloc.h +++ b/arch/microblaze/include/asm/pgalloc.h @@ -108,21 +108,7 @@ extern inline void free_pgd_slow(pgd_t *pgd) #define pmd_alloc_one_fast(mm, address) ({ BUG(); ((pmd_t *)1); }) #define pmd_alloc_one(mm, address) ({ BUG(); ((pmd_t *)2); }) -static inline pte_t *pte_alloc_one_kernel(struct mm_struct *mm, - unsigned long address) -{ - pte_t *pte; - extern void *early_get_page(void); - if (mem_init_done) { - pte = (pte_t *)__get_free_page(GFP_KERNEL | - __GFP_REPEAT | __GFP_ZERO); - } else { - pte = (pte_t *)early_get_page(); - if (pte) - clear_page(pte); - } - return pte; -} +extern pte_t *pte_alloc_one_kernel(struct mm_struct *mm, unsigned long addr); static inline struct page *pte_alloc_one(struct mm_struct *mm, unsigned long address) diff --git a/arch/microblaze/include/asm/pgtable.h b/arch/microblaze/include/asm/pgtable.h index dd2bb60651c..ca2d9287154 100644 --- a/arch/microblaze/include/asm/pgtable.h +++ b/arch/microblaze/include/asm/pgtable.h @@ -512,15 +512,6 @@ static inline pmd_t *pmd_offset(pgd_t *dir, unsigned long address) extern pgd_t swapper_pg_dir[PTRS_PER_PGD]; /* - * When flushing the tlb entry for a page, we also need to flush the hash - * table entry. flush_hash_page is assembler (for speed) in hashtable.S. - */ -extern int flush_hash_page(unsigned context, unsigned long va, pte_t *ptep); - -/* Add an HPTE to the hash table */ -extern void add_hash_page(unsigned context, unsigned long va, pte_t *ptep); - -/* * Encode and decode a swap entry. * Note that the bits we use in a PTE for representing a swap entry * must not include the _PAGE_PRESENT bit, or the _PAGE_HASHPTE bit @@ -533,15 +524,7 @@ extern void add_hash_page(unsigned context, unsigned long va, pte_t *ptep); #define __pte_to_swp_entry(pte) ((swp_entry_t) { pte_val(pte) >> 2 }) #define __swp_entry_to_pte(x) ((pte_t) { (x).val << 2 }) - -/* CONFIG_APUS */ -/* For virtual address to physical address conversion */ -extern void cache_clear(__u32 addr, int length); -extern void cache_push(__u32 addr, int length); -extern int mm_end_of_chunk(unsigned long addr, int len); extern unsigned long iopa(unsigned long addr); -/* extern unsigned long mm_ptov(unsigned long addr) \ - __attribute__ ((const)); TBD */ /* Values for nocacheflag and cmode */ /* These are not used by the APUS kernel_map, but prevents @@ -552,18 +535,6 @@ extern unsigned long iopa(unsigned long addr); #define IOMAP_NOCACHE_NONSER 2 #define IOMAP_NO_COPYBACK 3 -/* - * Map some physical address range into the kernel address space. - */ -extern unsigned long kernel_map(unsigned long paddr, unsigned long size, - int nocacheflag, unsigned long *memavailp); - -/* - * Set cache mode of (kernel space) address range. - */ -extern void kernel_set_cachemode(unsigned long address, unsigned long size, - unsigned int cmode); - /* Needs to be defined here and not in linux/mm.h, as it is arch dependent */ #define kern_addr_valid(addr) (1) @@ -577,10 +548,6 @@ extern void kernel_set_cachemode(unsigned long address, unsigned long size, void do_page_fault(struct pt_regs *regs, unsigned long address, unsigned long error_code); -void __init io_block_mapping(unsigned long virt, phys_addr_t phys, - unsigned int size, int flags); - -void __init adjust_total_lowmem(void); void mapin_ram(void); int map_page(unsigned long va, phys_addr_t pa, int flags); @@ -601,7 +568,7 @@ void __init *early_get_page(void); extern unsigned long ioremap_bot, ioremap_base; void *consistent_alloc(int gfp, size_t size, dma_addr_t *dma_handle); -void consistent_free(void *vaddr); +void consistent_free(size_t size, void *vaddr); void consistent_sync(void *vaddr, size_t size, int direction); void consistent_sync_page(struct page *page, unsigned long offset, size_t size, int direction); diff --git a/arch/microblaze/include/asm/processor.h b/arch/microblaze/include/asm/processor.h index 563c6b9453f..8eeb09211ec 100644 --- a/arch/microblaze/include/asm/processor.h +++ b/arch/microblaze/include/asm/processor.h @@ -14,7 +14,6 @@ #include <asm/ptrace.h> #include <asm/setup.h> #include <asm/registers.h> -#include <asm/segment.h> #include <asm/entry.h> #include <asm/current.h> diff --git a/arch/microblaze/include/asm/segment.h b/arch/microblaze/include/asm/segment.h deleted file mode 100644 index 0e7102c3fb1..00000000000 --- a/arch/microblaze/include/asm/segment.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2008-2009 Michal Simek <monstr@monstr.eu> - * Copyright (C) 2008-2009 PetaLogix - * Copyright (C) 2006 Atmark Techno, Inc. - * - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - */ - -#ifndef _ASM_MICROBLAZE_SEGMENT_H -#define _ASM_MICROBLAZE_SEGMENT_H - -# ifndef __ASSEMBLY__ - -typedef struct { - unsigned long seg; -} mm_segment_t; - -/* - * On Microblaze the fs value is actually the top of the corresponding - * address space. - * - * 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. - * - * For non-MMU arch like Microblaze, KERNEL_DS and USER_DS is equal. - */ -# define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) - -# ifndef CONFIG_MMU -# define KERNEL_DS MAKE_MM_SEG(0) -# define USER_DS KERNEL_DS -# else -# define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFF) -# define USER_DS MAKE_MM_SEG(TASK_SIZE - 1) -# endif - -# define get_ds() (KERNEL_DS) -# define get_fs() (current_thread_info()->addr_limit) -# define set_fs(val) (current_thread_info()->addr_limit = (val)) - -# define segment_eq(a, b) ((a).seg == (b).seg) - -# endif /* __ASSEMBLY__ */ -#endif /* _ASM_MICROBLAZE_SEGMENT_H */ diff --git a/arch/microblaze/include/asm/thread_info.h b/arch/microblaze/include/asm/thread_info.h index 6e92885d381..b2ca80f6464 100644 --- a/arch/microblaze/include/asm/thread_info.h +++ b/arch/microblaze/include/asm/thread_info.h @@ -19,7 +19,6 @@ #ifndef __ASSEMBLY__ # include <linux/types.h> # include <asm/processor.h> -# include <asm/segment.h> /* * low level task data that entry.S needs immediate access to @@ -60,6 +59,10 @@ struct cpu_context { __u32 fsr; }; +typedef struct { + unsigned long seg; +} mm_segment_t; + struct thread_info { struct task_struct *task; /* main task structure */ struct exec_domain *exec_domain; /* execution domain */ diff --git a/arch/microblaze/include/asm/tlbflush.h b/arch/microblaze/include/asm/tlbflush.h index bcb8b41d55a..2e1353c2d18 100644 --- a/arch/microblaze/include/asm/tlbflush.h +++ b/arch/microblaze/include/asm/tlbflush.h @@ -24,6 +24,7 @@ extern void _tlbie(unsigned long address); extern void _tlbia(void); #define __tlbia() { preempt_disable(); _tlbia(); preempt_enable(); } +#define __tlbie(x) { _tlbie(x); } static inline void local_flush_tlb_all(void) { __tlbia(); } @@ -31,7 +32,7 @@ static inline void local_flush_tlb_mm(struct mm_struct *mm) { __tlbia(); } static inline void local_flush_tlb_page(struct vm_area_struct *vma, unsigned long vmaddr) - { _tlbie(vmaddr); } + { __tlbie(vmaddr); } static inline void local_flush_tlb_range(struct vm_area_struct *vma, unsigned long start, unsigned long end) { __tlbia(); } diff --git a/arch/microblaze/include/asm/uaccess.h b/arch/microblaze/include/asm/uaccess.h index 371bd6e56d9..446bec29b14 100644 --- a/arch/microblaze/include/asm/uaccess.h +++ b/arch/microblaze/include/asm/uaccess.h @@ -22,101 +22,73 @@ #include <asm/mmu.h> #include <asm/page.h> #include <asm/pgtable.h> -#include <asm/segment.h> #include <linux/string.h> #define VERIFY_READ 0 #define VERIFY_WRITE 1 -#define __clear_user(addr, n) (memset((void *)(addr), 0, (n)), 0) - -#ifndef CONFIG_MMU - -extern int ___range_ok(unsigned long addr, unsigned long size); - -#define __range_ok(addr, size) \ - ___range_ok((unsigned long)(addr), (unsigned long)(size)) - -#define access_ok(type, addr, size) (__range_ok((addr), (size)) == 0) -#define __access_ok(add, size) (__range_ok((addr), (size)) == 0) - -/* Undefined function to trigger linker error */ -extern int bad_user_access_length(void); - -/* FIXME this is function for optimalization -> memcpy */ -#define __get_user(var, ptr) \ -({ \ - int __gu_err = 0; \ - switch (sizeof(*(ptr))) { \ - case 1: \ - case 2: \ - case 4: \ - (var) = *(ptr); \ - break; \ - case 8: \ - memcpy((void *) &(var), (ptr), 8); \ - break; \ - default: \ - (var) = 0; \ - __gu_err = __get_user_bad(); \ - break; \ - } \ - __gu_err; \ -}) +/* + * On Microblaze the fs value is actually the top of the corresponding + * address space. + * + * 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. + * + * For non-MMU arch like Microblaze, KERNEL_DS and USER_DS is equal. + */ +# define MAKE_MM_SEG(s) ((mm_segment_t) { (s) }) -#define __get_user_bad() (bad_user_access_length(), (-EFAULT)) +# ifndef CONFIG_MMU +# define KERNEL_DS MAKE_MM_SEG(0) +# define USER_DS KERNEL_DS +# else +# define KERNEL_DS MAKE_MM_SEG(0xFFFFFFFF) +# define USER_DS MAKE_MM_SEG(TASK_SIZE - 1) +# endif -/* FIXME is not there defined __pu_val */ -#define __put_user(var, ptr) \ -({ \ - int __pu_err = 0; \ - switch (sizeof(*(ptr))) { \ - case 1: \ - case 2: \ - case 4: \ - *(ptr) = (var); \ - break; \ - case 8: { \ - typeof(*(ptr)) __pu_val = (var); \ - memcpy(ptr, &__pu_val, sizeof(__pu_val)); \ - } \ - break; \ - default: \ - __pu_err = __put_user_bad(); \ - break; \ - } \ - __pu_err; \ -}) +# define get_ds() (KERNEL_DS) +# define get_fs() (current_thread_info()->addr_limit) +# define set_fs(val) (current_thread_info()->addr_limit = (val)) -#define __put_user_bad() (bad_user_access_length(), (-EFAULT)) +# define segment_eq(a, b) ((a).seg == (b).seg) -#define put_user(x, ptr) __put_user((x), (ptr)) -#define get_user(x, ptr) __get_user((x), (ptr)) +/* + * 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; +}; -#define copy_to_user(to, from, n) (memcpy((to), (from), (n)), 0) -#define copy_from_user(to, from, n) (memcpy((to), (from), (n)), 0) +/* Returns 0 if exception not found and fixup otherwise. */ +extern unsigned long search_exception_table(unsigned long); -#define __copy_to_user(to, from, n) (copy_to_user((to), (from), (n))) -#define __copy_from_user(to, from, n) (copy_from_user((to), (from), (n))) -#define __copy_to_user_inatomic(to, from, n) \ - (__copy_to_user((to), (from), (n))) -#define __copy_from_user_inatomic(to, from, n) \ - (__copy_from_user((to), (from), (n))) +#ifndef CONFIG_MMU -static inline unsigned long clear_user(void *addr, unsigned long size) +/* Check against bounds of physical memory */ +static inline int ___range_ok(unsigned long addr, unsigned long size) { - if (access_ok(VERIFY_WRITE, addr, size)) - size = __clear_user(addr, size); - return size; + return ((addr < memory_start) || + ((addr + size) > memory_end)); } -/* Returns 0 if exception not found and fixup otherwise. */ -extern unsigned long search_exception_table(unsigned long); +#define __range_ok(addr, size) \ + ___range_ok((unsigned long)(addr), (unsigned long)(size)) -extern long strncpy_from_user(char *dst, const char *src, long count); -extern long strnlen_user(const char *src, long count); +#define access_ok(type, addr, size) (__range_ok((addr), (size)) == 0) -#else /* CONFIG_MMU */ +#else /* * Address is valid if: @@ -129,24 +101,88 @@ extern long strnlen_user(const char *src, long count); /* || printk("access_ok failed for %s at 0x%08lx (size %d), seg 0x%08x\n", type?"WRITE":"READ",addr,size,get_fs().seg)) */ -/* - * All the __XXX versions macros/functions below do not perform - * access checking. It is assumed that the necessary checks have been - * already performed before the finction (macro) is called. - */ +#endif -#define get_user(x, ptr) \ -({ \ - access_ok(VERIFY_READ, (ptr), sizeof(*(ptr))) \ - ? __get_user((x), (ptr)) : -EFAULT; \ -}) +#ifdef CONFIG_MMU +# define __FIXUP_SECTION ".section .fixup,\"ax\"\n" +# define __EX_TABLE_SECTION ".section __ex_table,\"a\"\n" +#else +# define __FIXUP_SECTION ".section .discard,\"ax\"\n" +# define __EX_TABLE_SECTION ".section .discard,\"a\"\n" +#endif -#define put_user(x, ptr) \ -({ \ - access_ok(VERIFY_WRITE, (ptr), sizeof(*(ptr))) \ - ? __put_user((x), (ptr)) : -EFAULT; \ +extern unsigned long __copy_tofrom_user(void __user *to, + const void __user *from, unsigned long size); + +/* Return: number of not copied bytes, i.e. 0 if OK or non-zero if fail. */ +static inline unsigned long __must_check __clear_user(void __user *to, + unsigned long n) +{ + /* normal memset with two words to __ex_table */ + __asm__ __volatile__ ( \ + "1: sb r0, %2, r0;" \ + " addik %0, %0, -1;" \ + " bneid %0, 1b;" \ + " addik %2, %2, 1;" \ + "2: " \ + __EX_TABLE_SECTION \ + ".word 1b,2b;" \ + ".previous;" \ + : "=r"(n) \ + : "0"(n), "r"(to) + ); + return n; +} + +static inline unsigned long __must_check clear_user(void __user *to, + unsigned long n) +{ + might_sleep(); + if (unlikely(!access_ok(VERIFY_WRITE, to, n))) + return n; + + return __clear_user(to, n); +} + +/* put_user and get_user macros */ +extern long __user_bad(void); + +#define __get_user_asm(insn, __gu_ptr, __gu_val, __gu_err) \ +({ \ + __asm__ __volatile__ ( \ + "1:" insn " %1, %2, r0;" \ + " addk %0, r0, r0;" \ + "2: " \ + __FIXUP_SECTION \ + "3: brid 2b;" \ + " addik %0, r0, %3;" \ + ".previous;" \ + __EX_TABLE_SECTION \ + ".word 1b,3b;" \ + ".previous;" \ + : "=&r"(__gu_err), "=r"(__gu_val) \ + : "r"(__gu_ptr), "i"(-EFAULT) \ + ); \ }) +/** + * 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) \ ({ \ unsigned long __gu_val; \ @@ -163,30 +199,74 @@ extern long strnlen_user(const char *src, long count); __get_user_asm("lw", (ptr), __gu_val, __gu_err); \ break; \ default: \ - __gu_val = 0; __gu_err = -EINVAL; \ + /* __gu_val = 0; __gu_err = -EINVAL;*/ __gu_err = __user_bad();\ } \ x = (__typeof__(*(ptr))) __gu_val; \ __gu_err; \ }) -#define __get_user_asm(insn, __gu_ptr, __gu_val, __gu_err) \ + +#define get_user(x, ptr) \ ({ \ - __asm__ __volatile__ ( \ - "1:" insn " %1, %2, r0; \ - addk %0, r0, r0; \ - 2: \ - .section .fixup,\"ax\"; \ - 3: brid 2b; \ - addik %0, r0, %3; \ - .previous; \ - .section __ex_table,\"a\"; \ - .word 1b,3b; \ - .previous;" \ - : "=r"(__gu_err), "=r"(__gu_val) \ - : "r"(__gu_ptr), "i"(-EFAULT) \ - ); \ + access_ok(VERIFY_READ, (ptr), sizeof(*(ptr))) \ + ? __get_user((x), (ptr)) : -EFAULT; \ +}) + +#define __put_user_asm(insn, __gu_ptr, __gu_val, __gu_err) \ +({ \ + __asm__ __volatile__ ( \ + "1:" insn " %1, %2, r0;" \ + " addk %0, r0, r0;" \ + "2: " \ + __FIXUP_SECTION \ + "3: brid 2b;" \ + " addik %0, r0, %3;" \ + ".previous;" \ + __EX_TABLE_SECTION \ + ".word 1b,3b;" \ + ".previous;" \ + : "=&r"(__gu_err) \ + : "r"(__gu_val), "r"(__gu_ptr), "i"(-EFAULT) \ + ); \ }) +#define __put_user_asm_8(__gu_ptr, __gu_val, __gu_err) \ +({ \ + __asm__ __volatile__ (" lwi %0, %1, 0;" \ + "1: swi %0, %2, 0;" \ + " lwi %0, %1, 4;" \ + "2: swi %0, %2, 4;" \ + " addk %0, r0, r0;" \ + "3: " \ + __FIXUP_SECTION \ + "4: brid 3b;" \ + " addik %0, r0, %3;" \ + ".previous;" \ + __EX_TABLE_SECTION \ + ".word 1b,4b,2b,4b;" \ + ".previous;" \ + : "=&r"(__gu_err) \ + : "r"(&__gu_val), "r"(__gu_ptr), "i"(-EFAULT) \ + ); \ +}) + +/** + * 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)) volatile __gu_val = (x); \ @@ -195,7 +275,7 @@ extern long strnlen_user(const char *src, long count); case 1: \ __put_user_asm("sb", (ptr), __gu_val, __gu_err); \ break; \ - case 2: \ + case 2: \ __put_user_asm("sh", (ptr), __gu_val, __gu_err); \ break; \ case 4: \ @@ -205,121 +285,82 @@ extern long strnlen_user(const char *src, long count); __put_user_asm_8((ptr), __gu_val, __gu_err); \ break; \ default: \ - __gu_err = -EINVAL; \ + /*__gu_err = -EINVAL;*/ __gu_err = __user_bad(); \ } \ __gu_err; \ }) -#define __put_user_asm_8(__gu_ptr, __gu_val, __gu_err) \ -({ \ -__asm__ __volatile__ (" lwi %0, %1, 0; \ - 1: swi %0, %2, 0; \ - lwi %0, %1, 4; \ - 2: swi %0, %2, 4; \ - addk %0,r0,r0; \ - 3: \ - .section .fixup,\"ax\"; \ - 4: brid 3b; \ - addik %0, r0, %3; \ - .previous; \ - .section __ex_table,\"a\"; \ - .word 1b,4b,2b,4b; \ - .previous;" \ - : "=&r"(__gu_err) \ - : "r"(&__gu_val), \ - "r"(__gu_ptr), "i"(-EFAULT) \ - ); \ -}) +#ifndef CONFIG_MMU -#define __put_user_asm(insn, __gu_ptr, __gu_val, __gu_err) \ -({ \ - __asm__ __volatile__ ( \ - "1:" insn " %1, %2, r0; \ - addk %0, r0, r0; \ - 2: \ - .section .fixup,\"ax\"; \ - 3: brid 2b; \ - addik %0, r0, %3; \ - .previous; \ - .section __ex_table,\"a\"; \ - .word 1b,3b; \ - .previous;" \ - : "=r"(__gu_err) \ - : "r"(__gu_val), "r"(__gu_ptr), "i"(-EFAULT) \ - ); \ -}) +#define put_user(x, ptr) __put_user((x), (ptr)) -/* - * Return: number of not copied bytes, i.e. 0 if OK or non-zero if fail. - */ -static inline int clear_user(char *to, int size) -{ - if (size && access_ok(VERIFY_WRITE, to, size)) { - __asm__ __volatile__ (" \ - 1: \ - sb r0, %2, r0; \ - addik %0, %0, -1; \ - bneid %0, 1b; \ - addik %2, %2, 1; \ - 2: \ - .section __ex_table,\"a\"; \ - .word 1b,2b; \ - .section .text;" \ - : "=r"(size) \ - : "0"(size), "r"(to) - ); - } - return size; -} +#else /* CONFIG_MMU */ -#define __copy_from_user(to, from, n) copy_from_user((to), (from), (n)) +#define put_user(x, ptr) \ +({ \ + access_ok(VERIFY_WRITE, (ptr), sizeof(*(ptr))) \ + ? __put_user((x), (ptr)) : -EFAULT; \ +}) +#endif /* CONFIG_MMU */ + +/* copy_to_from_user */ +#define __copy_from_user(to, from, n) \ + __copy_tofrom_user((__force void __user *)(to), \ + (void __user *)(from), (n)) #define __copy_from_user_inatomic(to, from, n) \ copy_from_user((to), (from), (n)) -#define copy_to_user(to, from, n) \ - (access_ok(VERIFY_WRITE, (to), (n)) ? \ - __copy_tofrom_user((void __user *)(to), \ - (__force const void __user *)(from), (n)) \ - : -EFAULT) +static inline long copy_from_user(void *to, + const void __user *from, unsigned long n) +{ + might_sleep(); + if (access_ok(VERIFY_READ, from, n)) + return __copy_from_user(to, from, n); + return n; +} -#define __copy_to_user(to, from, n) copy_to_user((to), (from), (n)) +#define __copy_to_user(to, from, n) \ + __copy_tofrom_user((void __user *)(to), \ + (__force const void __user *)(from), (n)) #define __copy_to_user_inatomic(to, from, n) copy_to_user((to), (from), (n)) -#define copy_from_user(to, from, n) \ - (access_ok(VERIFY_READ, (from), (n)) ? \ - __copy_tofrom_user((__force void __user *)(to), \ - (void __user *)(from), (n)) \ - : -EFAULT) +static inline long copy_to_user(void __user *to, + const void *from, unsigned long n) +{ + might_sleep(); + if (access_ok(VERIFY_WRITE, to, n)) + return __copy_to_user(to, from, n); + return n; +} +/* + * Copy a null terminated string from userspace. + */ extern int __strncpy_user(char *to, const char __user *from, int len); -extern int __strnlen_user(const char __user *sstr, int len); -#define strncpy_from_user(to, from, len) \ - (access_ok(VERIFY_READ, from, 1) ? \ - __strncpy_user(to, from, len) : -EFAULT) -#define strnlen_user(str, len) \ - (access_ok(VERIFY_READ, str, 1) ? __strnlen_user(str, len) : 0) +#define __strncpy_from_user __strncpy_user -#endif /* CONFIG_MMU */ - -extern unsigned long __copy_tofrom_user(void __user *to, - const void __user *from, unsigned long size); +static inline long +strncpy_from_user(char *dst, const char __user *src, long count) +{ + if (!access_ok(VERIFY_READ, src, 1)) + return -EFAULT; + return __strncpy_from_user(dst, src, count); +} /* - * 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. + * Return the size of a string (including the ending 0) * - * 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. + * Return 0 on exception, a value greater than N if too long */ -struct exception_table_entry { - unsigned long insn, fixup; -}; +extern int __strnlen_user(const char __user *sstr, int len); + +static inline long strnlen_user(const char __user *src, long n) +{ + if (!access_ok(VERIFY_READ, src, 1)) + return 0; + return __strnlen_user(src, n); +} #endif /* __ASSEMBLY__ */ #endif /* __KERNEL__ */ |