diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-13 21:49:02 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-10-13 21:49:02 -0400 |
commit | 31003e3a9df675f1ac85d7bcf8e5a5d622576375 (patch) | |
tree | 6b08171deca1bf3cdcd6968ce0859e505048c73a /arch | |
parent | 1ee07ef6b5db7235b133ee257a3adf507697e6b3 (diff) | |
parent | 5f786595a291092d20fafe10c5a30378971a8cc3 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml
Pull UML update from Richard Weinberger:
"Besides of fixes this contains also support for CONFIG_STACKTRACE by
Daniel Walter"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/rw/uml:
um: net: Eliminate NULL test after alloc_bootmem
um: Add support for CONFIG_STACKTRACE
um: ubd: Fix for processes stuck in D state forever
um: delete unnecessary bootmem struct page array
um: remove csum_partial_copy_generic_i386 to clean up exception table
Diffstat (limited to 'arch')
-rw-r--r-- | arch/um/Kconfig.common | 3 | ||||
-rw-r--r-- | arch/um/drivers/net_kern.c | 4 | ||||
-rw-r--r-- | arch/um/drivers/ubd_kern.c | 5 | ||||
-rw-r--r-- | arch/um/include/asm/stacktrace.h | 42 | ||||
-rw-r--r-- | arch/um/include/shared/mem_user.h | 2 | ||||
-rw-r--r-- | arch/um/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/um/kernel/physmem.c | 32 | ||||
-rw-r--r-- | arch/um/kernel/stacktrace.c | 80 | ||||
-rw-r--r-- | arch/um/kernel/sysrq.c | 69 | ||||
-rw-r--r-- | arch/um/kernel/um_arch.c | 7 | ||||
-rw-r--r-- | arch/x86/um/checksum_32.S | 239 |
11 files changed, 153 insertions, 331 deletions
diff --git a/arch/um/Kconfig.common b/arch/um/Kconfig.common index 6915d28cf118..87bc86821bc9 100644 --- a/arch/um/Kconfig.common +++ b/arch/um/Kconfig.common | |||
@@ -39,7 +39,8 @@ config LOCKDEP_SUPPORT | |||
39 | 39 | ||
40 | config STACKTRACE_SUPPORT | 40 | config STACKTRACE_SUPPORT |
41 | bool | 41 | bool |
42 | default n | 42 | default y |
43 | select STACKTRACE | ||
43 | 44 | ||
44 | config GENERIC_CALIBRATE_DELAY | 45 | config GENERIC_CALIBRATE_DELAY |
45 | bool | 46 | bool |
diff --git a/arch/um/drivers/net_kern.c b/arch/um/drivers/net_kern.c index 7d26d9c0b2fb..f70dd540655d 100644 --- a/arch/um/drivers/net_kern.c +++ b/arch/um/drivers/net_kern.c | |||
@@ -659,10 +659,6 @@ static int __init eth_setup(char *str) | |||
659 | } | 659 | } |
660 | 660 | ||
661 | new = alloc_bootmem(sizeof(*new)); | 661 | new = alloc_bootmem(sizeof(*new)); |
662 | if (new == NULL) { | ||
663 | printk(KERN_ERR "eth_init : alloc_bootmem failed\n"); | ||
664 | return 1; | ||
665 | } | ||
666 | 662 | ||
667 | INIT_LIST_HEAD(&new->list); | 663 | INIT_LIST_HEAD(&new->list); |
668 | new->index = n; | 664 | new->index = n; |
diff --git a/arch/um/drivers/ubd_kern.c b/arch/um/drivers/ubd_kern.c index 3716e6952554..e8ab93c3e638 100644 --- a/arch/um/drivers/ubd_kern.c +++ b/arch/um/drivers/ubd_kern.c | |||
@@ -1277,7 +1277,7 @@ static void do_ubd_request(struct request_queue *q) | |||
1277 | 1277 | ||
1278 | while(1){ | 1278 | while(1){ |
1279 | struct ubd *dev = q->queuedata; | 1279 | struct ubd *dev = q->queuedata; |
1280 | if(dev->end_sg == 0){ | 1280 | if(dev->request == NULL){ |
1281 | struct request *req = blk_fetch_request(q); | 1281 | struct request *req = blk_fetch_request(q); |
1282 | if(req == NULL) | 1282 | if(req == NULL) |
1283 | return; | 1283 | return; |
@@ -1299,7 +1299,8 @@ static void do_ubd_request(struct request_queue *q) | |||
1299 | return; | 1299 | return; |
1300 | } | 1300 | } |
1301 | prepare_flush_request(req, io_req); | 1301 | prepare_flush_request(req, io_req); |
1302 | submit_request(io_req, dev); | 1302 | if (submit_request(io_req, dev) == false) |
1303 | return; | ||
1303 | } | 1304 | } |
1304 | 1305 | ||
1305 | while(dev->start_sg < dev->end_sg){ | 1306 | while(dev->start_sg < dev->end_sg){ |
diff --git a/arch/um/include/asm/stacktrace.h b/arch/um/include/asm/stacktrace.h new file mode 100644 index 000000000000..9a864328c67f --- /dev/null +++ b/arch/um/include/asm/stacktrace.h | |||
@@ -0,0 +1,42 @@ | |||
1 | #ifndef _ASM_UML_STACKTRACE_H | ||
2 | #define _ASM_UML_STACKTRACE_H | ||
3 | |||
4 | #include <linux/uaccess.h> | ||
5 | #include <linux/ptrace.h> | ||
6 | |||
7 | struct stack_frame { | ||
8 | struct stack_frame *next_frame; | ||
9 | unsigned long return_address; | ||
10 | }; | ||
11 | |||
12 | struct stacktrace_ops { | ||
13 | void (*address)(void *data, unsigned long address, int reliable); | ||
14 | }; | ||
15 | |||
16 | #ifdef CONFIG_FRAME_POINTER | ||
17 | static inline unsigned long | ||
18 | get_frame_pointer(struct task_struct *task, struct pt_regs *segv_regs) | ||
19 | { | ||
20 | if (!task || task == current) | ||
21 | return segv_regs ? PT_REGS_BP(segv_regs) : current_bp(); | ||
22 | return KSTK_EBP(task); | ||
23 | } | ||
24 | #else | ||
25 | static inline unsigned long | ||
26 | get_frame_pointer(struct task_struct *task, struct pt_regs *segv_regs) | ||
27 | { | ||
28 | return 0; | ||
29 | } | ||
30 | #endif | ||
31 | |||
32 | static inline unsigned long | ||
33 | *get_stack_pointer(struct task_struct *task, struct pt_regs *segv_regs) | ||
34 | { | ||
35 | if (!task || task == current) | ||
36 | return segv_regs ? (unsigned long *)PT_REGS_SP(segv_regs) : current_sp(); | ||
37 | return (unsigned long *)KSTK_ESP(task); | ||
38 | } | ||
39 | |||
40 | void dump_trace(struct task_struct *tsk, const struct stacktrace_ops *ops, void *data); | ||
41 | |||
42 | #endif /* _ASM_UML_STACKTRACE_H */ | ||
diff --git a/arch/um/include/shared/mem_user.h b/arch/um/include/shared/mem_user.h index 46384acd547b..cb84414e3e66 100644 --- a/arch/um/include/shared/mem_user.h +++ b/arch/um/include/shared/mem_user.h | |||
@@ -49,7 +49,7 @@ extern int iomem_size; | |||
49 | extern int init_mem_user(void); | 49 | extern int init_mem_user(void); |
50 | extern void setup_memory(void *entry); | 50 | extern void setup_memory(void *entry); |
51 | extern unsigned long find_iomem(char *driver, unsigned long *len_out); | 51 | extern unsigned long find_iomem(char *driver, unsigned long *len_out); |
52 | extern int init_maps(unsigned long physmem, unsigned long iomem, | 52 | extern void mem_total_pages(unsigned long physmem, unsigned long iomem, |
53 | unsigned long highmem); | 53 | unsigned long highmem); |
54 | extern unsigned long get_vm(unsigned long len); | 54 | extern unsigned long get_vm(unsigned long len); |
55 | extern void setup_physmem(unsigned long start, unsigned long usable, | 55 | extern void setup_physmem(unsigned long start, unsigned long usable, |
diff --git a/arch/um/kernel/Makefile b/arch/um/kernel/Makefile index d8b78a03855c..2d840a070c8b 100644 --- a/arch/um/kernel/Makefile +++ b/arch/um/kernel/Makefile | |||
@@ -19,6 +19,7 @@ obj-$(CONFIG_BLK_DEV_INITRD) += initrd.o | |||
19 | obj-$(CONFIG_GPROF) += gprof_syms.o | 19 | obj-$(CONFIG_GPROF) += gprof_syms.o |
20 | obj-$(CONFIG_GCOV) += gmon_syms.o | 20 | obj-$(CONFIG_GCOV) += gmon_syms.o |
21 | obj-$(CONFIG_EARLY_PRINTK) += early_printk.o | 21 | obj-$(CONFIG_EARLY_PRINTK) += early_printk.o |
22 | obj-$(CONFIG_STACKTRACE) += stacktrace.o | ||
22 | 23 | ||
23 | USER_OBJS := config.o | 24 | USER_OBJS := config.o |
24 | 25 | ||
diff --git a/arch/um/kernel/physmem.c b/arch/um/kernel/physmem.c index 30fdd5d0067b..549ecf3f5857 100644 --- a/arch/um/kernel/physmem.c +++ b/arch/um/kernel/physmem.c | |||
@@ -22,39 +22,19 @@ EXPORT_SYMBOL(high_physmem); | |||
22 | 22 | ||
23 | extern unsigned long long physmem_size; | 23 | extern unsigned long long physmem_size; |
24 | 24 | ||
25 | int __init init_maps(unsigned long physmem, unsigned long iomem, | 25 | void __init mem_total_pages(unsigned long physmem, unsigned long iomem, |
26 | unsigned long highmem) | 26 | unsigned long highmem) |
27 | { | 27 | { |
28 | struct page *p, *map; | 28 | unsigned long phys_pages, highmem_pages; |
29 | unsigned long phys_len, phys_pages, highmem_len, highmem_pages; | 29 | unsigned long iomem_pages, total_pages; |
30 | unsigned long iomem_len, iomem_pages, total_len, total_pages; | ||
31 | int i; | ||
32 | |||
33 | phys_pages = physmem >> PAGE_SHIFT; | ||
34 | phys_len = phys_pages * sizeof(struct page); | ||
35 | |||
36 | iomem_pages = iomem >> PAGE_SHIFT; | ||
37 | iomem_len = iomem_pages * sizeof(struct page); | ||
38 | 30 | ||
31 | phys_pages = physmem >> PAGE_SHIFT; | ||
32 | iomem_pages = iomem >> PAGE_SHIFT; | ||
39 | highmem_pages = highmem >> PAGE_SHIFT; | 33 | highmem_pages = highmem >> PAGE_SHIFT; |
40 | highmem_len = highmem_pages * sizeof(struct page); | ||
41 | |||
42 | total_pages = phys_pages + iomem_pages + highmem_pages; | ||
43 | total_len = phys_len + iomem_len + highmem_len; | ||
44 | 34 | ||
45 | map = alloc_bootmem_low_pages(total_len); | 35 | total_pages = phys_pages + iomem_pages + highmem_pages; |
46 | if (map == NULL) | ||
47 | return -ENOMEM; | ||
48 | |||
49 | for (i = 0; i < total_pages; i++) { | ||
50 | p = &map[i]; | ||
51 | memset(p, 0, sizeof(struct page)); | ||
52 | SetPageReserved(p); | ||
53 | INIT_LIST_HEAD(&p->lru); | ||
54 | } | ||
55 | 36 | ||
56 | max_mapnr = total_pages; | 37 | max_mapnr = total_pages; |
57 | return 0; | ||
58 | } | 38 | } |
59 | 39 | ||
60 | void map_memory(unsigned long virt, unsigned long phys, unsigned long len, | 40 | void map_memory(unsigned long virt, unsigned long phys, unsigned long len, |
diff --git a/arch/um/kernel/stacktrace.c b/arch/um/kernel/stacktrace.c new file mode 100644 index 000000000000..ebe7bcf62684 --- /dev/null +++ b/arch/um/kernel/stacktrace.c | |||
@@ -0,0 +1,80 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2001 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com) | ||
3 | * Copyright (C) 2013 Richard Weinberger <richard@nod.at> | ||
4 | * Copyright (C) 2014 Google Inc., Author: Daniel Walter <dwalter@google.com> | ||
5 | * | ||
6 | * This program is free software; you can redistribute it and/or modify | ||
7 | * it under the terms of the GNU General Public License version 2 as | ||
8 | * published by the Free Software Foundation. | ||
9 | */ | ||
10 | |||
11 | #include <linux/kallsyms.h> | ||
12 | #include <linux/kernel.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/stacktrace.h> | ||
15 | #include <linux/module.h> | ||
16 | #include <linux/uaccess.h> | ||
17 | #include <asm/stacktrace.h> | ||
18 | |||
19 | void dump_trace(struct task_struct *tsk, | ||
20 | const struct stacktrace_ops *ops, | ||
21 | void *data) | ||
22 | { | ||
23 | int reliable = 0; | ||
24 | unsigned long *sp, bp, addr; | ||
25 | struct pt_regs *segv_regs = tsk->thread.segv_regs; | ||
26 | struct stack_frame *frame; | ||
27 | |||
28 | bp = get_frame_pointer(tsk, segv_regs); | ||
29 | sp = get_stack_pointer(tsk, segv_regs); | ||
30 | |||
31 | frame = (struct stack_frame *)bp; | ||
32 | while (((long) sp & (THREAD_SIZE-1)) != 0) { | ||
33 | addr = *sp; | ||
34 | if (__kernel_text_address(addr)) { | ||
35 | reliable = 0; | ||
36 | if ((unsigned long) sp == bp + sizeof(long)) { | ||
37 | frame = frame ? frame->next_frame : NULL; | ||
38 | bp = (unsigned long)frame; | ||
39 | reliable = 1; | ||
40 | } | ||
41 | ops->address(data, addr, reliable); | ||
42 | } | ||
43 | sp++; | ||
44 | } | ||
45 | } | ||
46 | |||
47 | static void save_addr(void *data, unsigned long address, int reliable) | ||
48 | { | ||
49 | struct stack_trace *trace = data; | ||
50 | |||
51 | if (!reliable) | ||
52 | return; | ||
53 | if (trace->nr_entries >= trace->max_entries) | ||
54 | return; | ||
55 | |||
56 | trace->entries[trace->nr_entries++] = address; | ||
57 | } | ||
58 | |||
59 | static const struct stacktrace_ops dump_ops = { | ||
60 | .address = save_addr | ||
61 | }; | ||
62 | |||
63 | static void __save_stack_trace(struct task_struct *tsk, struct stack_trace *trace) | ||
64 | { | ||
65 | dump_trace(tsk, &dump_ops, trace); | ||
66 | if (trace->nr_entries < trace->max_entries) | ||
67 | trace->entries[trace->nr_entries++] = ULONG_MAX; | ||
68 | } | ||
69 | |||
70 | void save_stack_trace(struct stack_trace *trace) | ||
71 | { | ||
72 | __save_stack_trace(current, trace); | ||
73 | } | ||
74 | EXPORT_SYMBOL_GPL(save_stack_trace); | ||
75 | |||
76 | void save_stack_trace_tsk(struct task_struct *tsk, struct stack_trace *trace) | ||
77 | { | ||
78 | __save_stack_trace(tsk, trace); | ||
79 | } | ||
80 | EXPORT_SYMBOL_GPL(save_stack_trace_tsk); | ||
diff --git a/arch/um/kernel/sysrq.c b/arch/um/kernel/sysrq.c index 799d7e413bf5..894c8d303cda 100644 --- a/arch/um/kernel/sysrq.c +++ b/arch/um/kernel/sysrq.c | |||
@@ -12,57 +12,20 @@ | |||
12 | #include <linux/module.h> | 12 | #include <linux/module.h> |
13 | #include <linux/sched.h> | 13 | #include <linux/sched.h> |
14 | #include <asm/sysrq.h> | 14 | #include <asm/sysrq.h> |
15 | #include <asm/stacktrace.h> | ||
15 | #include <os.h> | 16 | #include <os.h> |
16 | 17 | ||
17 | struct stack_frame { | 18 | static void _print_addr(void *data, unsigned long address, int reliable) |
18 | struct stack_frame *next_frame; | ||
19 | unsigned long return_address; | ||
20 | }; | ||
21 | |||
22 | static void do_stack_trace(unsigned long *sp, unsigned long bp) | ||
23 | { | 19 | { |
24 | int reliable; | 20 | pr_info(" [<%08lx>]", address); |
25 | unsigned long addr; | 21 | pr_cont(" %s", reliable ? "" : "? "); |
26 | struct stack_frame *frame = (struct stack_frame *)bp; | 22 | print_symbol("%s", address); |
27 | 23 | pr_cont("\n"); | |
28 | printk(KERN_INFO "Call Trace:\n"); | ||
29 | while (((long) sp & (THREAD_SIZE-1)) != 0) { | ||
30 | addr = *sp; | ||
31 | if (__kernel_text_address(addr)) { | ||
32 | reliable = 0; | ||
33 | if ((unsigned long) sp == bp + sizeof(long)) { | ||
34 | frame = frame ? frame->next_frame : NULL; | ||
35 | bp = (unsigned long)frame; | ||
36 | reliable = 1; | ||
37 | } | ||
38 | |||
39 | printk(KERN_INFO " [<%08lx>]", addr); | ||
40 | printk(KERN_CONT " %s", reliable ? "" : "? "); | ||
41 | print_symbol(KERN_CONT "%s", addr); | ||
42 | printk(KERN_CONT "\n"); | ||
43 | } | ||
44 | sp++; | ||
45 | } | ||
46 | printk(KERN_INFO "\n"); | ||
47 | } | 24 | } |
48 | 25 | ||
49 | static unsigned long get_frame_pointer(struct task_struct *task, | 26 | static const struct stacktrace_ops stackops = { |
50 | struct pt_regs *segv_regs) | 27 | .address = _print_addr |
51 | { | 28 | }; |
52 | if (!task || task == current) | ||
53 | return segv_regs ? PT_REGS_BP(segv_regs) : current_bp(); | ||
54 | else | ||
55 | return KSTK_EBP(task); | ||
56 | } | ||
57 | |||
58 | static unsigned long *get_stack_pointer(struct task_struct *task, | ||
59 | struct pt_regs *segv_regs) | ||
60 | { | ||
61 | if (!task || task == current) | ||
62 | return segv_regs ? (unsigned long *)PT_REGS_SP(segv_regs) : current_sp(); | ||
63 | else | ||
64 | return (unsigned long *)KSTK_ESP(task); | ||
65 | } | ||
66 | 29 | ||
67 | void show_stack(struct task_struct *task, unsigned long *stack) | 30 | void show_stack(struct task_struct *task, unsigned long *stack) |
68 | { | 31 | { |
@@ -71,7 +34,7 @@ void show_stack(struct task_struct *task, unsigned long *stack) | |||
71 | int i; | 34 | int i; |
72 | 35 | ||
73 | if (!segv_regs && os_is_signal_stack()) { | 36 | if (!segv_regs && os_is_signal_stack()) { |
74 | printk(KERN_ERR "Received SIGSEGV in SIGSEGV handler," | 37 | pr_err("Received SIGSEGV in SIGSEGV handler," |
75 | " aborting stack trace!\n"); | 38 | " aborting stack trace!\n"); |
76 | return; | 39 | return; |
77 | } | 40 | } |
@@ -83,16 +46,18 @@ void show_stack(struct task_struct *task, unsigned long *stack) | |||
83 | if (!stack) | 46 | if (!stack) |
84 | sp = get_stack_pointer(task, segv_regs); | 47 | sp = get_stack_pointer(task, segv_regs); |
85 | 48 | ||
86 | printk(KERN_INFO "Stack:\n"); | 49 | pr_info("Stack:\n"); |
87 | stack = sp; | 50 | stack = sp; |
88 | for (i = 0; i < 3 * STACKSLOTS_PER_LINE; i++) { | 51 | for (i = 0; i < 3 * STACKSLOTS_PER_LINE; i++) { |
89 | if (kstack_end(stack)) | 52 | if (kstack_end(stack)) |
90 | break; | 53 | break; |
91 | if (i && ((i % STACKSLOTS_PER_LINE) == 0)) | 54 | if (i && ((i % STACKSLOTS_PER_LINE) == 0)) |
92 | printk(KERN_CONT "\n"); | 55 | pr_cont("\n"); |
93 | printk(KERN_CONT " %08lx", *stack++); | 56 | pr_cont(" %08lx", *stack++); |
94 | } | 57 | } |
95 | printk(KERN_CONT "\n"); | 58 | pr_cont("\n"); |
96 | 59 | ||
97 | do_stack_trace(sp, bp); | 60 | pr_info("Call Trace:\n"); |
61 | dump_trace(current, &stackops, NULL); | ||
62 | pr_info("\n"); | ||
98 | } | 63 | } |
diff --git a/arch/um/kernel/um_arch.c b/arch/um/kernel/um_arch.c index 016adf0985d5..9274eae6ae7b 100644 --- a/arch/um/kernel/um_arch.c +++ b/arch/um/kernel/um_arch.c | |||
@@ -348,12 +348,7 @@ int __init linux_main(int argc, char **argv) | |||
348 | start_vm = VMALLOC_START; | 348 | start_vm = VMALLOC_START; |
349 | 349 | ||
350 | setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem); | 350 | setup_physmem(uml_physmem, uml_reserved, physmem_size, highmem); |
351 | if (init_maps(physmem_size, iomem_size, highmem)) { | 351 | mem_total_pages(physmem_size, iomem_size, highmem); |
352 | printf("Failed to allocate mem_map for %Lu bytes of physical " | ||
353 | "memory and %Lu bytes of highmem\n", physmem_size, | ||
354 | highmem); | ||
355 | exit(1); | ||
356 | } | ||
357 | 352 | ||
358 | virtmem_size = physmem_size; | 353 | virtmem_size = physmem_size; |
359 | stack = (unsigned long) argv; | 354 | stack = (unsigned long) argv; |
diff --git a/arch/x86/um/checksum_32.S b/arch/x86/um/checksum_32.S index 8d0c420465cc..fa4b8b9841ff 100644 --- a/arch/x86/um/checksum_32.S +++ b/arch/x86/um/checksum_32.S | |||
@@ -214,242 +214,3 @@ csum_partial: | |||
214 | ret | 214 | ret |
215 | 215 | ||
216 | #endif | 216 | #endif |
217 | |||
218 | /* | ||
219 | unsigned int csum_partial_copy_generic (const char *src, char *dst, | ||
220 | int len, int sum, int *src_err_ptr, int *dst_err_ptr) | ||
221 | */ | ||
222 | |||
223 | /* | ||
224 | * Copy from ds while checksumming, otherwise like csum_partial | ||
225 | * | ||
226 | * The macros SRC and DST specify the type of access for the instruction. | ||
227 | * thus we can call a custom exception handler for all access types. | ||
228 | * | ||
229 | * FIXME: could someone double-check whether I haven't mixed up some SRC and | ||
230 | * DST definitions? It's damn hard to trigger all cases. I hope I got | ||
231 | * them all but there's no guarantee. | ||
232 | */ | ||
233 | |||
234 | #define SRC(y...) \ | ||
235 | 9999: y; \ | ||
236 | _ASM_EXTABLE(9999b, 6001f) | ||
237 | |||
238 | #define DST(y...) \ | ||
239 | 9999: y; \ | ||
240 | _ASM_EXTABLE(9999b, 6002f) | ||
241 | |||
242 | .align 4 | ||
243 | |||
244 | #ifndef CONFIG_X86_USE_PPRO_CHECKSUM | ||
245 | |||
246 | #define ARGBASE 16 | ||
247 | #define FP 12 | ||
248 | |||
249 | csum_partial_copy_generic_i386: | ||
250 | subl $4,%esp | ||
251 | pushl %edi | ||
252 | pushl %esi | ||
253 | pushl %ebx | ||
254 | movl ARGBASE+16(%esp),%eax # sum | ||
255 | movl ARGBASE+12(%esp),%ecx # len | ||
256 | movl ARGBASE+4(%esp),%esi # src | ||
257 | movl ARGBASE+8(%esp),%edi # dst | ||
258 | |||
259 | testl $2, %edi # Check alignment. | ||
260 | jz 2f # Jump if alignment is ok. | ||
261 | subl $2, %ecx # Alignment uses up two bytes. | ||
262 | jae 1f # Jump if we had at least two bytes. | ||
263 | addl $2, %ecx # ecx was < 2. Deal with it. | ||
264 | jmp 4f | ||
265 | SRC(1: movw (%esi), %bx ) | ||
266 | addl $2, %esi | ||
267 | DST( movw %bx, (%edi) ) | ||
268 | addl $2, %edi | ||
269 | addw %bx, %ax | ||
270 | adcl $0, %eax | ||
271 | 2: | ||
272 | movl %ecx, FP(%esp) | ||
273 | shrl $5, %ecx | ||
274 | jz 2f | ||
275 | testl %esi, %esi | ||
276 | SRC(1: movl (%esi), %ebx ) | ||
277 | SRC( movl 4(%esi), %edx ) | ||
278 | adcl %ebx, %eax | ||
279 | DST( movl %ebx, (%edi) ) | ||
280 | adcl %edx, %eax | ||
281 | DST( movl %edx, 4(%edi) ) | ||
282 | |||
283 | SRC( movl 8(%esi), %ebx ) | ||
284 | SRC( movl 12(%esi), %edx ) | ||
285 | adcl %ebx, %eax | ||
286 | DST( movl %ebx, 8(%edi) ) | ||
287 | adcl %edx, %eax | ||
288 | DST( movl %edx, 12(%edi) ) | ||
289 | |||
290 | SRC( movl 16(%esi), %ebx ) | ||
291 | SRC( movl 20(%esi), %edx ) | ||
292 | adcl %ebx, %eax | ||
293 | DST( movl %ebx, 16(%edi) ) | ||
294 | adcl %edx, %eax | ||
295 | DST( movl %edx, 20(%edi) ) | ||
296 | |||
297 | SRC( movl 24(%esi), %ebx ) | ||
298 | SRC( movl 28(%esi), %edx ) | ||
299 | adcl %ebx, %eax | ||
300 | DST( movl %ebx, 24(%edi) ) | ||
301 | adcl %edx, %eax | ||
302 | DST( movl %edx, 28(%edi) ) | ||
303 | |||
304 | lea 32(%esi), %esi | ||
305 | lea 32(%edi), %edi | ||
306 | dec %ecx | ||
307 | jne 1b | ||
308 | adcl $0, %eax | ||
309 | 2: movl FP(%esp), %edx | ||
310 | movl %edx, %ecx | ||
311 | andl $0x1c, %edx | ||
312 | je 4f | ||
313 | shrl $2, %edx # This clears CF | ||
314 | SRC(3: movl (%esi), %ebx ) | ||
315 | adcl %ebx, %eax | ||
316 | DST( movl %ebx, (%edi) ) | ||
317 | lea 4(%esi), %esi | ||
318 | lea 4(%edi), %edi | ||
319 | dec %edx | ||
320 | jne 3b | ||
321 | adcl $0, %eax | ||
322 | 4: andl $3, %ecx | ||
323 | jz 7f | ||
324 | cmpl $2, %ecx | ||
325 | jb 5f | ||
326 | SRC( movw (%esi), %cx ) | ||
327 | leal 2(%esi), %esi | ||
328 | DST( movw %cx, (%edi) ) | ||
329 | leal 2(%edi), %edi | ||
330 | je 6f | ||
331 | shll $16,%ecx | ||
332 | SRC(5: movb (%esi), %cl ) | ||
333 | DST( movb %cl, (%edi) ) | ||
334 | 6: addl %ecx, %eax | ||
335 | adcl $0, %eax | ||
336 | 7: | ||
337 | 5000: | ||
338 | |||
339 | # Exception handler: | ||
340 | .section .fixup, "ax" | ||
341 | |||
342 | 6001: | ||
343 | movl ARGBASE+20(%esp), %ebx # src_err_ptr | ||
344 | movl $-EFAULT, (%ebx) | ||
345 | |||
346 | # zero the complete destination - computing the rest | ||
347 | # is too much work | ||
348 | movl ARGBASE+8(%esp), %edi # dst | ||
349 | movl ARGBASE+12(%esp), %ecx # len | ||
350 | xorl %eax,%eax | ||
351 | rep ; stosb | ||
352 | |||
353 | jmp 5000b | ||
354 | |||
355 | 6002: | ||
356 | movl ARGBASE+24(%esp), %ebx # dst_err_ptr | ||
357 | movl $-EFAULT,(%ebx) | ||
358 | jmp 5000b | ||
359 | |||
360 | .previous | ||
361 | |||
362 | popl %ebx | ||
363 | popl %esi | ||
364 | popl %edi | ||
365 | popl %ecx # equivalent to addl $4,%esp | ||
366 | ret | ||
367 | |||
368 | #else | ||
369 | |||
370 | /* Version for PentiumII/PPro */ | ||
371 | |||
372 | #define ROUND1(x) \ | ||
373 | SRC(movl x(%esi), %ebx ) ; \ | ||
374 | addl %ebx, %eax ; \ | ||
375 | DST(movl %ebx, x(%edi) ) ; | ||
376 | |||
377 | #define ROUND(x) \ | ||
378 | SRC(movl x(%esi), %ebx ) ; \ | ||
379 | adcl %ebx, %eax ; \ | ||
380 | DST(movl %ebx, x(%edi) ) ; | ||
381 | |||
382 | #define ARGBASE 12 | ||
383 | |||
384 | csum_partial_copy_generic_i386: | ||
385 | pushl %ebx | ||
386 | pushl %edi | ||
387 | pushl %esi | ||
388 | movl ARGBASE+4(%esp),%esi #src | ||
389 | movl ARGBASE+8(%esp),%edi #dst | ||
390 | movl ARGBASE+12(%esp),%ecx #len | ||
391 | movl ARGBASE+16(%esp),%eax #sum | ||
392 | # movl %ecx, %edx | ||
393 | movl %ecx, %ebx | ||
394 | movl %esi, %edx | ||
395 | shrl $6, %ecx | ||
396 | andl $0x3c, %ebx | ||
397 | negl %ebx | ||
398 | subl %ebx, %esi | ||
399 | subl %ebx, %edi | ||
400 | lea -1(%esi),%edx | ||
401 | andl $-32,%edx | ||
402 | lea 3f(%ebx,%ebx), %ebx | ||
403 | testl %esi, %esi | ||
404 | jmp *%ebx | ||
405 | 1: addl $64,%esi | ||
406 | addl $64,%edi | ||
407 | SRC(movb -32(%edx),%bl) ; SRC(movb (%edx),%bl) | ||
408 | ROUND1(-64) ROUND(-60) ROUND(-56) ROUND(-52) | ||
409 | ROUND (-48) ROUND(-44) ROUND(-40) ROUND(-36) | ||
410 | ROUND (-32) ROUND(-28) ROUND(-24) ROUND(-20) | ||
411 | ROUND (-16) ROUND(-12) ROUND(-8) ROUND(-4) | ||
412 | 3: adcl $0,%eax | ||
413 | addl $64, %edx | ||
414 | dec %ecx | ||
415 | jge 1b | ||
416 | 4: movl ARGBASE+12(%esp),%edx #len | ||
417 | andl $3, %edx | ||
418 | jz 7f | ||
419 | cmpl $2, %edx | ||
420 | jb 5f | ||
421 | SRC( movw (%esi), %dx ) | ||
422 | leal 2(%esi), %esi | ||
423 | DST( movw %dx, (%edi) ) | ||
424 | leal 2(%edi), %edi | ||
425 | je 6f | ||
426 | shll $16,%edx | ||
427 | 5: | ||
428 | SRC( movb (%esi), %dl ) | ||
429 | DST( movb %dl, (%edi) ) | ||
430 | 6: addl %edx, %eax | ||
431 | adcl $0, %eax | ||
432 | 7: | ||
433 | .section .fixup, "ax" | ||
434 | 6001: movl ARGBASE+20(%esp), %ebx # src_err_ptr | ||
435 | movl $-EFAULT, (%ebx) | ||
436 | # zero the complete destination (computing the rest is too much work) | ||
437 | movl ARGBASE+8(%esp),%edi # dst | ||
438 | movl ARGBASE+12(%esp),%ecx # len | ||
439 | xorl %eax,%eax | ||
440 | rep; stosb | ||
441 | jmp 7b | ||
442 | 6002: movl ARGBASE+24(%esp), %ebx # dst_err_ptr | ||
443 | movl $-EFAULT, (%ebx) | ||
444 | jmp 7b | ||
445 | .previous | ||
446 | |||
447 | popl %esi | ||
448 | popl %edi | ||
449 | popl %ebx | ||
450 | ret | ||
451 | |||
452 | #undef ROUND | ||
453 | #undef ROUND1 | ||
454 | |||
455 | #endif | ||