diff options
author | Greg Ungerer <gerg@uclinux.org> | 2011-03-21 23:39:27 -0400 |
---|---|---|
committer | Greg Ungerer <gerg@uclinux.org> | 2011-03-25 00:05:13 -0400 |
commit | 66d857b08b8c3ed5c72c361f863cce77d2a978d7 (patch) | |
tree | 47222d86f4d78dc0da31baf64188bd2e4b38ac1e /arch/m68k/kernel | |
parent | d39dd11c3e6a7af5c20bfac40594db36cf270f42 (diff) |
m68k: merge m68k and m68knommu arch directories
There is a lot of common code that could be shared between the m68k
and m68knommu arch branches. It makes sense to merge the two branches
into a single directory structure so that we can more easily share
that common code.
This is a brute force merge, based on a script from Stephen King
<sfking@fdwdc.com>, which was originally written by Arnd Bergmann
<arnd@arndb.de>.
> The script was inspired by the script Sam Ravnborg used to merge the
> includes from m68knommu. For those files common to both arches but
> differing in content, the m68k version of the file is renamed to
> <file>_mm.<ext> and the m68knommu version of the file is moved into the
> corresponding m68k directory and renamed <file>_no.<ext> and a small
> wrapper file <file>.<ext> is used to select between the two version. Files
> that are common to both but don't differ are removed from the m68knommu
> tree and files and directories that are unique to the m68knommu tree are
> moved to the m68k tree. Finally, the arch/m68knommu tree is removed.
>
> To select between the the versions of the files, the wrapper uses
>
> #ifdef CONFIG_MMU
> #include <file>_mm.<ext>
> #else
> #include <file>_no.<ext>
> #endif
On top of this file merge I have done a simplistic merge of m68k and
m68knommu Kconfig, which primarily attempts to keep existing options and
menus in place. Other than a handful of options being moved it produces
identical .config outputs on m68k and m68knommu targets I tested it on.
With this in place there is now quite a bit of scope for merge cleanups
in future patches.
Signed-off-by: Greg Ungerer <gerg@uclinux.org>
Diffstat (limited to 'arch/m68k/kernel')
45 files changed, 8714 insertions, 5212 deletions
diff --git a/arch/m68k/kernel/Makefile b/arch/m68k/kernel/Makefile index 55d5d6b680a2..c482ebc9dd54 100644 --- a/arch/m68k/kernel/Makefile +++ b/arch/m68k/kernel/Makefile | |||
@@ -1,17 +1,5 @@ | |||
1 | # | 1 | ifdef CONFIG_MMU |
2 | # Makefile for the linux kernel. | 2 | include arch/m68k/kernel/Makefile_mm |
3 | # | ||
4 | |||
5 | ifndef CONFIG_SUN3 | ||
6 | extra-y := head.o | ||
7 | else | 3 | else |
8 | extra-y := sun3-head.o | 4 | include arch/m68k/kernel/Makefile_no |
9 | endif | 5 | endif |
10 | extra-y += vmlinux.lds | ||
11 | |||
12 | obj-y := entry.o process.o traps.o ints.o signal.o ptrace.o module.o \ | ||
13 | sys_m68k.o time.o setup.o m68k_ksyms.o devres.o | ||
14 | |||
15 | devres-y = ../../../kernel/irq/devres.o | ||
16 | |||
17 | obj-y$(CONFIG_MMU_SUN3) += dma.o # no, it's not a typo | ||
diff --git a/arch/m68k/kernel/Makefile_mm b/arch/m68k/kernel/Makefile_mm new file mode 100644 index 000000000000..55d5d6b680a2 --- /dev/null +++ b/arch/m68k/kernel/Makefile_mm | |||
@@ -0,0 +1,17 @@ | |||
1 | # | ||
2 | # Makefile for the linux kernel. | ||
3 | # | ||
4 | |||
5 | ifndef CONFIG_SUN3 | ||
6 | extra-y := head.o | ||
7 | else | ||
8 | extra-y := sun3-head.o | ||
9 | endif | ||
10 | extra-y += vmlinux.lds | ||
11 | |||
12 | obj-y := entry.o process.o traps.o ints.o signal.o ptrace.o module.o \ | ||
13 | sys_m68k.o time.o setup.o m68k_ksyms.o devres.o | ||
14 | |||
15 | devres-y = ../../../kernel/irq/devres.o | ||
16 | |||
17 | obj-y$(CONFIG_MMU_SUN3) += dma.o # no, it's not a typo | ||
diff --git a/arch/m68k/kernel/Makefile_no b/arch/m68k/kernel/Makefile_no new file mode 100644 index 000000000000..37c3fc074c0a --- /dev/null +++ b/arch/m68k/kernel/Makefile_no | |||
@@ -0,0 +1,10 @@ | |||
1 | # | ||
2 | # Makefile for arch/m68knommu/kernel. | ||
3 | # | ||
4 | |||
5 | extra-y := vmlinux.lds | ||
6 | |||
7 | obj-y += dma.o entry.o init_task.o irq.o m68k_ksyms.o process.o ptrace.o \ | ||
8 | setup.o signal.o syscalltable.o sys_m68k.o time.o traps.o | ||
9 | |||
10 | obj-$(CONFIG_MODULES) += module.o | ||
diff --git a/arch/m68k/kernel/asm-offsets.c b/arch/m68k/kernel/asm-offsets.c index 78e59b82ebc3..59a69a5c62f2 100644 --- a/arch/m68k/kernel/asm-offsets.c +++ b/arch/m68k/kernel/asm-offsets.c | |||
@@ -1,100 +1,5 @@ | |||
1 | /* | ||
2 | * This program is used to generate definitions needed by | ||
3 | * assembly language modules. | ||
4 | * | ||
5 | * We use the technique used in the OSF Mach kernel code: | ||
6 | * generate asm statements containing #defines, | ||
7 | * compile this file to assembler, and then extract the | ||
8 | * #defines from the assembly-language output. | ||
9 | */ | ||
10 | |||
11 | #define ASM_OFFSETS_C | ||
12 | |||
13 | #include <linux/stddef.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/kernel_stat.h> | ||
16 | #include <linux/kbuild.h> | ||
17 | #include <asm/bootinfo.h> | ||
18 | #include <asm/irq.h> | ||
19 | #include <asm/amigahw.h> | ||
20 | #include <linux/font.h> | ||
21 | |||
22 | int main(void) | ||
23 | { | ||
24 | /* offsets into the task struct */ | ||
25 | DEFINE(TASK_THREAD, offsetof(struct task_struct, thread)); | ||
26 | DEFINE(TASK_INFO, offsetof(struct task_struct, thread.info)); | ||
27 | DEFINE(TASK_MM, offsetof(struct task_struct, mm)); | ||
28 | #ifdef CONFIG_MMU | 1 | #ifdef CONFIG_MMU |
29 | DEFINE(TASK_TINFO, offsetof(struct task_struct, thread.info)); | 2 | #include "asm-offsets_mm.c" |
3 | #else | ||
4 | #include "asm-offsets_no.c" | ||
30 | #endif | 5 | #endif |
31 | |||
32 | /* offsets into the thread struct */ | ||
33 | DEFINE(THREAD_KSP, offsetof(struct thread_struct, ksp)); | ||
34 | DEFINE(THREAD_USP, offsetof(struct thread_struct, usp)); | ||
35 | DEFINE(THREAD_SR, offsetof(struct thread_struct, sr)); | ||
36 | DEFINE(THREAD_FS, offsetof(struct thread_struct, fs)); | ||
37 | DEFINE(THREAD_CRP, offsetof(struct thread_struct, crp)); | ||
38 | DEFINE(THREAD_ESP0, offsetof(struct thread_struct, esp0)); | ||
39 | DEFINE(THREAD_FPREG, offsetof(struct thread_struct, fp)); | ||
40 | DEFINE(THREAD_FPCNTL, offsetof(struct thread_struct, fpcntl)); | ||
41 | DEFINE(THREAD_FPSTATE, offsetof(struct thread_struct, fpstate)); | ||
42 | |||
43 | /* offsets into the thread_info struct */ | ||
44 | DEFINE(TINFO_PREEMPT, offsetof(struct thread_info, preempt_count)); | ||
45 | DEFINE(TINFO_FLAGS, offsetof(struct thread_info, flags)); | ||
46 | |||
47 | /* offsets into the pt_regs */ | ||
48 | DEFINE(PT_OFF_D0, offsetof(struct pt_regs, d0)); | ||
49 | DEFINE(PT_OFF_ORIG_D0, offsetof(struct pt_regs, orig_d0)); | ||
50 | DEFINE(PT_OFF_D1, offsetof(struct pt_regs, d1)); | ||
51 | DEFINE(PT_OFF_D2, offsetof(struct pt_regs, d2)); | ||
52 | DEFINE(PT_OFF_D3, offsetof(struct pt_regs, d3)); | ||
53 | DEFINE(PT_OFF_D4, offsetof(struct pt_regs, d4)); | ||
54 | DEFINE(PT_OFF_D5, offsetof(struct pt_regs, d5)); | ||
55 | DEFINE(PT_OFF_A0, offsetof(struct pt_regs, a0)); | ||
56 | DEFINE(PT_OFF_A1, offsetof(struct pt_regs, a1)); | ||
57 | DEFINE(PT_OFF_A2, offsetof(struct pt_regs, a2)); | ||
58 | DEFINE(PT_OFF_PC, offsetof(struct pt_regs, pc)); | ||
59 | DEFINE(PT_OFF_SR, offsetof(struct pt_regs, sr)); | ||
60 | /* bitfields are a bit difficult */ | ||
61 | DEFINE(PT_OFF_FORMATVEC, offsetof(struct pt_regs, pc) + 4); | ||
62 | |||
63 | /* offsets into the irq_cpustat_t struct */ | ||
64 | DEFINE(CPUSTAT_SOFTIRQ_PENDING, offsetof(irq_cpustat_t, __softirq_pending)); | ||
65 | |||
66 | /* offsets into the bi_record struct */ | ||
67 | DEFINE(BIR_TAG, offsetof(struct bi_record, tag)); | ||
68 | DEFINE(BIR_SIZE, offsetof(struct bi_record, size)); | ||
69 | DEFINE(BIR_DATA, offsetof(struct bi_record, data)); | ||
70 | |||
71 | /* offsets into font_desc (drivers/video/console/font.h) */ | ||
72 | DEFINE(FONT_DESC_IDX, offsetof(struct font_desc, idx)); | ||
73 | DEFINE(FONT_DESC_NAME, offsetof(struct font_desc, name)); | ||
74 | DEFINE(FONT_DESC_WIDTH, offsetof(struct font_desc, width)); | ||
75 | DEFINE(FONT_DESC_HEIGHT, offsetof(struct font_desc, height)); | ||
76 | DEFINE(FONT_DESC_DATA, offsetof(struct font_desc, data)); | ||
77 | DEFINE(FONT_DESC_PREF, offsetof(struct font_desc, pref)); | ||
78 | |||
79 | /* signal defines */ | ||
80 | DEFINE(LSIGSEGV, SIGSEGV); | ||
81 | DEFINE(LSEGV_MAPERR, SEGV_MAPERR); | ||
82 | DEFINE(LSIGTRAP, SIGTRAP); | ||
83 | DEFINE(LTRAP_TRACE, TRAP_TRACE); | ||
84 | |||
85 | /* offsets into the custom struct */ | ||
86 | DEFINE(CUSTOMBASE, &amiga_custom); | ||
87 | DEFINE(C_INTENAR, offsetof(struct CUSTOM, intenar)); | ||
88 | DEFINE(C_INTREQR, offsetof(struct CUSTOM, intreqr)); | ||
89 | DEFINE(C_INTENA, offsetof(struct CUSTOM, intena)); | ||
90 | DEFINE(C_INTREQ, offsetof(struct CUSTOM, intreq)); | ||
91 | DEFINE(C_SERDATR, offsetof(struct CUSTOM, serdatr)); | ||
92 | DEFINE(C_SERDAT, offsetof(struct CUSTOM, serdat)); | ||
93 | DEFINE(C_SERPER, offsetof(struct CUSTOM, serper)); | ||
94 | DEFINE(CIAABASE, &ciaa); | ||
95 | DEFINE(CIABBASE, &ciab); | ||
96 | DEFINE(C_PRA, offsetof(struct CIA, pra)); | ||
97 | DEFINE(ZTWOBASE, zTwoBase); | ||
98 | |||
99 | return 0; | ||
100 | } | ||
diff --git a/arch/m68k/kernel/asm-offsets_mm.c b/arch/m68k/kernel/asm-offsets_mm.c new file mode 100644 index 000000000000..78e59b82ebc3 --- /dev/null +++ b/arch/m68k/kernel/asm-offsets_mm.c | |||
@@ -0,0 +1,100 @@ | |||
1 | /* | ||
2 | * This program is used to generate definitions needed by | ||
3 | * assembly language modules. | ||
4 | * | ||
5 | * We use the technique used in the OSF Mach kernel code: | ||
6 | * generate asm statements containing #defines, | ||
7 | * compile this file to assembler, and then extract the | ||
8 | * #defines from the assembly-language output. | ||
9 | */ | ||
10 | |||
11 | #define ASM_OFFSETS_C | ||
12 | |||
13 | #include <linux/stddef.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/kernel_stat.h> | ||
16 | #include <linux/kbuild.h> | ||
17 | #include <asm/bootinfo.h> | ||
18 | #include <asm/irq.h> | ||
19 | #include <asm/amigahw.h> | ||
20 | #include <linux/font.h> | ||
21 | |||
22 | int main(void) | ||
23 | { | ||
24 | /* offsets into the task struct */ | ||
25 | DEFINE(TASK_THREAD, offsetof(struct task_struct, thread)); | ||
26 | DEFINE(TASK_INFO, offsetof(struct task_struct, thread.info)); | ||
27 | DEFINE(TASK_MM, offsetof(struct task_struct, mm)); | ||
28 | #ifdef CONFIG_MMU | ||
29 | DEFINE(TASK_TINFO, offsetof(struct task_struct, thread.info)); | ||
30 | #endif | ||
31 | |||
32 | /* offsets into the thread struct */ | ||
33 | DEFINE(THREAD_KSP, offsetof(struct thread_struct, ksp)); | ||
34 | DEFINE(THREAD_USP, offsetof(struct thread_struct, usp)); | ||
35 | DEFINE(THREAD_SR, offsetof(struct thread_struct, sr)); | ||
36 | DEFINE(THREAD_FS, offsetof(struct thread_struct, fs)); | ||
37 | DEFINE(THREAD_CRP, offsetof(struct thread_struct, crp)); | ||
38 | DEFINE(THREAD_ESP0, offsetof(struct thread_struct, esp0)); | ||
39 | DEFINE(THREAD_FPREG, offsetof(struct thread_struct, fp)); | ||
40 | DEFINE(THREAD_FPCNTL, offsetof(struct thread_struct, fpcntl)); | ||
41 | DEFINE(THREAD_FPSTATE, offsetof(struct thread_struct, fpstate)); | ||
42 | |||
43 | /* offsets into the thread_info struct */ | ||
44 | DEFINE(TINFO_PREEMPT, offsetof(struct thread_info, preempt_count)); | ||
45 | DEFINE(TINFO_FLAGS, offsetof(struct thread_info, flags)); | ||
46 | |||
47 | /* offsets into the pt_regs */ | ||
48 | DEFINE(PT_OFF_D0, offsetof(struct pt_regs, d0)); | ||
49 | DEFINE(PT_OFF_ORIG_D0, offsetof(struct pt_regs, orig_d0)); | ||
50 | DEFINE(PT_OFF_D1, offsetof(struct pt_regs, d1)); | ||
51 | DEFINE(PT_OFF_D2, offsetof(struct pt_regs, d2)); | ||
52 | DEFINE(PT_OFF_D3, offsetof(struct pt_regs, d3)); | ||
53 | DEFINE(PT_OFF_D4, offsetof(struct pt_regs, d4)); | ||
54 | DEFINE(PT_OFF_D5, offsetof(struct pt_regs, d5)); | ||
55 | DEFINE(PT_OFF_A0, offsetof(struct pt_regs, a0)); | ||
56 | DEFINE(PT_OFF_A1, offsetof(struct pt_regs, a1)); | ||
57 | DEFINE(PT_OFF_A2, offsetof(struct pt_regs, a2)); | ||
58 | DEFINE(PT_OFF_PC, offsetof(struct pt_regs, pc)); | ||
59 | DEFINE(PT_OFF_SR, offsetof(struct pt_regs, sr)); | ||
60 | /* bitfields are a bit difficult */ | ||
61 | DEFINE(PT_OFF_FORMATVEC, offsetof(struct pt_regs, pc) + 4); | ||
62 | |||
63 | /* offsets into the irq_cpustat_t struct */ | ||
64 | DEFINE(CPUSTAT_SOFTIRQ_PENDING, offsetof(irq_cpustat_t, __softirq_pending)); | ||
65 | |||
66 | /* offsets into the bi_record struct */ | ||
67 | DEFINE(BIR_TAG, offsetof(struct bi_record, tag)); | ||
68 | DEFINE(BIR_SIZE, offsetof(struct bi_record, size)); | ||
69 | DEFINE(BIR_DATA, offsetof(struct bi_record, data)); | ||
70 | |||
71 | /* offsets into font_desc (drivers/video/console/font.h) */ | ||
72 | DEFINE(FONT_DESC_IDX, offsetof(struct font_desc, idx)); | ||
73 | DEFINE(FONT_DESC_NAME, offsetof(struct font_desc, name)); | ||
74 | DEFINE(FONT_DESC_WIDTH, offsetof(struct font_desc, width)); | ||
75 | DEFINE(FONT_DESC_HEIGHT, offsetof(struct font_desc, height)); | ||
76 | DEFINE(FONT_DESC_DATA, offsetof(struct font_desc, data)); | ||
77 | DEFINE(FONT_DESC_PREF, offsetof(struct font_desc, pref)); | ||
78 | |||
79 | /* signal defines */ | ||
80 | DEFINE(LSIGSEGV, SIGSEGV); | ||
81 | DEFINE(LSEGV_MAPERR, SEGV_MAPERR); | ||
82 | DEFINE(LSIGTRAP, SIGTRAP); | ||
83 | DEFINE(LTRAP_TRACE, TRAP_TRACE); | ||
84 | |||
85 | /* offsets into the custom struct */ | ||
86 | DEFINE(CUSTOMBASE, &amiga_custom); | ||
87 | DEFINE(C_INTENAR, offsetof(struct CUSTOM, intenar)); | ||
88 | DEFINE(C_INTREQR, offsetof(struct CUSTOM, intreqr)); | ||
89 | DEFINE(C_INTENA, offsetof(struct CUSTOM, intena)); | ||
90 | DEFINE(C_INTREQ, offsetof(struct CUSTOM, intreq)); | ||
91 | DEFINE(C_SERDATR, offsetof(struct CUSTOM, serdatr)); | ||
92 | DEFINE(C_SERDAT, offsetof(struct CUSTOM, serdat)); | ||
93 | DEFINE(C_SERPER, offsetof(struct CUSTOM, serper)); | ||
94 | DEFINE(CIAABASE, &ciaa); | ||
95 | DEFINE(CIABBASE, &ciab); | ||
96 | DEFINE(C_PRA, offsetof(struct CIA, pra)); | ||
97 | DEFINE(ZTWOBASE, zTwoBase); | ||
98 | |||
99 | return 0; | ||
100 | } | ||
diff --git a/arch/m68k/kernel/asm-offsets_no.c b/arch/m68k/kernel/asm-offsets_no.c new file mode 100644 index 000000000000..ffe02f41ad46 --- /dev/null +++ b/arch/m68k/kernel/asm-offsets_no.c | |||
@@ -0,0 +1,76 @@ | |||
1 | /* | ||
2 | * This program is used to generate definitions needed by | ||
3 | * assembly language modules. | ||
4 | * | ||
5 | * We use the technique used in the OSF Mach kernel code: | ||
6 | * generate asm statements containing #defines, | ||
7 | * compile this file to assembler, and then extract the | ||
8 | * #defines from the assembly-language output. | ||
9 | */ | ||
10 | |||
11 | #include <linux/stddef.h> | ||
12 | #include <linux/sched.h> | ||
13 | #include <linux/kernel_stat.h> | ||
14 | #include <linux/ptrace.h> | ||
15 | #include <linux/hardirq.h> | ||
16 | #include <linux/kbuild.h> | ||
17 | #include <asm/bootinfo.h> | ||
18 | #include <asm/irq.h> | ||
19 | #include <asm/thread_info.h> | ||
20 | |||
21 | int main(void) | ||
22 | { | ||
23 | /* offsets into the task struct */ | ||
24 | DEFINE(TASK_THREAD, offsetof(struct task_struct, thread)); | ||
25 | DEFINE(TASK_MM, offsetof(struct task_struct, mm)); | ||
26 | |||
27 | /* offsets into the irq_cpustat_t struct */ | ||
28 | DEFINE(CPUSTAT_SOFTIRQ_PENDING, offsetof(irq_cpustat_t, __softirq_pending)); | ||
29 | |||
30 | /* offsets into the thread struct */ | ||
31 | DEFINE(THREAD_KSP, offsetof(struct thread_struct, ksp)); | ||
32 | DEFINE(THREAD_USP, offsetof(struct thread_struct, usp)); | ||
33 | DEFINE(THREAD_SR, offsetof(struct thread_struct, sr)); | ||
34 | DEFINE(THREAD_FS, offsetof(struct thread_struct, fs)); | ||
35 | DEFINE(THREAD_CRP, offsetof(struct thread_struct, crp)); | ||
36 | DEFINE(THREAD_ESP0, offsetof(struct thread_struct, esp0)); | ||
37 | DEFINE(THREAD_FPREG, offsetof(struct thread_struct, fp)); | ||
38 | DEFINE(THREAD_FPCNTL, offsetof(struct thread_struct, fpcntl)); | ||
39 | DEFINE(THREAD_FPSTATE, offsetof(struct thread_struct, fpstate)); | ||
40 | |||
41 | /* offsets into the pt_regs */ | ||
42 | DEFINE(PT_OFF_D0, offsetof(struct pt_regs, d0)); | ||
43 | DEFINE(PT_OFF_ORIG_D0, offsetof(struct pt_regs, orig_d0)); | ||
44 | DEFINE(PT_OFF_D1, offsetof(struct pt_regs, d1)); | ||
45 | DEFINE(PT_OFF_D2, offsetof(struct pt_regs, d2)); | ||
46 | DEFINE(PT_OFF_D3, offsetof(struct pt_regs, d3)); | ||
47 | DEFINE(PT_OFF_D4, offsetof(struct pt_regs, d4)); | ||
48 | DEFINE(PT_OFF_D5, offsetof(struct pt_regs, d5)); | ||
49 | DEFINE(PT_OFF_A0, offsetof(struct pt_regs, a0)); | ||
50 | DEFINE(PT_OFF_A1, offsetof(struct pt_regs, a1)); | ||
51 | DEFINE(PT_OFF_A2, offsetof(struct pt_regs, a2)); | ||
52 | DEFINE(PT_OFF_PC, offsetof(struct pt_regs, pc)); | ||
53 | DEFINE(PT_OFF_SR, offsetof(struct pt_regs, sr)); | ||
54 | |||
55 | #ifdef CONFIG_COLDFIRE | ||
56 | /* bitfields are a bit difficult */ | ||
57 | DEFINE(PT_OFF_FORMATVEC, offsetof(struct pt_regs, sr) - 2); | ||
58 | #else | ||
59 | /* bitfields are a bit difficult */ | ||
60 | DEFINE(PT_OFF_FORMATVEC, offsetof(struct pt_regs, pc) + 4); | ||
61 | #endif | ||
62 | |||
63 | /* signal defines */ | ||
64 | DEFINE(SIGSEGV, SIGSEGV); | ||
65 | DEFINE(SEGV_MAPERR, SEGV_MAPERR); | ||
66 | DEFINE(SIGTRAP, SIGTRAP); | ||
67 | DEFINE(TRAP_TRACE, TRAP_TRACE); | ||
68 | |||
69 | DEFINE(PT_PTRACED, PT_PTRACED); | ||
70 | |||
71 | /* Offsets in thread_info structure */ | ||
72 | DEFINE(TI_FLAGS, offsetof(struct thread_info, flags)); | ||
73 | DEFINE(TI_PREEMPTCOUNT, offsetof(struct thread_info, preempt_count)); | ||
74 | |||
75 | return 0; | ||
76 | } | ||
diff --git a/arch/m68k/kernel/dma.c b/arch/m68k/kernel/dma.c index 4bbb3c2a8880..90e8cb726c8c 100644 --- a/arch/m68k/kernel/dma.c +++ b/arch/m68k/kernel/dma.c | |||
@@ -1,130 +1,5 @@ | |||
1 | /* | 1 | #ifdef CONFIG_MMU |
2 | * This file is subject to the terms and conditions of the GNU General Public | 2 | #include "dma_mm.c" |
3 | * License. See the file COPYING in the main directory of this archive | 3 | #else |
4 | * for more details. | 4 | #include "dma_no.c" |
5 | */ | 5 | #endif |
6 | |||
7 | #undef DEBUG | ||
8 | |||
9 | #include <linux/dma-mapping.h> | ||
10 | #include <linux/device.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/scatterlist.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/vmalloc.h> | ||
15 | |||
16 | #include <asm/pgalloc.h> | ||
17 | |||
18 | void *dma_alloc_coherent(struct device *dev, size_t size, | ||
19 | dma_addr_t *handle, gfp_t flag) | ||
20 | { | ||
21 | struct page *page, **map; | ||
22 | pgprot_t pgprot; | ||
23 | void *addr; | ||
24 | int i, order; | ||
25 | |||
26 | pr_debug("dma_alloc_coherent: %d,%x\n", size, flag); | ||
27 | |||
28 | size = PAGE_ALIGN(size); | ||
29 | order = get_order(size); | ||
30 | |||
31 | page = alloc_pages(flag, order); | ||
32 | if (!page) | ||
33 | return NULL; | ||
34 | |||
35 | *handle = page_to_phys(page); | ||
36 | map = kmalloc(sizeof(struct page *) << order, flag & ~__GFP_DMA); | ||
37 | if (!map) { | ||
38 | __free_pages(page, order); | ||
39 | return NULL; | ||
40 | } | ||
41 | split_page(page, order); | ||
42 | |||
43 | order = 1 << order; | ||
44 | size >>= PAGE_SHIFT; | ||
45 | map[0] = page; | ||
46 | for (i = 1; i < size; i++) | ||
47 | map[i] = page + i; | ||
48 | for (; i < order; i++) | ||
49 | __free_page(page + i); | ||
50 | pgprot = __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY); | ||
51 | if (CPU_IS_040_OR_060) | ||
52 | pgprot_val(pgprot) |= _PAGE_GLOBAL040 | _PAGE_NOCACHE_S; | ||
53 | else | ||
54 | pgprot_val(pgprot) |= _PAGE_NOCACHE030; | ||
55 | addr = vmap(map, size, VM_MAP, pgprot); | ||
56 | kfree(map); | ||
57 | |||
58 | return addr; | ||
59 | } | ||
60 | EXPORT_SYMBOL(dma_alloc_coherent); | ||
61 | |||
62 | void dma_free_coherent(struct device *dev, size_t size, | ||
63 | void *addr, dma_addr_t handle) | ||
64 | { | ||
65 | pr_debug("dma_free_coherent: %p, %x\n", addr, handle); | ||
66 | vfree(addr); | ||
67 | } | ||
68 | EXPORT_SYMBOL(dma_free_coherent); | ||
69 | |||
70 | void dma_sync_single_for_device(struct device *dev, dma_addr_t handle, | ||
71 | size_t size, enum dma_data_direction dir) | ||
72 | { | ||
73 | switch (dir) { | ||
74 | case DMA_TO_DEVICE: | ||
75 | cache_push(handle, size); | ||
76 | break; | ||
77 | case DMA_FROM_DEVICE: | ||
78 | cache_clear(handle, size); | ||
79 | break; | ||
80 | default: | ||
81 | if (printk_ratelimit()) | ||
82 | printk("dma_sync_single_for_device: unsupported dir %u\n", dir); | ||
83 | break; | ||
84 | } | ||
85 | } | ||
86 | EXPORT_SYMBOL(dma_sync_single_for_device); | ||
87 | |||
88 | void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nents, | ||
89 | enum dma_data_direction dir) | ||
90 | { | ||
91 | int i; | ||
92 | |||
93 | for (i = 0; i < nents; sg++, i++) | ||
94 | dma_sync_single_for_device(dev, sg->dma_address, sg->length, dir); | ||
95 | } | ||
96 | EXPORT_SYMBOL(dma_sync_sg_for_device); | ||
97 | |||
98 | dma_addr_t dma_map_single(struct device *dev, void *addr, size_t size, | ||
99 | enum dma_data_direction dir) | ||
100 | { | ||
101 | dma_addr_t handle = virt_to_bus(addr); | ||
102 | |||
103 | dma_sync_single_for_device(dev, handle, size, dir); | ||
104 | return handle; | ||
105 | } | ||
106 | EXPORT_SYMBOL(dma_map_single); | ||
107 | |||
108 | dma_addr_t dma_map_page(struct device *dev, struct page *page, | ||
109 | unsigned long offset, size_t size, | ||
110 | enum dma_data_direction dir) | ||
111 | { | ||
112 | dma_addr_t handle = page_to_phys(page) + offset; | ||
113 | |||
114 | dma_sync_single_for_device(dev, handle, size, dir); | ||
115 | return handle; | ||
116 | } | ||
117 | EXPORT_SYMBOL(dma_map_page); | ||
118 | |||
119 | int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, | ||
120 | enum dma_data_direction dir) | ||
121 | { | ||
122 | int i; | ||
123 | |||
124 | for (i = 0; i < nents; sg++, i++) { | ||
125 | sg->dma_address = sg_phys(sg); | ||
126 | dma_sync_single_for_device(dev, sg->dma_address, sg->length, dir); | ||
127 | } | ||
128 | return nents; | ||
129 | } | ||
130 | EXPORT_SYMBOL(dma_map_sg); | ||
diff --git a/arch/m68k/kernel/dma_mm.c b/arch/m68k/kernel/dma_mm.c new file mode 100644 index 000000000000..4bbb3c2a8880 --- /dev/null +++ b/arch/m68k/kernel/dma_mm.c | |||
@@ -0,0 +1,130 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file COPYING in the main directory of this archive | ||
4 | * for more details. | ||
5 | */ | ||
6 | |||
7 | #undef DEBUG | ||
8 | |||
9 | #include <linux/dma-mapping.h> | ||
10 | #include <linux/device.h> | ||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/scatterlist.h> | ||
13 | #include <linux/slab.h> | ||
14 | #include <linux/vmalloc.h> | ||
15 | |||
16 | #include <asm/pgalloc.h> | ||
17 | |||
18 | void *dma_alloc_coherent(struct device *dev, size_t size, | ||
19 | dma_addr_t *handle, gfp_t flag) | ||
20 | { | ||
21 | struct page *page, **map; | ||
22 | pgprot_t pgprot; | ||
23 | void *addr; | ||
24 | int i, order; | ||
25 | |||
26 | pr_debug("dma_alloc_coherent: %d,%x\n", size, flag); | ||
27 | |||
28 | size = PAGE_ALIGN(size); | ||
29 | order = get_order(size); | ||
30 | |||
31 | page = alloc_pages(flag, order); | ||
32 | if (!page) | ||
33 | return NULL; | ||
34 | |||
35 | *handle = page_to_phys(page); | ||
36 | map = kmalloc(sizeof(struct page *) << order, flag & ~__GFP_DMA); | ||
37 | if (!map) { | ||
38 | __free_pages(page, order); | ||
39 | return NULL; | ||
40 | } | ||
41 | split_page(page, order); | ||
42 | |||
43 | order = 1 << order; | ||
44 | size >>= PAGE_SHIFT; | ||
45 | map[0] = page; | ||
46 | for (i = 1; i < size; i++) | ||
47 | map[i] = page + i; | ||
48 | for (; i < order; i++) | ||
49 | __free_page(page + i); | ||
50 | pgprot = __pgprot(_PAGE_PRESENT | _PAGE_ACCESSED | _PAGE_DIRTY); | ||
51 | if (CPU_IS_040_OR_060) | ||
52 | pgprot_val(pgprot) |= _PAGE_GLOBAL040 | _PAGE_NOCACHE_S; | ||
53 | else | ||
54 | pgprot_val(pgprot) |= _PAGE_NOCACHE030; | ||
55 | addr = vmap(map, size, VM_MAP, pgprot); | ||
56 | kfree(map); | ||
57 | |||
58 | return addr; | ||
59 | } | ||
60 | EXPORT_SYMBOL(dma_alloc_coherent); | ||
61 | |||
62 | void dma_free_coherent(struct device *dev, size_t size, | ||
63 | void *addr, dma_addr_t handle) | ||
64 | { | ||
65 | pr_debug("dma_free_coherent: %p, %x\n", addr, handle); | ||
66 | vfree(addr); | ||
67 | } | ||
68 | EXPORT_SYMBOL(dma_free_coherent); | ||
69 | |||
70 | void dma_sync_single_for_device(struct device *dev, dma_addr_t handle, | ||
71 | size_t size, enum dma_data_direction dir) | ||
72 | { | ||
73 | switch (dir) { | ||
74 | case DMA_TO_DEVICE: | ||
75 | cache_push(handle, size); | ||
76 | break; | ||
77 | case DMA_FROM_DEVICE: | ||
78 | cache_clear(handle, size); | ||
79 | break; | ||
80 | default: | ||
81 | if (printk_ratelimit()) | ||
82 | printk("dma_sync_single_for_device: unsupported dir %u\n", dir); | ||
83 | break; | ||
84 | } | ||
85 | } | ||
86 | EXPORT_SYMBOL(dma_sync_single_for_device); | ||
87 | |||
88 | void dma_sync_sg_for_device(struct device *dev, struct scatterlist *sg, int nents, | ||
89 | enum dma_data_direction dir) | ||
90 | { | ||
91 | int i; | ||
92 | |||
93 | for (i = 0; i < nents; sg++, i++) | ||
94 | dma_sync_single_for_device(dev, sg->dma_address, sg->length, dir); | ||
95 | } | ||
96 | EXPORT_SYMBOL(dma_sync_sg_for_device); | ||
97 | |||
98 | dma_addr_t dma_map_single(struct device *dev, void *addr, size_t size, | ||
99 | enum dma_data_direction dir) | ||
100 | { | ||
101 | dma_addr_t handle = virt_to_bus(addr); | ||
102 | |||
103 | dma_sync_single_for_device(dev, handle, size, dir); | ||
104 | return handle; | ||
105 | } | ||
106 | EXPORT_SYMBOL(dma_map_single); | ||
107 | |||
108 | dma_addr_t dma_map_page(struct device *dev, struct page *page, | ||
109 | unsigned long offset, size_t size, | ||
110 | enum dma_data_direction dir) | ||
111 | { | ||
112 | dma_addr_t handle = page_to_phys(page) + offset; | ||
113 | |||
114 | dma_sync_single_for_device(dev, handle, size, dir); | ||
115 | return handle; | ||
116 | } | ||
117 | EXPORT_SYMBOL(dma_map_page); | ||
118 | |||
119 | int dma_map_sg(struct device *dev, struct scatterlist *sg, int nents, | ||
120 | enum dma_data_direction dir) | ||
121 | { | ||
122 | int i; | ||
123 | |||
124 | for (i = 0; i < nents; sg++, i++) { | ||
125 | sg->dma_address = sg_phys(sg); | ||
126 | dma_sync_single_for_device(dev, sg->dma_address, sg->length, dir); | ||
127 | } | ||
128 | return nents; | ||
129 | } | ||
130 | EXPORT_SYMBOL(dma_map_sg); | ||
diff --git a/arch/m68k/kernel/dma_no.c b/arch/m68k/kernel/dma_no.c new file mode 100644 index 000000000000..fc61541aeb71 --- /dev/null +++ b/arch/m68k/kernel/dma_no.c | |||
@@ -0,0 +1,74 @@ | |||
1 | /* | ||
2 | * Dynamic DMA mapping support. | ||
3 | * | ||
4 | * We never have any address translations to worry about, so this | ||
5 | * is just alloc/free. | ||
6 | */ | ||
7 | |||
8 | #include <linux/types.h> | ||
9 | #include <linux/gfp.h> | ||
10 | #include <linux/mm.h> | ||
11 | #include <linux/device.h> | ||
12 | #include <linux/dma-mapping.h> | ||
13 | #include <asm/cacheflush.h> | ||
14 | |||
15 | void *dma_alloc_coherent(struct device *dev, size_t size, | ||
16 | dma_addr_t *dma_handle, gfp_t gfp) | ||
17 | { | ||
18 | void *ret; | ||
19 | /* ignore region specifiers */ | ||
20 | gfp &= ~(__GFP_DMA | __GFP_HIGHMEM); | ||
21 | |||
22 | if (dev == NULL || (*dev->dma_mask < 0xffffffff)) | ||
23 | gfp |= GFP_DMA; | ||
24 | ret = (void *)__get_free_pages(gfp, get_order(size)); | ||
25 | |||
26 | if (ret != NULL) { | ||
27 | memset(ret, 0, size); | ||
28 | *dma_handle = virt_to_phys(ret); | ||
29 | } | ||
30 | return ret; | ||
31 | } | ||
32 | |||
33 | void dma_free_coherent(struct device *dev, size_t size, | ||
34 | void *vaddr, dma_addr_t dma_handle) | ||
35 | { | ||
36 | free_pages((unsigned long)vaddr, get_order(size)); | ||
37 | } | ||
38 | |||
39 | void dma_sync_single_for_device(struct device *dev, dma_addr_t handle, | ||
40 | size_t size, enum dma_data_direction dir) | ||
41 | { | ||
42 | switch (dir) { | ||
43 | case DMA_TO_DEVICE: | ||
44 | flush_dcache_range(handle, size); | ||
45 | break; | ||
46 | case DMA_FROM_DEVICE: | ||
47 | /* Should be clear already */ | ||
48 | break; | ||
49 | default: | ||
50 | if (printk_ratelimit()) | ||
51 | printk("dma_sync_single_for_device: unsupported dir %u\n", dir); | ||
52 | break; | ||
53 | } | ||
54 | } | ||
55 | |||
56 | EXPORT_SYMBOL(dma_sync_single_for_device); | ||
57 | dma_addr_t dma_map_single(struct device *dev, void *addr, size_t size, | ||
58 | enum dma_data_direction dir) | ||
59 | { | ||
60 | dma_addr_t handle = virt_to_phys(addr); | ||
61 | flush_dcache_range(handle, size); | ||
62 | return handle; | ||
63 | } | ||
64 | EXPORT_SYMBOL(dma_map_single); | ||
65 | |||
66 | dma_addr_t dma_map_page(struct device *dev, struct page *page, | ||
67 | unsigned long offset, size_t size, | ||
68 | enum dma_data_direction dir) | ||
69 | { | ||
70 | dma_addr_t handle = page_to_phys(page) + offset; | ||
71 | dma_sync_single_for_device(dev, handle, size, dir); | ||
72 | return handle; | ||
73 | } | ||
74 | EXPORT_SYMBOL(dma_map_page); | ||
diff --git a/arch/m68k/kernel/entry.S b/arch/m68k/kernel/entry.S index 1559dea36e55..081cf96f243b 100644 --- a/arch/m68k/kernel/entry.S +++ b/arch/m68k/kernel/entry.S | |||
@@ -1,753 +1,5 @@ | |||
1 | /* -*- mode: asm -*- | 1 | #ifdef CONFIG_MMU |
2 | * | 2 | #include "entry_mm.S" |
3 | * linux/arch/m68k/kernel/entry.S | 3 | #else |
4 | * | 4 | #include "entry_no.S" |
5 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
6 | * | ||
7 | * This file is subject to the terms and conditions of the GNU General Public | ||
8 | * License. See the file README.legal in the main directory of this archive | ||
9 | * for more details. | ||
10 | * | ||
11 | * Linux/m68k support by Hamish Macdonald | ||
12 | * | ||
13 | * 68060 fixes by Jesper Skov | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | /* | ||
18 | * entry.S contains the system-call and fault low-level handling routines. | ||
19 | * This also contains the timer-interrupt handler, as well as all interrupts | ||
20 | * and faults that can result in a task-switch. | ||
21 | * | ||
22 | * NOTE: This code handles signal-recognition, which happens every time | ||
23 | * after a timer-interrupt and after each system call. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | /* | ||
28 | * 12/03/96 Jes: Currently we only support m68k single-cpu systems, so | ||
29 | * all pointers that used to be 'current' are now entry | ||
30 | * number 0 in the 'current_set' list. | ||
31 | * | ||
32 | * 6/05/00 RZ: addedd writeback completion after return from sighandler | ||
33 | * for 68040 | ||
34 | */ | ||
35 | |||
36 | #include <linux/linkage.h> | ||
37 | #include <asm/entry.h> | ||
38 | #include <asm/errno.h> | ||
39 | #include <asm/setup.h> | ||
40 | #include <asm/segment.h> | ||
41 | #include <asm/traps.h> | ||
42 | #include <asm/unistd.h> | ||
43 | |||
44 | #include <asm/asm-offsets.h> | ||
45 | |||
46 | .globl system_call, buserr, trap, resume | ||
47 | .globl sys_call_table | ||
48 | .globl sys_fork, sys_clone, sys_vfork | ||
49 | .globl ret_from_interrupt, bad_interrupt | ||
50 | .globl auto_irqhandler_fixup | ||
51 | .globl user_irqvec_fixup, user_irqhandler_fixup | ||
52 | |||
53 | .text | ||
54 | ENTRY(buserr) | ||
55 | SAVE_ALL_INT | ||
56 | GET_CURRENT(%d0) | ||
57 | movel %sp,%sp@- | stack frame pointer argument | ||
58 | bsrl buserr_c | ||
59 | addql #4,%sp | ||
60 | jra .Lret_from_exception | ||
61 | |||
62 | ENTRY(trap) | ||
63 | SAVE_ALL_INT | ||
64 | GET_CURRENT(%d0) | ||
65 | movel %sp,%sp@- | stack frame pointer argument | ||
66 | bsrl trap_c | ||
67 | addql #4,%sp | ||
68 | jra .Lret_from_exception | ||
69 | |||
70 | | After a fork we jump here directly from resume, | ||
71 | | so that %d1 contains the previous task | ||
72 | | schedule_tail now used regardless of CONFIG_SMP | ||
73 | ENTRY(ret_from_fork) | ||
74 | movel %d1,%sp@- | ||
75 | jsr schedule_tail | ||
76 | addql #4,%sp | ||
77 | jra .Lret_from_exception | ||
78 | |||
79 | do_trace_entry: | ||
80 | movel #-ENOSYS,%sp@(PT_OFF_D0)| needed for strace | ||
81 | subql #4,%sp | ||
82 | SAVE_SWITCH_STACK | ||
83 | jbsr syscall_trace | ||
84 | RESTORE_SWITCH_STACK | ||
85 | addql #4,%sp | ||
86 | movel %sp@(PT_OFF_ORIG_D0),%d0 | ||
87 | cmpl #NR_syscalls,%d0 | ||
88 | jcs syscall | ||
89 | badsys: | ||
90 | movel #-ENOSYS,%sp@(PT_OFF_D0) | ||
91 | jra ret_from_syscall | ||
92 | |||
93 | do_trace_exit: | ||
94 | subql #4,%sp | ||
95 | SAVE_SWITCH_STACK | ||
96 | jbsr syscall_trace | ||
97 | RESTORE_SWITCH_STACK | ||
98 | addql #4,%sp | ||
99 | jra .Lret_from_exception | ||
100 | |||
101 | ENTRY(ret_from_signal) | ||
102 | tstb %curptr@(TASK_INFO+TINFO_FLAGS+2) | ||
103 | jge 1f | ||
104 | jbsr syscall_trace | ||
105 | 1: RESTORE_SWITCH_STACK | ||
106 | addql #4,%sp | ||
107 | /* on 68040 complete pending writebacks if any */ | ||
108 | #ifdef CONFIG_M68040 | ||
109 | bfextu %sp@(PT_OFF_FORMATVEC){#0,#4},%d0 | ||
110 | subql #7,%d0 | bus error frame ? | ||
111 | jbne 1f | ||
112 | movel %sp,%sp@- | ||
113 | jbsr berr_040cleanup | ||
114 | addql #4,%sp | ||
115 | 1: | ||
116 | #endif | 5 | #endif |
117 | jra .Lret_from_exception | ||
118 | |||
119 | ENTRY(system_call) | ||
120 | SAVE_ALL_SYS | ||
121 | |||
122 | GET_CURRENT(%d1) | ||
123 | | save top of frame | ||
124 | movel %sp,%curptr@(TASK_THREAD+THREAD_ESP0) | ||
125 | |||
126 | | syscall trace? | ||
127 | tstb %curptr@(TASK_INFO+TINFO_FLAGS+2) | ||
128 | jmi do_trace_entry | ||
129 | cmpl #NR_syscalls,%d0 | ||
130 | jcc badsys | ||
131 | syscall: | ||
132 | jbsr @(sys_call_table,%d0:l:4)@(0) | ||
133 | movel %d0,%sp@(PT_OFF_D0) | save the return value | ||
134 | ret_from_syscall: | ||
135 | |oriw #0x0700,%sr | ||
136 | movew %curptr@(TASK_INFO+TINFO_FLAGS+2),%d0 | ||
137 | jne syscall_exit_work | ||
138 | 1: RESTORE_ALL | ||
139 | |||
140 | syscall_exit_work: | ||
141 | btst #5,%sp@(PT_OFF_SR) | check if returning to kernel | ||
142 | bnes 1b | if so, skip resched, signals | ||
143 | lslw #1,%d0 | ||
144 | jcs do_trace_exit | ||
145 | jmi do_delayed_trace | ||
146 | lslw #8,%d0 | ||
147 | jmi do_signal_return | ||
148 | pea resume_userspace | ||
149 | jra schedule | ||
150 | |||
151 | |||
152 | ENTRY(ret_from_exception) | ||
153 | .Lret_from_exception: | ||
154 | btst #5,%sp@(PT_OFF_SR) | check if returning to kernel | ||
155 | bnes 1f | if so, skip resched, signals | ||
156 | | only allow interrupts when we are really the last one on the | ||
157 | | kernel stack, otherwise stack overflow can occur during | ||
158 | | heavy interrupt load | ||
159 | andw #ALLOWINT,%sr | ||
160 | |||
161 | resume_userspace: | ||
162 | moveb %curptr@(TASK_INFO+TINFO_FLAGS+3),%d0 | ||
163 | jne exit_work | ||
164 | 1: RESTORE_ALL | ||
165 | |||
166 | exit_work: | ||
167 | | save top of frame | ||
168 | movel %sp,%curptr@(TASK_THREAD+THREAD_ESP0) | ||
169 | lslb #1,%d0 | ||
170 | jmi do_signal_return | ||
171 | pea resume_userspace | ||
172 | jra schedule | ||
173 | |||
174 | |||
175 | do_signal_return: | ||
176 | |andw #ALLOWINT,%sr | ||
177 | subql #4,%sp | dummy return address | ||
178 | SAVE_SWITCH_STACK | ||
179 | pea %sp@(SWITCH_STACK_SIZE) | ||
180 | bsrl do_signal | ||
181 | addql #4,%sp | ||
182 | RESTORE_SWITCH_STACK | ||
183 | addql #4,%sp | ||
184 | jbra resume_userspace | ||
185 | |||
186 | do_delayed_trace: | ||
187 | bclr #7,%sp@(PT_OFF_SR) | clear trace bit in SR | ||
188 | pea 1 | send SIGTRAP | ||
189 | movel %curptr,%sp@- | ||
190 | pea LSIGTRAP | ||
191 | jbsr send_sig | ||
192 | addql #8,%sp | ||
193 | addql #4,%sp | ||
194 | jbra resume_userspace | ||
195 | |||
196 | |||
197 | /* This is the main interrupt handler for autovector interrupts */ | ||
198 | |||
199 | ENTRY(auto_inthandler) | ||
200 | SAVE_ALL_INT | ||
201 | GET_CURRENT(%d0) | ||
202 | addqb #1,%curptr@(TASK_INFO+TINFO_PREEMPT+1) | ||
203 | | put exception # in d0 | ||
204 | bfextu %sp@(PT_OFF_FORMATVEC){#4,#10},%d0 | ||
205 | subw #VEC_SPUR,%d0 | ||
206 | |||
207 | movel %sp,%sp@- | ||
208 | movel %d0,%sp@- | put vector # on stack | ||
209 | auto_irqhandler_fixup = . + 2 | ||
210 | jsr __m68k_handle_int | process the IRQ | ||
211 | addql #8,%sp | pop parameters off stack | ||
212 | |||
213 | ret_from_interrupt: | ||
214 | subqb #1,%curptr@(TASK_INFO+TINFO_PREEMPT+1) | ||
215 | jeq ret_from_last_interrupt | ||
216 | 2: RESTORE_ALL | ||
217 | |||
218 | ALIGN | ||
219 | ret_from_last_interrupt: | ||
220 | moveq #(~ALLOWINT>>8)&0xff,%d0 | ||
221 | andb %sp@(PT_OFF_SR),%d0 | ||
222 | jne 2b | ||
223 | |||
224 | /* check if we need to do software interrupts */ | ||
225 | tstl irq_stat+CPUSTAT_SOFTIRQ_PENDING | ||
226 | jeq .Lret_from_exception | ||
227 | pea ret_from_exception | ||
228 | jra do_softirq | ||
229 | |||
230 | /* Handler for user defined interrupt vectors */ | ||
231 | |||
232 | ENTRY(user_inthandler) | ||
233 | SAVE_ALL_INT | ||
234 | GET_CURRENT(%d0) | ||
235 | addqb #1,%curptr@(TASK_INFO+TINFO_PREEMPT+1) | ||
236 | | put exception # in d0 | ||
237 | bfextu %sp@(PT_OFF_FORMATVEC){#4,#10},%d0 | ||
238 | user_irqvec_fixup = . + 2 | ||
239 | subw #VEC_USER,%d0 | ||
240 | |||
241 | movel %sp,%sp@- | ||
242 | movel %d0,%sp@- | put vector # on stack | ||
243 | user_irqhandler_fixup = . + 2 | ||
244 | jsr __m68k_handle_int | process the IRQ | ||
245 | addql #8,%sp | pop parameters off stack | ||
246 | |||
247 | subqb #1,%curptr@(TASK_INFO+TINFO_PREEMPT+1) | ||
248 | jeq ret_from_last_interrupt | ||
249 | RESTORE_ALL | ||
250 | |||
251 | /* Handler for uninitialized and spurious interrupts */ | ||
252 | |||
253 | ENTRY(bad_inthandler) | ||
254 | SAVE_ALL_INT | ||
255 | GET_CURRENT(%d0) | ||
256 | addqb #1,%curptr@(TASK_INFO+TINFO_PREEMPT+1) | ||
257 | |||
258 | movel %sp,%sp@- | ||
259 | jsr handle_badint | ||
260 | addql #4,%sp | ||
261 | |||
262 | subqb #1,%curptr@(TASK_INFO+TINFO_PREEMPT+1) | ||
263 | jeq ret_from_last_interrupt | ||
264 | RESTORE_ALL | ||
265 | |||
266 | |||
267 | ENTRY(sys_fork) | ||
268 | SAVE_SWITCH_STACK | ||
269 | pea %sp@(SWITCH_STACK_SIZE) | ||
270 | jbsr m68k_fork | ||
271 | addql #4,%sp | ||
272 | RESTORE_SWITCH_STACK | ||
273 | rts | ||
274 | |||
275 | ENTRY(sys_clone) | ||
276 | SAVE_SWITCH_STACK | ||
277 | pea %sp@(SWITCH_STACK_SIZE) | ||
278 | jbsr m68k_clone | ||
279 | addql #4,%sp | ||
280 | RESTORE_SWITCH_STACK | ||
281 | rts | ||
282 | |||
283 | ENTRY(sys_vfork) | ||
284 | SAVE_SWITCH_STACK | ||
285 | pea %sp@(SWITCH_STACK_SIZE) | ||
286 | jbsr m68k_vfork | ||
287 | addql #4,%sp | ||
288 | RESTORE_SWITCH_STACK | ||
289 | rts | ||
290 | |||
291 | ENTRY(sys_sigreturn) | ||
292 | SAVE_SWITCH_STACK | ||
293 | jbsr do_sigreturn | ||
294 | RESTORE_SWITCH_STACK | ||
295 | rts | ||
296 | |||
297 | ENTRY(sys_rt_sigreturn) | ||
298 | SAVE_SWITCH_STACK | ||
299 | jbsr do_rt_sigreturn | ||
300 | RESTORE_SWITCH_STACK | ||
301 | rts | ||
302 | |||
303 | resume: | ||
304 | /* | ||
305 | * Beware - when entering resume, prev (the current task) is | ||
306 | * in a0, next (the new task) is in a1,so don't change these | ||
307 | * registers until their contents are no longer needed. | ||
308 | */ | ||
309 | |||
310 | /* save sr */ | ||
311 | movew %sr,%a0@(TASK_THREAD+THREAD_SR) | ||
312 | |||
313 | /* save fs (sfc,%dfc) (may be pointing to kernel memory) */ | ||
314 | movec %sfc,%d0 | ||
315 | movew %d0,%a0@(TASK_THREAD+THREAD_FS) | ||
316 | |||
317 | /* save usp */ | ||
318 | /* it is better to use a movel here instead of a movew 8*) */ | ||
319 | movec %usp,%d0 | ||
320 | movel %d0,%a0@(TASK_THREAD+THREAD_USP) | ||
321 | |||
322 | /* save non-scratch registers on stack */ | ||
323 | SAVE_SWITCH_STACK | ||
324 | |||
325 | /* save current kernel stack pointer */ | ||
326 | movel %sp,%a0@(TASK_THREAD+THREAD_KSP) | ||
327 | |||
328 | /* save floating point context */ | ||
329 | #ifndef CONFIG_M68KFPU_EMU_ONLY | ||
330 | #ifdef CONFIG_M68KFPU_EMU | ||
331 | tstl m68k_fputype | ||
332 | jeq 3f | ||
333 | #endif | ||
334 | fsave %a0@(TASK_THREAD+THREAD_FPSTATE) | ||
335 | |||
336 | #if defined(CONFIG_M68060) | ||
337 | #if !defined(CPU_M68060_ONLY) | ||
338 | btst #3,m68k_cputype+3 | ||
339 | beqs 1f | ||
340 | #endif | ||
341 | /* The 060 FPU keeps status in bits 15-8 of the first longword */ | ||
342 | tstb %a0@(TASK_THREAD+THREAD_FPSTATE+2) | ||
343 | jeq 3f | ||
344 | #if !defined(CPU_M68060_ONLY) | ||
345 | jra 2f | ||
346 | #endif | ||
347 | #endif /* CONFIG_M68060 */ | ||
348 | #if !defined(CPU_M68060_ONLY) | ||
349 | 1: tstb %a0@(TASK_THREAD+THREAD_FPSTATE) | ||
350 | jeq 3f | ||
351 | #endif | ||
352 | 2: fmovemx %fp0-%fp7,%a0@(TASK_THREAD+THREAD_FPREG) | ||
353 | fmoveml %fpcr/%fpsr/%fpiar,%a0@(TASK_THREAD+THREAD_FPCNTL) | ||
354 | 3: | ||
355 | #endif /* CONFIG_M68KFPU_EMU_ONLY */ | ||
356 | /* Return previous task in %d1 */ | ||
357 | movel %curptr,%d1 | ||
358 | |||
359 | /* switch to new task (a1 contains new task) */ | ||
360 | movel %a1,%curptr | ||
361 | |||
362 | /* restore floating point context */ | ||
363 | #ifndef CONFIG_M68KFPU_EMU_ONLY | ||
364 | #ifdef CONFIG_M68KFPU_EMU | ||
365 | tstl m68k_fputype | ||
366 | jeq 4f | ||
367 | #endif | ||
368 | #if defined(CONFIG_M68060) | ||
369 | #if !defined(CPU_M68060_ONLY) | ||
370 | btst #3,m68k_cputype+3 | ||
371 | beqs 1f | ||
372 | #endif | ||
373 | /* The 060 FPU keeps status in bits 15-8 of the first longword */ | ||
374 | tstb %a1@(TASK_THREAD+THREAD_FPSTATE+2) | ||
375 | jeq 3f | ||
376 | #if !defined(CPU_M68060_ONLY) | ||
377 | jra 2f | ||
378 | #endif | ||
379 | #endif /* CONFIG_M68060 */ | ||
380 | #if !defined(CPU_M68060_ONLY) | ||
381 | 1: tstb %a1@(TASK_THREAD+THREAD_FPSTATE) | ||
382 | jeq 3f | ||
383 | #endif | ||
384 | 2: fmovemx %a1@(TASK_THREAD+THREAD_FPREG),%fp0-%fp7 | ||
385 | fmoveml %a1@(TASK_THREAD+THREAD_FPCNTL),%fpcr/%fpsr/%fpiar | ||
386 | 3: frestore %a1@(TASK_THREAD+THREAD_FPSTATE) | ||
387 | 4: | ||
388 | #endif /* CONFIG_M68KFPU_EMU_ONLY */ | ||
389 | |||
390 | /* restore the kernel stack pointer */ | ||
391 | movel %a1@(TASK_THREAD+THREAD_KSP),%sp | ||
392 | |||
393 | /* restore non-scratch registers */ | ||
394 | RESTORE_SWITCH_STACK | ||
395 | |||
396 | /* restore user stack pointer */ | ||
397 | movel %a1@(TASK_THREAD+THREAD_USP),%a0 | ||
398 | movel %a0,%usp | ||
399 | |||
400 | /* restore fs (sfc,%dfc) */ | ||
401 | movew %a1@(TASK_THREAD+THREAD_FS),%a0 | ||
402 | movec %a0,%sfc | ||
403 | movec %a0,%dfc | ||
404 | |||
405 | /* restore status register */ | ||
406 | movew %a1@(TASK_THREAD+THREAD_SR),%sr | ||
407 | |||
408 | rts | ||
409 | |||
410 | .data | ||
411 | ALIGN | ||
412 | sys_call_table: | ||
413 | .long sys_restart_syscall /* 0 - old "setup()" system call, used for restarting */ | ||
414 | .long sys_exit | ||
415 | .long sys_fork | ||
416 | .long sys_read | ||
417 | .long sys_write | ||
418 | .long sys_open /* 5 */ | ||
419 | .long sys_close | ||
420 | .long sys_waitpid | ||
421 | .long sys_creat | ||
422 | .long sys_link | ||
423 | .long sys_unlink /* 10 */ | ||
424 | .long sys_execve | ||
425 | .long sys_chdir | ||
426 | .long sys_time | ||
427 | .long sys_mknod | ||
428 | .long sys_chmod /* 15 */ | ||
429 | .long sys_chown16 | ||
430 | .long sys_ni_syscall /* old break syscall holder */ | ||
431 | .long sys_stat | ||
432 | .long sys_lseek | ||
433 | .long sys_getpid /* 20 */ | ||
434 | .long sys_mount | ||
435 | .long sys_oldumount | ||
436 | .long sys_setuid16 | ||
437 | .long sys_getuid16 | ||
438 | .long sys_stime /* 25 */ | ||
439 | .long sys_ptrace | ||
440 | .long sys_alarm | ||
441 | .long sys_fstat | ||
442 | .long sys_pause | ||
443 | .long sys_utime /* 30 */ | ||
444 | .long sys_ni_syscall /* old stty syscall holder */ | ||
445 | .long sys_ni_syscall /* old gtty syscall holder */ | ||
446 | .long sys_access | ||
447 | .long sys_nice | ||
448 | .long sys_ni_syscall /* 35 */ /* old ftime syscall holder */ | ||
449 | .long sys_sync | ||
450 | .long sys_kill | ||
451 | .long sys_rename | ||
452 | .long sys_mkdir | ||
453 | .long sys_rmdir /* 40 */ | ||
454 | .long sys_dup | ||
455 | .long sys_pipe | ||
456 | .long sys_times | ||
457 | .long sys_ni_syscall /* old prof syscall holder */ | ||
458 | .long sys_brk /* 45 */ | ||
459 | .long sys_setgid16 | ||
460 | .long sys_getgid16 | ||
461 | .long sys_signal | ||
462 | .long sys_geteuid16 | ||
463 | .long sys_getegid16 /* 50 */ | ||
464 | .long sys_acct | ||
465 | .long sys_umount /* recycled never used phys() */ | ||
466 | .long sys_ni_syscall /* old lock syscall holder */ | ||
467 | .long sys_ioctl | ||
468 | .long sys_fcntl /* 55 */ | ||
469 | .long sys_ni_syscall /* old mpx syscall holder */ | ||
470 | .long sys_setpgid | ||
471 | .long sys_ni_syscall /* old ulimit syscall holder */ | ||
472 | .long sys_ni_syscall | ||
473 | .long sys_umask /* 60 */ | ||
474 | .long sys_chroot | ||
475 | .long sys_ustat | ||
476 | .long sys_dup2 | ||
477 | .long sys_getppid | ||
478 | .long sys_getpgrp /* 65 */ | ||
479 | .long sys_setsid | ||
480 | .long sys_sigaction | ||
481 | .long sys_sgetmask | ||
482 | .long sys_ssetmask | ||
483 | .long sys_setreuid16 /* 70 */ | ||
484 | .long sys_setregid16 | ||
485 | .long sys_sigsuspend | ||
486 | .long sys_sigpending | ||
487 | .long sys_sethostname | ||
488 | .long sys_setrlimit /* 75 */ | ||
489 | .long sys_old_getrlimit | ||
490 | .long sys_getrusage | ||
491 | .long sys_gettimeofday | ||
492 | .long sys_settimeofday | ||
493 | .long sys_getgroups16 /* 80 */ | ||
494 | .long sys_setgroups16 | ||
495 | .long sys_old_select | ||
496 | .long sys_symlink | ||
497 | .long sys_lstat | ||
498 | .long sys_readlink /* 85 */ | ||
499 | .long sys_uselib | ||
500 | .long sys_swapon | ||
501 | .long sys_reboot | ||
502 | .long sys_old_readdir | ||
503 | .long sys_old_mmap /* 90 */ | ||
504 | .long sys_munmap | ||
505 | .long sys_truncate | ||
506 | .long sys_ftruncate | ||
507 | .long sys_fchmod | ||
508 | .long sys_fchown16 /* 95 */ | ||
509 | .long sys_getpriority | ||
510 | .long sys_setpriority | ||
511 | .long sys_ni_syscall /* old profil syscall holder */ | ||
512 | .long sys_statfs | ||
513 | .long sys_fstatfs /* 100 */ | ||
514 | .long sys_ni_syscall /* ioperm for i386 */ | ||
515 | .long sys_socketcall | ||
516 | .long sys_syslog | ||
517 | .long sys_setitimer | ||
518 | .long sys_getitimer /* 105 */ | ||
519 | .long sys_newstat | ||
520 | .long sys_newlstat | ||
521 | .long sys_newfstat | ||
522 | .long sys_ni_syscall | ||
523 | .long sys_ni_syscall /* 110 */ /* iopl for i386 */ | ||
524 | .long sys_vhangup | ||
525 | .long sys_ni_syscall /* obsolete idle() syscall */ | ||
526 | .long sys_ni_syscall /* vm86old for i386 */ | ||
527 | .long sys_wait4 | ||
528 | .long sys_swapoff /* 115 */ | ||
529 | .long sys_sysinfo | ||
530 | .long sys_ipc | ||
531 | .long sys_fsync | ||
532 | .long sys_sigreturn | ||
533 | .long sys_clone /* 120 */ | ||
534 | .long sys_setdomainname | ||
535 | .long sys_newuname | ||
536 | .long sys_cacheflush /* modify_ldt for i386 */ | ||
537 | .long sys_adjtimex | ||
538 | .long sys_mprotect /* 125 */ | ||
539 | .long sys_sigprocmask | ||
540 | .long sys_ni_syscall /* old "create_module" */ | ||
541 | .long sys_init_module | ||
542 | .long sys_delete_module | ||
543 | .long sys_ni_syscall /* 130 - old "get_kernel_syms" */ | ||
544 | .long sys_quotactl | ||
545 | .long sys_getpgid | ||
546 | .long sys_fchdir | ||
547 | .long sys_bdflush | ||
548 | .long sys_sysfs /* 135 */ | ||
549 | .long sys_personality | ||
550 | .long sys_ni_syscall /* for afs_syscall */ | ||
551 | .long sys_setfsuid16 | ||
552 | .long sys_setfsgid16 | ||
553 | .long sys_llseek /* 140 */ | ||
554 | .long sys_getdents | ||
555 | .long sys_select | ||
556 | .long sys_flock | ||
557 | .long sys_msync | ||
558 | .long sys_readv /* 145 */ | ||
559 | .long sys_writev | ||
560 | .long sys_getsid | ||
561 | .long sys_fdatasync | ||
562 | .long sys_sysctl | ||
563 | .long sys_mlock /* 150 */ | ||
564 | .long sys_munlock | ||
565 | .long sys_mlockall | ||
566 | .long sys_munlockall | ||
567 | .long sys_sched_setparam | ||
568 | .long sys_sched_getparam /* 155 */ | ||
569 | .long sys_sched_setscheduler | ||
570 | .long sys_sched_getscheduler | ||
571 | .long sys_sched_yield | ||
572 | .long sys_sched_get_priority_max | ||
573 | .long sys_sched_get_priority_min /* 160 */ | ||
574 | .long sys_sched_rr_get_interval | ||
575 | .long sys_nanosleep | ||
576 | .long sys_mremap | ||
577 | .long sys_setresuid16 | ||
578 | .long sys_getresuid16 /* 165 */ | ||
579 | .long sys_getpagesize | ||
580 | .long sys_ni_syscall /* old sys_query_module */ | ||
581 | .long sys_poll | ||
582 | .long sys_nfsservctl | ||
583 | .long sys_setresgid16 /* 170 */ | ||
584 | .long sys_getresgid16 | ||
585 | .long sys_prctl | ||
586 | .long sys_rt_sigreturn | ||
587 | .long sys_rt_sigaction | ||
588 | .long sys_rt_sigprocmask /* 175 */ | ||
589 | .long sys_rt_sigpending | ||
590 | .long sys_rt_sigtimedwait | ||
591 | .long sys_rt_sigqueueinfo | ||
592 | .long sys_rt_sigsuspend | ||
593 | .long sys_pread64 /* 180 */ | ||
594 | .long sys_pwrite64 | ||
595 | .long sys_lchown16; | ||
596 | .long sys_getcwd | ||
597 | .long sys_capget | ||
598 | .long sys_capset /* 185 */ | ||
599 | .long sys_sigaltstack | ||
600 | .long sys_sendfile | ||
601 | .long sys_ni_syscall /* streams1 */ | ||
602 | .long sys_ni_syscall /* streams2 */ | ||
603 | .long sys_vfork /* 190 */ | ||
604 | .long sys_getrlimit | ||
605 | .long sys_mmap2 | ||
606 | .long sys_truncate64 | ||
607 | .long sys_ftruncate64 | ||
608 | .long sys_stat64 /* 195 */ | ||
609 | .long sys_lstat64 | ||
610 | .long sys_fstat64 | ||
611 | .long sys_chown | ||
612 | .long sys_getuid | ||
613 | .long sys_getgid /* 200 */ | ||
614 | .long sys_geteuid | ||
615 | .long sys_getegid | ||
616 | .long sys_setreuid | ||
617 | .long sys_setregid | ||
618 | .long sys_getgroups /* 205 */ | ||
619 | .long sys_setgroups | ||
620 | .long sys_fchown | ||
621 | .long sys_setresuid | ||
622 | .long sys_getresuid | ||
623 | .long sys_setresgid /* 210 */ | ||
624 | .long sys_getresgid | ||
625 | .long sys_lchown | ||
626 | .long sys_setuid | ||
627 | .long sys_setgid | ||
628 | .long sys_setfsuid /* 215 */ | ||
629 | .long sys_setfsgid | ||
630 | .long sys_pivot_root | ||
631 | .long sys_ni_syscall | ||
632 | .long sys_ni_syscall | ||
633 | .long sys_getdents64 /* 220 */ | ||
634 | .long sys_gettid | ||
635 | .long sys_tkill | ||
636 | .long sys_setxattr | ||
637 | .long sys_lsetxattr | ||
638 | .long sys_fsetxattr /* 225 */ | ||
639 | .long sys_getxattr | ||
640 | .long sys_lgetxattr | ||
641 | .long sys_fgetxattr | ||
642 | .long sys_listxattr | ||
643 | .long sys_llistxattr /* 230 */ | ||
644 | .long sys_flistxattr | ||
645 | .long sys_removexattr | ||
646 | .long sys_lremovexattr | ||
647 | .long sys_fremovexattr | ||
648 | .long sys_futex /* 235 */ | ||
649 | .long sys_sendfile64 | ||
650 | .long sys_mincore | ||
651 | .long sys_madvise | ||
652 | .long sys_fcntl64 | ||
653 | .long sys_readahead /* 240 */ | ||
654 | .long sys_io_setup | ||
655 | .long sys_io_destroy | ||
656 | .long sys_io_getevents | ||
657 | .long sys_io_submit | ||
658 | .long sys_io_cancel /* 245 */ | ||
659 | .long sys_fadvise64 | ||
660 | .long sys_exit_group | ||
661 | .long sys_lookup_dcookie | ||
662 | .long sys_epoll_create | ||
663 | .long sys_epoll_ctl /* 250 */ | ||
664 | .long sys_epoll_wait | ||
665 | .long sys_remap_file_pages | ||
666 | .long sys_set_tid_address | ||
667 | .long sys_timer_create | ||
668 | .long sys_timer_settime /* 255 */ | ||
669 | .long sys_timer_gettime | ||
670 | .long sys_timer_getoverrun | ||
671 | .long sys_timer_delete | ||
672 | .long sys_clock_settime | ||
673 | .long sys_clock_gettime /* 260 */ | ||
674 | .long sys_clock_getres | ||
675 | .long sys_clock_nanosleep | ||
676 | .long sys_statfs64 | ||
677 | .long sys_fstatfs64 | ||
678 | .long sys_tgkill /* 265 */ | ||
679 | .long sys_utimes | ||
680 | .long sys_fadvise64_64 | ||
681 | .long sys_mbind | ||
682 | .long sys_get_mempolicy | ||
683 | .long sys_set_mempolicy /* 270 */ | ||
684 | .long sys_mq_open | ||
685 | .long sys_mq_unlink | ||
686 | .long sys_mq_timedsend | ||
687 | .long sys_mq_timedreceive | ||
688 | .long sys_mq_notify /* 275 */ | ||
689 | .long sys_mq_getsetattr | ||
690 | .long sys_waitid | ||
691 | .long sys_ni_syscall /* for sys_vserver */ | ||
692 | .long sys_add_key | ||
693 | .long sys_request_key /* 280 */ | ||
694 | .long sys_keyctl | ||
695 | .long sys_ioprio_set | ||
696 | .long sys_ioprio_get | ||
697 | .long sys_inotify_init | ||
698 | .long sys_inotify_add_watch /* 285 */ | ||
699 | .long sys_inotify_rm_watch | ||
700 | .long sys_migrate_pages | ||
701 | .long sys_openat | ||
702 | .long sys_mkdirat | ||
703 | .long sys_mknodat /* 290 */ | ||
704 | .long sys_fchownat | ||
705 | .long sys_futimesat | ||
706 | .long sys_fstatat64 | ||
707 | .long sys_unlinkat | ||
708 | .long sys_renameat /* 295 */ | ||
709 | .long sys_linkat | ||
710 | .long sys_symlinkat | ||
711 | .long sys_readlinkat | ||
712 | .long sys_fchmodat | ||
713 | .long sys_faccessat /* 300 */ | ||
714 | .long sys_ni_syscall /* Reserved for pselect6 */ | ||
715 | .long sys_ni_syscall /* Reserved for ppoll */ | ||
716 | .long sys_unshare | ||
717 | .long sys_set_robust_list | ||
718 | .long sys_get_robust_list /* 305 */ | ||
719 | .long sys_splice | ||
720 | .long sys_sync_file_range | ||
721 | .long sys_tee | ||
722 | .long sys_vmsplice | ||
723 | .long sys_move_pages /* 310 */ | ||
724 | .long sys_sched_setaffinity | ||
725 | .long sys_sched_getaffinity | ||
726 | .long sys_kexec_load | ||
727 | .long sys_getcpu | ||
728 | .long sys_epoll_pwait /* 315 */ | ||
729 | .long sys_utimensat | ||
730 | .long sys_signalfd | ||
731 | .long sys_timerfd_create | ||
732 | .long sys_eventfd | ||
733 | .long sys_fallocate /* 320 */ | ||
734 | .long sys_timerfd_settime | ||
735 | .long sys_timerfd_gettime | ||
736 | .long sys_signalfd4 | ||
737 | .long sys_eventfd2 | ||
738 | .long sys_epoll_create1 /* 325 */ | ||
739 | .long sys_dup3 | ||
740 | .long sys_pipe2 | ||
741 | .long sys_inotify_init1 | ||
742 | .long sys_preadv | ||
743 | .long sys_pwritev /* 330 */ | ||
744 | .long sys_rt_tgsigqueueinfo | ||
745 | .long sys_perf_event_open | ||
746 | .long sys_get_thread_area | ||
747 | .long sys_set_thread_area | ||
748 | .long sys_atomic_cmpxchg_32 /* 335 */ | ||
749 | .long sys_atomic_barrier | ||
750 | .long sys_fanotify_init | ||
751 | .long sys_fanotify_mark | ||
752 | .long sys_prlimit64 | ||
753 | |||
diff --git a/arch/m68k/kernel/entry_mm.S b/arch/m68k/kernel/entry_mm.S new file mode 100644 index 000000000000..1559dea36e55 --- /dev/null +++ b/arch/m68k/kernel/entry_mm.S | |||
@@ -0,0 +1,753 @@ | |||
1 | /* -*- mode: asm -*- | ||
2 | * | ||
3 | * linux/arch/m68k/kernel/entry.S | ||
4 | * | ||
5 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
6 | * | ||
7 | * This file is subject to the terms and conditions of the GNU General Public | ||
8 | * License. See the file README.legal in the main directory of this archive | ||
9 | * for more details. | ||
10 | * | ||
11 | * Linux/m68k support by Hamish Macdonald | ||
12 | * | ||
13 | * 68060 fixes by Jesper Skov | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | /* | ||
18 | * entry.S contains the system-call and fault low-level handling routines. | ||
19 | * This also contains the timer-interrupt handler, as well as all interrupts | ||
20 | * and faults that can result in a task-switch. | ||
21 | * | ||
22 | * NOTE: This code handles signal-recognition, which happens every time | ||
23 | * after a timer-interrupt and after each system call. | ||
24 | * | ||
25 | */ | ||
26 | |||
27 | /* | ||
28 | * 12/03/96 Jes: Currently we only support m68k single-cpu systems, so | ||
29 | * all pointers that used to be 'current' are now entry | ||
30 | * number 0 in the 'current_set' list. | ||
31 | * | ||
32 | * 6/05/00 RZ: addedd writeback completion after return from sighandler | ||
33 | * for 68040 | ||
34 | */ | ||
35 | |||
36 | #include <linux/linkage.h> | ||
37 | #include <asm/entry.h> | ||
38 | #include <asm/errno.h> | ||
39 | #include <asm/setup.h> | ||
40 | #include <asm/segment.h> | ||
41 | #include <asm/traps.h> | ||
42 | #include <asm/unistd.h> | ||
43 | |||
44 | #include <asm/asm-offsets.h> | ||
45 | |||
46 | .globl system_call, buserr, trap, resume | ||
47 | .globl sys_call_table | ||
48 | .globl sys_fork, sys_clone, sys_vfork | ||
49 | .globl ret_from_interrupt, bad_interrupt | ||
50 | .globl auto_irqhandler_fixup | ||
51 | .globl user_irqvec_fixup, user_irqhandler_fixup | ||
52 | |||
53 | .text | ||
54 | ENTRY(buserr) | ||
55 | SAVE_ALL_INT | ||
56 | GET_CURRENT(%d0) | ||
57 | movel %sp,%sp@- | stack frame pointer argument | ||
58 | bsrl buserr_c | ||
59 | addql #4,%sp | ||
60 | jra .Lret_from_exception | ||
61 | |||
62 | ENTRY(trap) | ||
63 | SAVE_ALL_INT | ||
64 | GET_CURRENT(%d0) | ||
65 | movel %sp,%sp@- | stack frame pointer argument | ||
66 | bsrl trap_c | ||
67 | addql #4,%sp | ||
68 | jra .Lret_from_exception | ||
69 | |||
70 | | After a fork we jump here directly from resume, | ||
71 | | so that %d1 contains the previous task | ||
72 | | schedule_tail now used regardless of CONFIG_SMP | ||
73 | ENTRY(ret_from_fork) | ||
74 | movel %d1,%sp@- | ||
75 | jsr schedule_tail | ||
76 | addql #4,%sp | ||
77 | jra .Lret_from_exception | ||
78 | |||
79 | do_trace_entry: | ||
80 | movel #-ENOSYS,%sp@(PT_OFF_D0)| needed for strace | ||
81 | subql #4,%sp | ||
82 | SAVE_SWITCH_STACK | ||
83 | jbsr syscall_trace | ||
84 | RESTORE_SWITCH_STACK | ||
85 | addql #4,%sp | ||
86 | movel %sp@(PT_OFF_ORIG_D0),%d0 | ||
87 | cmpl #NR_syscalls,%d0 | ||
88 | jcs syscall | ||
89 | badsys: | ||
90 | movel #-ENOSYS,%sp@(PT_OFF_D0) | ||
91 | jra ret_from_syscall | ||
92 | |||
93 | do_trace_exit: | ||
94 | subql #4,%sp | ||
95 | SAVE_SWITCH_STACK | ||
96 | jbsr syscall_trace | ||
97 | RESTORE_SWITCH_STACK | ||
98 | addql #4,%sp | ||
99 | jra .Lret_from_exception | ||
100 | |||
101 | ENTRY(ret_from_signal) | ||
102 | tstb %curptr@(TASK_INFO+TINFO_FLAGS+2) | ||
103 | jge 1f | ||
104 | jbsr syscall_trace | ||
105 | 1: RESTORE_SWITCH_STACK | ||
106 | addql #4,%sp | ||
107 | /* on 68040 complete pending writebacks if any */ | ||
108 | #ifdef CONFIG_M68040 | ||
109 | bfextu %sp@(PT_OFF_FORMATVEC){#0,#4},%d0 | ||
110 | subql #7,%d0 | bus error frame ? | ||
111 | jbne 1f | ||
112 | movel %sp,%sp@- | ||
113 | jbsr berr_040cleanup | ||
114 | addql #4,%sp | ||
115 | 1: | ||
116 | #endif | ||
117 | jra .Lret_from_exception | ||
118 | |||
119 | ENTRY(system_call) | ||
120 | SAVE_ALL_SYS | ||
121 | |||
122 | GET_CURRENT(%d1) | ||
123 | | save top of frame | ||
124 | movel %sp,%curptr@(TASK_THREAD+THREAD_ESP0) | ||
125 | |||
126 | | syscall trace? | ||
127 | tstb %curptr@(TASK_INFO+TINFO_FLAGS+2) | ||
128 | jmi do_trace_entry | ||
129 | cmpl #NR_syscalls,%d0 | ||
130 | jcc badsys | ||
131 | syscall: | ||
132 | jbsr @(sys_call_table,%d0:l:4)@(0) | ||
133 | movel %d0,%sp@(PT_OFF_D0) | save the return value | ||
134 | ret_from_syscall: | ||
135 | |oriw #0x0700,%sr | ||
136 | movew %curptr@(TASK_INFO+TINFO_FLAGS+2),%d0 | ||
137 | jne syscall_exit_work | ||
138 | 1: RESTORE_ALL | ||
139 | |||
140 | syscall_exit_work: | ||
141 | btst #5,%sp@(PT_OFF_SR) | check if returning to kernel | ||
142 | bnes 1b | if so, skip resched, signals | ||
143 | lslw #1,%d0 | ||
144 | jcs do_trace_exit | ||
145 | jmi do_delayed_trace | ||
146 | lslw #8,%d0 | ||
147 | jmi do_signal_return | ||
148 | pea resume_userspace | ||
149 | jra schedule | ||
150 | |||
151 | |||
152 | ENTRY(ret_from_exception) | ||
153 | .Lret_from_exception: | ||
154 | btst #5,%sp@(PT_OFF_SR) | check if returning to kernel | ||
155 | bnes 1f | if so, skip resched, signals | ||
156 | | only allow interrupts when we are really the last one on the | ||
157 | | kernel stack, otherwise stack overflow can occur during | ||
158 | | heavy interrupt load | ||
159 | andw #ALLOWINT,%sr | ||
160 | |||
161 | resume_userspace: | ||
162 | moveb %curptr@(TASK_INFO+TINFO_FLAGS+3),%d0 | ||
163 | jne exit_work | ||
164 | 1: RESTORE_ALL | ||
165 | |||
166 | exit_work: | ||
167 | | save top of frame | ||
168 | movel %sp,%curptr@(TASK_THREAD+THREAD_ESP0) | ||
169 | lslb #1,%d0 | ||
170 | jmi do_signal_return | ||
171 | pea resume_userspace | ||
172 | jra schedule | ||
173 | |||
174 | |||
175 | do_signal_return: | ||
176 | |andw #ALLOWINT,%sr | ||
177 | subql #4,%sp | dummy return address | ||
178 | SAVE_SWITCH_STACK | ||
179 | pea %sp@(SWITCH_STACK_SIZE) | ||
180 | bsrl do_signal | ||
181 | addql #4,%sp | ||
182 | RESTORE_SWITCH_STACK | ||
183 | addql #4,%sp | ||
184 | jbra resume_userspace | ||
185 | |||
186 | do_delayed_trace: | ||
187 | bclr #7,%sp@(PT_OFF_SR) | clear trace bit in SR | ||
188 | pea 1 | send SIGTRAP | ||
189 | movel %curptr,%sp@- | ||
190 | pea LSIGTRAP | ||
191 | jbsr send_sig | ||
192 | addql #8,%sp | ||
193 | addql #4,%sp | ||
194 | jbra resume_userspace | ||
195 | |||
196 | |||
197 | /* This is the main interrupt handler for autovector interrupts */ | ||
198 | |||
199 | ENTRY(auto_inthandler) | ||
200 | SAVE_ALL_INT | ||
201 | GET_CURRENT(%d0) | ||
202 | addqb #1,%curptr@(TASK_INFO+TINFO_PREEMPT+1) | ||
203 | | put exception # in d0 | ||
204 | bfextu %sp@(PT_OFF_FORMATVEC){#4,#10},%d0 | ||
205 | subw #VEC_SPUR,%d0 | ||
206 | |||
207 | movel %sp,%sp@- | ||
208 | movel %d0,%sp@- | put vector # on stack | ||
209 | auto_irqhandler_fixup = . + 2 | ||
210 | jsr __m68k_handle_int | process the IRQ | ||
211 | addql #8,%sp | pop parameters off stack | ||
212 | |||
213 | ret_from_interrupt: | ||
214 | subqb #1,%curptr@(TASK_INFO+TINFO_PREEMPT+1) | ||
215 | jeq ret_from_last_interrupt | ||
216 | 2: RESTORE_ALL | ||
217 | |||
218 | ALIGN | ||
219 | ret_from_last_interrupt: | ||
220 | moveq #(~ALLOWINT>>8)&0xff,%d0 | ||
221 | andb %sp@(PT_OFF_SR),%d0 | ||
222 | jne 2b | ||
223 | |||
224 | /* check if we need to do software interrupts */ | ||
225 | tstl irq_stat+CPUSTAT_SOFTIRQ_PENDING | ||
226 | jeq .Lret_from_exception | ||
227 | pea ret_from_exception | ||
228 | jra do_softirq | ||
229 | |||
230 | /* Handler for user defined interrupt vectors */ | ||
231 | |||
232 | ENTRY(user_inthandler) | ||
233 | SAVE_ALL_INT | ||
234 | GET_CURRENT(%d0) | ||
235 | addqb #1,%curptr@(TASK_INFO+TINFO_PREEMPT+1) | ||
236 | | put exception # in d0 | ||
237 | bfextu %sp@(PT_OFF_FORMATVEC){#4,#10},%d0 | ||
238 | user_irqvec_fixup = . + 2 | ||
239 | subw #VEC_USER,%d0 | ||
240 | |||
241 | movel %sp,%sp@- | ||
242 | movel %d0,%sp@- | put vector # on stack | ||
243 | user_irqhandler_fixup = . + 2 | ||
244 | jsr __m68k_handle_int | process the IRQ | ||
245 | addql #8,%sp | pop parameters off stack | ||
246 | |||
247 | subqb #1,%curptr@(TASK_INFO+TINFO_PREEMPT+1) | ||
248 | jeq ret_from_last_interrupt | ||
249 | RESTORE_ALL | ||
250 | |||
251 | /* Handler for uninitialized and spurious interrupts */ | ||
252 | |||
253 | ENTRY(bad_inthandler) | ||
254 | SAVE_ALL_INT | ||
255 | GET_CURRENT(%d0) | ||
256 | addqb #1,%curptr@(TASK_INFO+TINFO_PREEMPT+1) | ||
257 | |||
258 | movel %sp,%sp@- | ||
259 | jsr handle_badint | ||
260 | addql #4,%sp | ||
261 | |||
262 | subqb #1,%curptr@(TASK_INFO+TINFO_PREEMPT+1) | ||
263 | jeq ret_from_last_interrupt | ||
264 | RESTORE_ALL | ||
265 | |||
266 | |||
267 | ENTRY(sys_fork) | ||
268 | SAVE_SWITCH_STACK | ||
269 | pea %sp@(SWITCH_STACK_SIZE) | ||
270 | jbsr m68k_fork | ||
271 | addql #4,%sp | ||
272 | RESTORE_SWITCH_STACK | ||
273 | rts | ||
274 | |||
275 | ENTRY(sys_clone) | ||
276 | SAVE_SWITCH_STACK | ||
277 | pea %sp@(SWITCH_STACK_SIZE) | ||
278 | jbsr m68k_clone | ||
279 | addql #4,%sp | ||
280 | RESTORE_SWITCH_STACK | ||
281 | rts | ||
282 | |||
283 | ENTRY(sys_vfork) | ||
284 | SAVE_SWITCH_STACK | ||
285 | pea %sp@(SWITCH_STACK_SIZE) | ||
286 | jbsr m68k_vfork | ||
287 | addql #4,%sp | ||
288 | RESTORE_SWITCH_STACK | ||
289 | rts | ||
290 | |||
291 | ENTRY(sys_sigreturn) | ||
292 | SAVE_SWITCH_STACK | ||
293 | jbsr do_sigreturn | ||
294 | RESTORE_SWITCH_STACK | ||
295 | rts | ||
296 | |||
297 | ENTRY(sys_rt_sigreturn) | ||
298 | SAVE_SWITCH_STACK | ||
299 | jbsr do_rt_sigreturn | ||
300 | RESTORE_SWITCH_STACK | ||
301 | rts | ||
302 | |||
303 | resume: | ||
304 | /* | ||
305 | * Beware - when entering resume, prev (the current task) is | ||
306 | * in a0, next (the new task) is in a1,so don't change these | ||
307 | * registers until their contents are no longer needed. | ||
308 | */ | ||
309 | |||
310 | /* save sr */ | ||
311 | movew %sr,%a0@(TASK_THREAD+THREAD_SR) | ||
312 | |||
313 | /* save fs (sfc,%dfc) (may be pointing to kernel memory) */ | ||
314 | movec %sfc,%d0 | ||
315 | movew %d0,%a0@(TASK_THREAD+THREAD_FS) | ||
316 | |||
317 | /* save usp */ | ||
318 | /* it is better to use a movel here instead of a movew 8*) */ | ||
319 | movec %usp,%d0 | ||
320 | movel %d0,%a0@(TASK_THREAD+THREAD_USP) | ||
321 | |||
322 | /* save non-scratch registers on stack */ | ||
323 | SAVE_SWITCH_STACK | ||
324 | |||
325 | /* save current kernel stack pointer */ | ||
326 | movel %sp,%a0@(TASK_THREAD+THREAD_KSP) | ||
327 | |||
328 | /* save floating point context */ | ||
329 | #ifndef CONFIG_M68KFPU_EMU_ONLY | ||
330 | #ifdef CONFIG_M68KFPU_EMU | ||
331 | tstl m68k_fputype | ||
332 | jeq 3f | ||
333 | #endif | ||
334 | fsave %a0@(TASK_THREAD+THREAD_FPSTATE) | ||
335 | |||
336 | #if defined(CONFIG_M68060) | ||
337 | #if !defined(CPU_M68060_ONLY) | ||
338 | btst #3,m68k_cputype+3 | ||
339 | beqs 1f | ||
340 | #endif | ||
341 | /* The 060 FPU keeps status in bits 15-8 of the first longword */ | ||
342 | tstb %a0@(TASK_THREAD+THREAD_FPSTATE+2) | ||
343 | jeq 3f | ||
344 | #if !defined(CPU_M68060_ONLY) | ||
345 | jra 2f | ||
346 | #endif | ||
347 | #endif /* CONFIG_M68060 */ | ||
348 | #if !defined(CPU_M68060_ONLY) | ||
349 | 1: tstb %a0@(TASK_THREAD+THREAD_FPSTATE) | ||
350 | jeq 3f | ||
351 | #endif | ||
352 | 2: fmovemx %fp0-%fp7,%a0@(TASK_THREAD+THREAD_FPREG) | ||
353 | fmoveml %fpcr/%fpsr/%fpiar,%a0@(TASK_THREAD+THREAD_FPCNTL) | ||
354 | 3: | ||
355 | #endif /* CONFIG_M68KFPU_EMU_ONLY */ | ||
356 | /* Return previous task in %d1 */ | ||
357 | movel %curptr,%d1 | ||
358 | |||
359 | /* switch to new task (a1 contains new task) */ | ||
360 | movel %a1,%curptr | ||
361 | |||
362 | /* restore floating point context */ | ||
363 | #ifndef CONFIG_M68KFPU_EMU_ONLY | ||
364 | #ifdef CONFIG_M68KFPU_EMU | ||
365 | tstl m68k_fputype | ||
366 | jeq 4f | ||
367 | #endif | ||
368 | #if defined(CONFIG_M68060) | ||
369 | #if !defined(CPU_M68060_ONLY) | ||
370 | btst #3,m68k_cputype+3 | ||
371 | beqs 1f | ||
372 | #endif | ||
373 | /* The 060 FPU keeps status in bits 15-8 of the first longword */ | ||
374 | tstb %a1@(TASK_THREAD+THREAD_FPSTATE+2) | ||
375 | jeq 3f | ||
376 | #if !defined(CPU_M68060_ONLY) | ||
377 | jra 2f | ||
378 | #endif | ||
379 | #endif /* CONFIG_M68060 */ | ||
380 | #if !defined(CPU_M68060_ONLY) | ||
381 | 1: tstb %a1@(TASK_THREAD+THREAD_FPSTATE) | ||
382 | jeq 3f | ||
383 | #endif | ||
384 | 2: fmovemx %a1@(TASK_THREAD+THREAD_FPREG),%fp0-%fp7 | ||
385 | fmoveml %a1@(TASK_THREAD+THREAD_FPCNTL),%fpcr/%fpsr/%fpiar | ||
386 | 3: frestore %a1@(TASK_THREAD+THREAD_FPSTATE) | ||
387 | 4: | ||
388 | #endif /* CONFIG_M68KFPU_EMU_ONLY */ | ||
389 | |||
390 | /* restore the kernel stack pointer */ | ||
391 | movel %a1@(TASK_THREAD+THREAD_KSP),%sp | ||
392 | |||
393 | /* restore non-scratch registers */ | ||
394 | RESTORE_SWITCH_STACK | ||
395 | |||
396 | /* restore user stack pointer */ | ||
397 | movel %a1@(TASK_THREAD+THREAD_USP),%a0 | ||
398 | movel %a0,%usp | ||
399 | |||
400 | /* restore fs (sfc,%dfc) */ | ||
401 | movew %a1@(TASK_THREAD+THREAD_FS),%a0 | ||
402 | movec %a0,%sfc | ||
403 | movec %a0,%dfc | ||
404 | |||
405 | /* restore status register */ | ||
406 | movew %a1@(TASK_THREAD+THREAD_SR),%sr | ||
407 | |||
408 | rts | ||
409 | |||
410 | .data | ||
411 | ALIGN | ||
412 | sys_call_table: | ||
413 | .long sys_restart_syscall /* 0 - old "setup()" system call, used for restarting */ | ||
414 | .long sys_exit | ||
415 | .long sys_fork | ||
416 | .long sys_read | ||
417 | .long sys_write | ||
418 | .long sys_open /* 5 */ | ||
419 | .long sys_close | ||
420 | .long sys_waitpid | ||
421 | .long sys_creat | ||
422 | .long sys_link | ||
423 | .long sys_unlink /* 10 */ | ||
424 | .long sys_execve | ||
425 | .long sys_chdir | ||
426 | .long sys_time | ||
427 | .long sys_mknod | ||
428 | .long sys_chmod /* 15 */ | ||
429 | .long sys_chown16 | ||
430 | .long sys_ni_syscall /* old break syscall holder */ | ||
431 | .long sys_stat | ||
432 | .long sys_lseek | ||
433 | .long sys_getpid /* 20 */ | ||
434 | .long sys_mount | ||
435 | .long sys_oldumount | ||
436 | .long sys_setuid16 | ||
437 | .long sys_getuid16 | ||
438 | .long sys_stime /* 25 */ | ||
439 | .long sys_ptrace | ||
440 | .long sys_alarm | ||
441 | .long sys_fstat | ||
442 | .long sys_pause | ||
443 | .long sys_utime /* 30 */ | ||
444 | .long sys_ni_syscall /* old stty syscall holder */ | ||
445 | .long sys_ni_syscall /* old gtty syscall holder */ | ||
446 | .long sys_access | ||
447 | .long sys_nice | ||
448 | .long sys_ni_syscall /* 35 */ /* old ftime syscall holder */ | ||
449 | .long sys_sync | ||
450 | .long sys_kill | ||
451 | .long sys_rename | ||
452 | .long sys_mkdir | ||
453 | .long sys_rmdir /* 40 */ | ||
454 | .long sys_dup | ||
455 | .long sys_pipe | ||
456 | .long sys_times | ||
457 | .long sys_ni_syscall /* old prof syscall holder */ | ||
458 | .long sys_brk /* 45 */ | ||
459 | .long sys_setgid16 | ||
460 | .long sys_getgid16 | ||
461 | .long sys_signal | ||
462 | .long sys_geteuid16 | ||
463 | .long sys_getegid16 /* 50 */ | ||
464 | .long sys_acct | ||
465 | .long sys_umount /* recycled never used phys() */ | ||
466 | .long sys_ni_syscall /* old lock syscall holder */ | ||
467 | .long sys_ioctl | ||
468 | .long sys_fcntl /* 55 */ | ||
469 | .long sys_ni_syscall /* old mpx syscall holder */ | ||
470 | .long sys_setpgid | ||
471 | .long sys_ni_syscall /* old ulimit syscall holder */ | ||
472 | .long sys_ni_syscall | ||
473 | .long sys_umask /* 60 */ | ||
474 | .long sys_chroot | ||
475 | .long sys_ustat | ||
476 | .long sys_dup2 | ||
477 | .long sys_getppid | ||
478 | .long sys_getpgrp /* 65 */ | ||
479 | .long sys_setsid | ||
480 | .long sys_sigaction | ||
481 | .long sys_sgetmask | ||
482 | .long sys_ssetmask | ||
483 | .long sys_setreuid16 /* 70 */ | ||
484 | .long sys_setregid16 | ||
485 | .long sys_sigsuspend | ||
486 | .long sys_sigpending | ||
487 | .long sys_sethostname | ||
488 | .long sys_setrlimit /* 75 */ | ||
489 | .long sys_old_getrlimit | ||
490 | .long sys_getrusage | ||
491 | .long sys_gettimeofday | ||
492 | .long sys_settimeofday | ||
493 | .long sys_getgroups16 /* 80 */ | ||
494 | .long sys_setgroups16 | ||
495 | .long sys_old_select | ||
496 | .long sys_symlink | ||
497 | .long sys_lstat | ||
498 | .long sys_readlink /* 85 */ | ||
499 | .long sys_uselib | ||
500 | .long sys_swapon | ||
501 | .long sys_reboot | ||
502 | .long sys_old_readdir | ||
503 | .long sys_old_mmap /* 90 */ | ||
504 | .long sys_munmap | ||
505 | .long sys_truncate | ||
506 | .long sys_ftruncate | ||
507 | .long sys_fchmod | ||
508 | .long sys_fchown16 /* 95 */ | ||
509 | .long sys_getpriority | ||
510 | .long sys_setpriority | ||
511 | .long sys_ni_syscall /* old profil syscall holder */ | ||
512 | .long sys_statfs | ||
513 | .long sys_fstatfs /* 100 */ | ||
514 | .long sys_ni_syscall /* ioperm for i386 */ | ||
515 | .long sys_socketcall | ||
516 | .long sys_syslog | ||
517 | .long sys_setitimer | ||
518 | .long sys_getitimer /* 105 */ | ||
519 | .long sys_newstat | ||
520 | .long sys_newlstat | ||
521 | .long sys_newfstat | ||
522 | .long sys_ni_syscall | ||
523 | .long sys_ni_syscall /* 110 */ /* iopl for i386 */ | ||
524 | .long sys_vhangup | ||
525 | .long sys_ni_syscall /* obsolete idle() syscall */ | ||
526 | .long sys_ni_syscall /* vm86old for i386 */ | ||
527 | .long sys_wait4 | ||
528 | .long sys_swapoff /* 115 */ | ||
529 | .long sys_sysinfo | ||
530 | .long sys_ipc | ||
531 | .long sys_fsync | ||
532 | .long sys_sigreturn | ||
533 | .long sys_clone /* 120 */ | ||
534 | .long sys_setdomainname | ||
535 | .long sys_newuname | ||
536 | .long sys_cacheflush /* modify_ldt for i386 */ | ||
537 | .long sys_adjtimex | ||
538 | .long sys_mprotect /* 125 */ | ||
539 | .long sys_sigprocmask | ||
540 | .long sys_ni_syscall /* old "create_module" */ | ||
541 | .long sys_init_module | ||
542 | .long sys_delete_module | ||
543 | .long sys_ni_syscall /* 130 - old "get_kernel_syms" */ | ||
544 | .long sys_quotactl | ||
545 | .long sys_getpgid | ||
546 | .long sys_fchdir | ||
547 | .long sys_bdflush | ||
548 | .long sys_sysfs /* 135 */ | ||
549 | .long sys_personality | ||
550 | .long sys_ni_syscall /* for afs_syscall */ | ||
551 | .long sys_setfsuid16 | ||
552 | .long sys_setfsgid16 | ||
553 | .long sys_llseek /* 140 */ | ||
554 | .long sys_getdents | ||
555 | .long sys_select | ||
556 | .long sys_flock | ||
557 | .long sys_msync | ||
558 | .long sys_readv /* 145 */ | ||
559 | .long sys_writev | ||
560 | .long sys_getsid | ||
561 | .long sys_fdatasync | ||
562 | .long sys_sysctl | ||
563 | .long sys_mlock /* 150 */ | ||
564 | .long sys_munlock | ||
565 | .long sys_mlockall | ||
566 | .long sys_munlockall | ||
567 | .long sys_sched_setparam | ||
568 | .long sys_sched_getparam /* 155 */ | ||
569 | .long sys_sched_setscheduler | ||
570 | .long sys_sched_getscheduler | ||
571 | .long sys_sched_yield | ||
572 | .long sys_sched_get_priority_max | ||
573 | .long sys_sched_get_priority_min /* 160 */ | ||
574 | .long sys_sched_rr_get_interval | ||
575 | .long sys_nanosleep | ||
576 | .long sys_mremap | ||
577 | .long sys_setresuid16 | ||
578 | .long sys_getresuid16 /* 165 */ | ||
579 | .long sys_getpagesize | ||
580 | .long sys_ni_syscall /* old sys_query_module */ | ||
581 | .long sys_poll | ||
582 | .long sys_nfsservctl | ||
583 | .long sys_setresgid16 /* 170 */ | ||
584 | .long sys_getresgid16 | ||
585 | .long sys_prctl | ||
586 | .long sys_rt_sigreturn | ||
587 | .long sys_rt_sigaction | ||
588 | .long sys_rt_sigprocmask /* 175 */ | ||
589 | .long sys_rt_sigpending | ||
590 | .long sys_rt_sigtimedwait | ||
591 | .long sys_rt_sigqueueinfo | ||
592 | .long sys_rt_sigsuspend | ||
593 | .long sys_pread64 /* 180 */ | ||
594 | .long sys_pwrite64 | ||
595 | .long sys_lchown16; | ||
596 | .long sys_getcwd | ||
597 | .long sys_capget | ||
598 | .long sys_capset /* 185 */ | ||
599 | .long sys_sigaltstack | ||
600 | .long sys_sendfile | ||
601 | .long sys_ni_syscall /* streams1 */ | ||
602 | .long sys_ni_syscall /* streams2 */ | ||
603 | .long sys_vfork /* 190 */ | ||
604 | .long sys_getrlimit | ||
605 | .long sys_mmap2 | ||
606 | .long sys_truncate64 | ||
607 | .long sys_ftruncate64 | ||
608 | .long sys_stat64 /* 195 */ | ||
609 | .long sys_lstat64 | ||
610 | .long sys_fstat64 | ||
611 | .long sys_chown | ||
612 | .long sys_getuid | ||
613 | .long sys_getgid /* 200 */ | ||
614 | .long sys_geteuid | ||
615 | .long sys_getegid | ||
616 | .long sys_setreuid | ||
617 | .long sys_setregid | ||
618 | .long sys_getgroups /* 205 */ | ||
619 | .long sys_setgroups | ||
620 | .long sys_fchown | ||
621 | .long sys_setresuid | ||
622 | .long sys_getresuid | ||
623 | .long sys_setresgid /* 210 */ | ||
624 | .long sys_getresgid | ||
625 | .long sys_lchown | ||
626 | .long sys_setuid | ||
627 | .long sys_setgid | ||
628 | .long sys_setfsuid /* 215 */ | ||
629 | .long sys_setfsgid | ||
630 | .long sys_pivot_root | ||
631 | .long sys_ni_syscall | ||
632 | .long sys_ni_syscall | ||
633 | .long sys_getdents64 /* 220 */ | ||
634 | .long sys_gettid | ||
635 | .long sys_tkill | ||
636 | .long sys_setxattr | ||
637 | .long sys_lsetxattr | ||
638 | .long sys_fsetxattr /* 225 */ | ||
639 | .long sys_getxattr | ||
640 | .long sys_lgetxattr | ||
641 | .long sys_fgetxattr | ||
642 | .long sys_listxattr | ||
643 | .long sys_llistxattr /* 230 */ | ||
644 | .long sys_flistxattr | ||
645 | .long sys_removexattr | ||
646 | .long sys_lremovexattr | ||
647 | .long sys_fremovexattr | ||
648 | .long sys_futex /* 235 */ | ||
649 | .long sys_sendfile64 | ||
650 | .long sys_mincore | ||
651 | .long sys_madvise | ||
652 | .long sys_fcntl64 | ||
653 | .long sys_readahead /* 240 */ | ||
654 | .long sys_io_setup | ||
655 | .long sys_io_destroy | ||
656 | .long sys_io_getevents | ||
657 | .long sys_io_submit | ||
658 | .long sys_io_cancel /* 245 */ | ||
659 | .long sys_fadvise64 | ||
660 | .long sys_exit_group | ||
661 | .long sys_lookup_dcookie | ||
662 | .long sys_epoll_create | ||
663 | .long sys_epoll_ctl /* 250 */ | ||
664 | .long sys_epoll_wait | ||
665 | .long sys_remap_file_pages | ||
666 | .long sys_set_tid_address | ||
667 | .long sys_timer_create | ||
668 | .long sys_timer_settime /* 255 */ | ||
669 | .long sys_timer_gettime | ||
670 | .long sys_timer_getoverrun | ||
671 | .long sys_timer_delete | ||
672 | .long sys_clock_settime | ||
673 | .long sys_clock_gettime /* 260 */ | ||
674 | .long sys_clock_getres | ||
675 | .long sys_clock_nanosleep | ||
676 | .long sys_statfs64 | ||
677 | .long sys_fstatfs64 | ||
678 | .long sys_tgkill /* 265 */ | ||
679 | .long sys_utimes | ||
680 | .long sys_fadvise64_64 | ||
681 | .long sys_mbind | ||
682 | .long sys_get_mempolicy | ||
683 | .long sys_set_mempolicy /* 270 */ | ||
684 | .long sys_mq_open | ||
685 | .long sys_mq_unlink | ||
686 | .long sys_mq_timedsend | ||
687 | .long sys_mq_timedreceive | ||
688 | .long sys_mq_notify /* 275 */ | ||
689 | .long sys_mq_getsetattr | ||
690 | .long sys_waitid | ||
691 | .long sys_ni_syscall /* for sys_vserver */ | ||
692 | .long sys_add_key | ||
693 | .long sys_request_key /* 280 */ | ||
694 | .long sys_keyctl | ||
695 | .long sys_ioprio_set | ||
696 | .long sys_ioprio_get | ||
697 | .long sys_inotify_init | ||
698 | .long sys_inotify_add_watch /* 285 */ | ||
699 | .long sys_inotify_rm_watch | ||
700 | .long sys_migrate_pages | ||
701 | .long sys_openat | ||
702 | .long sys_mkdirat | ||
703 | .long sys_mknodat /* 290 */ | ||
704 | .long sys_fchownat | ||
705 | .long sys_futimesat | ||
706 | .long sys_fstatat64 | ||
707 | .long sys_unlinkat | ||
708 | .long sys_renameat /* 295 */ | ||
709 | .long sys_linkat | ||
710 | .long sys_symlinkat | ||
711 | .long sys_readlinkat | ||
712 | .long sys_fchmodat | ||
713 | .long sys_faccessat /* 300 */ | ||
714 | .long sys_ni_syscall /* Reserved for pselect6 */ | ||
715 | .long sys_ni_syscall /* Reserved for ppoll */ | ||
716 | .long sys_unshare | ||
717 | .long sys_set_robust_list | ||
718 | .long sys_get_robust_list /* 305 */ | ||
719 | .long sys_splice | ||
720 | .long sys_sync_file_range | ||
721 | .long sys_tee | ||
722 | .long sys_vmsplice | ||
723 | .long sys_move_pages /* 310 */ | ||
724 | .long sys_sched_setaffinity | ||
725 | .long sys_sched_getaffinity | ||
726 | .long sys_kexec_load | ||
727 | .long sys_getcpu | ||
728 | .long sys_epoll_pwait /* 315 */ | ||
729 | .long sys_utimensat | ||
730 | .long sys_signalfd | ||
731 | .long sys_timerfd_create | ||
732 | .long sys_eventfd | ||
733 | .long sys_fallocate /* 320 */ | ||
734 | .long sys_timerfd_settime | ||
735 | .long sys_timerfd_gettime | ||
736 | .long sys_signalfd4 | ||
737 | .long sys_eventfd2 | ||
738 | .long sys_epoll_create1 /* 325 */ | ||
739 | .long sys_dup3 | ||
740 | .long sys_pipe2 | ||
741 | .long sys_inotify_init1 | ||
742 | .long sys_preadv | ||
743 | .long sys_pwritev /* 330 */ | ||
744 | .long sys_rt_tgsigqueueinfo | ||
745 | .long sys_perf_event_open | ||
746 | .long sys_get_thread_area | ||
747 | .long sys_set_thread_area | ||
748 | .long sys_atomic_cmpxchg_32 /* 335 */ | ||
749 | .long sys_atomic_barrier | ||
750 | .long sys_fanotify_init | ||
751 | .long sys_fanotify_mark | ||
752 | .long sys_prlimit64 | ||
753 | |||
diff --git a/arch/m68k/kernel/entry_no.S b/arch/m68k/kernel/entry_no.S new file mode 100644 index 000000000000..2783f25e38bd --- /dev/null +++ b/arch/m68k/kernel/entry_no.S | |||
@@ -0,0 +1,134 @@ | |||
1 | /* | ||
2 | * linux/arch/m68knommu/kernel/entry.S | ||
3 | * | ||
4 | * Copyright (C) 1999-2002, Greg Ungerer (gerg@snapgear.com) | ||
5 | * Copyright (C) 1998 D. Jeff Dionne <jeff@lineo.ca>, | ||
6 | * Kenneth Albanowski <kjahds@kjahds.com>, | ||
7 | * Copyright (C) 2000 Lineo Inc. (www.lineo.com) | ||
8 | * | ||
9 | * Based on: | ||
10 | * | ||
11 | * linux/arch/m68k/kernel/entry.S | ||
12 | * | ||
13 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
14 | * | ||
15 | * This file is subject to the terms and conditions of the GNU General Public | ||
16 | * License. See the file README.legal in the main directory of this archive | ||
17 | * for more details. | ||
18 | * | ||
19 | * Linux/m68k support by Hamish Macdonald | ||
20 | * | ||
21 | * 68060 fixes by Jesper Skov | ||
22 | * ColdFire support by Greg Ungerer (gerg@snapgear.com) | ||
23 | * 5307 fixes by David W. Miller | ||
24 | * linux 2.4 support David McCullough <davidm@snapgear.com> | ||
25 | */ | ||
26 | |||
27 | #include <linux/sys.h> | ||
28 | #include <linux/linkage.h> | ||
29 | #include <asm/errno.h> | ||
30 | #include <asm/setup.h> | ||
31 | #include <asm/segment.h> | ||
32 | #include <asm/asm-offsets.h> | ||
33 | #include <asm/entry.h> | ||
34 | #include <asm/unistd.h> | ||
35 | |||
36 | .text | ||
37 | |||
38 | .globl buserr | ||
39 | .globl trap | ||
40 | .globl ret_from_exception | ||
41 | .globl ret_from_signal | ||
42 | .globl sys_fork | ||
43 | .globl sys_clone | ||
44 | .globl sys_vfork | ||
45 | |||
46 | ENTRY(buserr) | ||
47 | SAVE_ALL | ||
48 | moveq #-1,%d0 | ||
49 | movel %d0,%sp@(PT_OFF_ORIG_D0) | ||
50 | movel %sp,%sp@- /* stack frame pointer argument */ | ||
51 | jsr buserr_c | ||
52 | addql #4,%sp | ||
53 | jra ret_from_exception | ||
54 | |||
55 | ENTRY(trap) | ||
56 | SAVE_ALL | ||
57 | moveq #-1,%d0 | ||
58 | movel %d0,%sp@(PT_OFF_ORIG_D0) | ||
59 | movel %sp,%sp@- /* stack frame pointer argument */ | ||
60 | jsr trap_c | ||
61 | addql #4,%sp | ||
62 | jra ret_from_exception | ||
63 | |||
64 | #ifdef TRAP_DBG_INTERRUPT | ||
65 | |||
66 | .globl dbginterrupt | ||
67 | ENTRY(dbginterrupt) | ||
68 | SAVE_ALL | ||
69 | moveq #-1,%d0 | ||
70 | movel %d0,%sp@(PT_OFF_ORIG_D0) | ||
71 | movel %sp,%sp@- /* stack frame pointer argument */ | ||
72 | jsr dbginterrupt_c | ||
73 | addql #4,%sp | ||
74 | jra ret_from_exception | ||
75 | #endif | ||
76 | |||
77 | ENTRY(reschedule) | ||
78 | /* save top of frame */ | ||
79 | pea %sp@ | ||
80 | jbsr set_esp0 | ||
81 | addql #4,%sp | ||
82 | pea ret_from_exception | ||
83 | jmp schedule | ||
84 | |||
85 | ENTRY(ret_from_fork) | ||
86 | movel %d1,%sp@- | ||
87 | jsr schedule_tail | ||
88 | addql #4,%sp | ||
89 | jra ret_from_exception | ||
90 | |||
91 | ENTRY(sys_fork) | ||
92 | SAVE_SWITCH_STACK | ||
93 | pea %sp@(SWITCH_STACK_SIZE) | ||
94 | jbsr m68k_fork | ||
95 | addql #4,%sp | ||
96 | RESTORE_SWITCH_STACK | ||
97 | rts | ||
98 | |||
99 | ENTRY(sys_vfork) | ||
100 | SAVE_SWITCH_STACK | ||
101 | pea %sp@(SWITCH_STACK_SIZE) | ||
102 | jbsr m68k_vfork | ||
103 | addql #4,%sp | ||
104 | RESTORE_SWITCH_STACK | ||
105 | rts | ||
106 | |||
107 | ENTRY(sys_clone) | ||
108 | SAVE_SWITCH_STACK | ||
109 | pea %sp@(SWITCH_STACK_SIZE) | ||
110 | jbsr m68k_clone | ||
111 | addql #4,%sp | ||
112 | RESTORE_SWITCH_STACK | ||
113 | rts | ||
114 | |||
115 | ENTRY(sys_sigreturn) | ||
116 | SAVE_SWITCH_STACK | ||
117 | jbsr do_sigreturn | ||
118 | RESTORE_SWITCH_STACK | ||
119 | rts | ||
120 | |||
121 | ENTRY(sys_rt_sigreturn) | ||
122 | SAVE_SWITCH_STACK | ||
123 | jbsr do_rt_sigreturn | ||
124 | RESTORE_SWITCH_STACK | ||
125 | rts | ||
126 | |||
127 | ENTRY(ret_from_user_signal) | ||
128 | moveq #__NR_sigreturn,%d0 | ||
129 | trap #0 | ||
130 | |||
131 | ENTRY(ret_from_user_rt_signal) | ||
132 | movel #__NR_rt_sigreturn,%d0 | ||
133 | trap #0 | ||
134 | |||
diff --git a/arch/m68k/kernel/init_task.c b/arch/m68k/kernel/init_task.c new file mode 100644 index 000000000000..cbf9dc3cc51d --- /dev/null +++ b/arch/m68k/kernel/init_task.c | |||
@@ -0,0 +1,36 @@ | |||
1 | /* | ||
2 | * linux/arch/m68knommu/kernel/init_task.c | ||
3 | */ | ||
4 | #include <linux/mm.h> | ||
5 | #include <linux/module.h> | ||
6 | #include <linux/sched.h> | ||
7 | #include <linux/init.h> | ||
8 | #include <linux/init_task.h> | ||
9 | #include <linux/fs.h> | ||
10 | #include <linux/mqueue.h> | ||
11 | |||
12 | #include <asm/uaccess.h> | ||
13 | #include <asm/pgtable.h> | ||
14 | |||
15 | static struct signal_struct init_signals = INIT_SIGNALS(init_signals); | ||
16 | static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); | ||
17 | /* | ||
18 | * Initial task structure. | ||
19 | * | ||
20 | * All other task structs will be allocated on slabs in fork.c | ||
21 | */ | ||
22 | __asm__(".align 4"); | ||
23 | struct task_struct init_task = INIT_TASK(init_task); | ||
24 | |||
25 | EXPORT_SYMBOL(init_task); | ||
26 | |||
27 | /* | ||
28 | * Initial thread structure. | ||
29 | * | ||
30 | * We need to make sure that this is 8192-byte aligned due to the | ||
31 | * way process stacks are handled. This is done by having a special | ||
32 | * "init_task" linker map entry.. | ||
33 | */ | ||
34 | union thread_union init_thread_union __init_task_data = | ||
35 | { INIT_THREAD_INFO(init_task) }; | ||
36 | |||
diff --git a/arch/m68k/kernel/irq.c b/arch/m68k/kernel/irq.c new file mode 100644 index 000000000000..c7dd48f37bee --- /dev/null +++ b/arch/m68k/kernel/irq.c | |||
@@ -0,0 +1,58 @@ | |||
1 | /* | ||
2 | * irq.c | ||
3 | * | ||
4 | * (C) Copyright 2007, Greg Ungerer <gerg@snapgear.com> | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file COPYING in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | |||
11 | #include <linux/types.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/kernel_stat.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/irq.h> | ||
17 | #include <linux/seq_file.h> | ||
18 | #include <asm/system.h> | ||
19 | #include <asm/traps.h> | ||
20 | |||
21 | asmlinkage void do_IRQ(int irq, struct pt_regs *regs) | ||
22 | { | ||
23 | struct pt_regs *oldregs = set_irq_regs(regs); | ||
24 | |||
25 | irq_enter(); | ||
26 | generic_handle_irq(irq); | ||
27 | irq_exit(); | ||
28 | |||
29 | set_irq_regs(oldregs); | ||
30 | } | ||
31 | |||
32 | int show_interrupts(struct seq_file *p, void *v) | ||
33 | { | ||
34 | struct irqaction *ap; | ||
35 | int irq = *((loff_t *) v); | ||
36 | |||
37 | if (irq == 0) | ||
38 | seq_puts(p, " CPU0\n"); | ||
39 | |||
40 | if (irq < NR_IRQS) { | ||
41 | struct irq_desc *desc = irq_to_desc(irq); | ||
42 | |||
43 | ap = desc->action; | ||
44 | if (ap) { | ||
45 | seq_printf(p, "%3d: ", irq); | ||
46 | seq_printf(p, "%10u ", kstat_irqs(irq)); | ||
47 | seq_printf(p, "%14s ", get_irq_desc_chip(desc)->name); | ||
48 | |||
49 | seq_printf(p, "%s", ap->name); | ||
50 | for (ap = ap->next; ap; ap = ap->next) | ||
51 | seq_printf(p, ", %s", ap->name); | ||
52 | seq_putc(p, '\n'); | ||
53 | } | ||
54 | } | ||
55 | |||
56 | return 0; | ||
57 | } | ||
58 | |||
diff --git a/arch/m68k/kernel/m68k_ksyms.c b/arch/m68k/kernel/m68k_ksyms.c index d900e77e5363..4752c28ce0ac 100644 --- a/arch/m68k/kernel/m68k_ksyms.c +++ b/arch/m68k/kernel/m68k_ksyms.c | |||
@@ -1,16 +1,5 @@ | |||
1 | #include <linux/module.h> | 1 | #ifdef CONFIG_MMU |
2 | 2 | #include "m68k_ksyms_mm.c" | |
3 | asmlinkage long long __ashldi3 (long long, int); | 3 | #else |
4 | asmlinkage long long __ashrdi3 (long long, int); | 4 | #include "m68k_ksyms_no.c" |
5 | asmlinkage long long __lshrdi3 (long long, int); | 5 | #endif |
6 | asmlinkage long long __muldi3 (long long, long long); | ||
7 | |||
8 | /* The following are special because they're not called | ||
9 | explicitly (the C compiler generates them). Fortunately, | ||
10 | their interface isn't gonna change any time soon now, so | ||
11 | it's OK to leave it out of version control. */ | ||
12 | EXPORT_SYMBOL(__ashldi3); | ||
13 | EXPORT_SYMBOL(__ashrdi3); | ||
14 | EXPORT_SYMBOL(__lshrdi3); | ||
15 | EXPORT_SYMBOL(__muldi3); | ||
16 | |||
diff --git a/arch/m68k/kernel/m68k_ksyms_mm.c b/arch/m68k/kernel/m68k_ksyms_mm.c new file mode 100644 index 000000000000..d900e77e5363 --- /dev/null +++ b/arch/m68k/kernel/m68k_ksyms_mm.c | |||
@@ -0,0 +1,16 @@ | |||
1 | #include <linux/module.h> | ||
2 | |||
3 | asmlinkage long long __ashldi3 (long long, int); | ||
4 | asmlinkage long long __ashrdi3 (long long, int); | ||
5 | asmlinkage long long __lshrdi3 (long long, int); | ||
6 | asmlinkage long long __muldi3 (long long, long long); | ||
7 | |||
8 | /* The following are special because they're not called | ||
9 | explicitly (the C compiler generates them). Fortunately, | ||
10 | their interface isn't gonna change any time soon now, so | ||
11 | it's OK to leave it out of version control. */ | ||
12 | EXPORT_SYMBOL(__ashldi3); | ||
13 | EXPORT_SYMBOL(__ashrdi3); | ||
14 | EXPORT_SYMBOL(__lshrdi3); | ||
15 | EXPORT_SYMBOL(__muldi3); | ||
16 | |||
diff --git a/arch/m68k/kernel/m68k_ksyms_no.c b/arch/m68k/kernel/m68k_ksyms_no.c new file mode 100644 index 000000000000..39fe0a7aec32 --- /dev/null +++ b/arch/m68k/kernel/m68k_ksyms_no.c | |||
@@ -0,0 +1,78 @@ | |||
1 | #include <linux/module.h> | ||
2 | #include <linux/linkage.h> | ||
3 | #include <linux/sched.h> | ||
4 | #include <linux/string.h> | ||
5 | #include <linux/mm.h> | ||
6 | #include <linux/user.h> | ||
7 | #include <linux/elfcore.h> | ||
8 | #include <linux/in6.h> | ||
9 | #include <linux/interrupt.h> | ||
10 | |||
11 | #include <asm/setup.h> | ||
12 | #include <asm/machdep.h> | ||
13 | #include <asm/pgalloc.h> | ||
14 | #include <asm/irq.h> | ||
15 | #include <asm/io.h> | ||
16 | #include <asm/checksum.h> | ||
17 | #include <asm/current.h> | ||
18 | |||
19 | extern int dump_fpu(struct pt_regs *, elf_fpregset_t *); | ||
20 | |||
21 | /* platform dependent support */ | ||
22 | |||
23 | EXPORT_SYMBOL(__ioremap); | ||
24 | EXPORT_SYMBOL(iounmap); | ||
25 | EXPORT_SYMBOL(dump_fpu); | ||
26 | |||
27 | EXPORT_SYMBOL(ip_fast_csum); | ||
28 | |||
29 | EXPORT_SYMBOL(kernel_thread); | ||
30 | |||
31 | /* Networking helper routines. */ | ||
32 | EXPORT_SYMBOL(csum_partial_copy_nocheck); | ||
33 | |||
34 | /* The following are special because they're not called | ||
35 | explicitly (the C compiler generates them). Fortunately, | ||
36 | their interface isn't gonna change any time soon now, so | ||
37 | it's OK to leave it out of version control. */ | ||
38 | EXPORT_SYMBOL(memcpy); | ||
39 | EXPORT_SYMBOL(memset); | ||
40 | |||
41 | /* | ||
42 | * libgcc functions - functions that are used internally by the | ||
43 | * compiler... (prototypes are not correct though, but that | ||
44 | * doesn't really matter since they're not versioned). | ||
45 | */ | ||
46 | extern void __ashldi3(void); | ||
47 | extern void __ashrdi3(void); | ||
48 | extern void __divsi3(void); | ||
49 | extern void __lshrdi3(void); | ||
50 | extern void __modsi3(void); | ||
51 | extern void __muldi3(void); | ||
52 | extern void __mulsi3(void); | ||
53 | extern void __udivsi3(void); | ||
54 | extern void __umodsi3(void); | ||
55 | |||
56 | /* gcc lib functions */ | ||
57 | EXPORT_SYMBOL(__ashldi3); | ||
58 | EXPORT_SYMBOL(__ashrdi3); | ||
59 | EXPORT_SYMBOL(__divsi3); | ||
60 | EXPORT_SYMBOL(__lshrdi3); | ||
61 | EXPORT_SYMBOL(__modsi3); | ||
62 | EXPORT_SYMBOL(__muldi3); | ||
63 | EXPORT_SYMBOL(__mulsi3); | ||
64 | EXPORT_SYMBOL(__udivsi3); | ||
65 | EXPORT_SYMBOL(__umodsi3); | ||
66 | |||
67 | #ifdef CONFIG_COLDFIRE | ||
68 | extern unsigned int *dma_device_address; | ||
69 | extern unsigned long dma_base_addr, _ramend; | ||
70 | EXPORT_SYMBOL(dma_base_addr); | ||
71 | EXPORT_SYMBOL(dma_device_address); | ||
72 | EXPORT_SYMBOL(_ramend); | ||
73 | |||
74 | extern asmlinkage void trap(void); | ||
75 | extern void *_ramvec; | ||
76 | EXPORT_SYMBOL(trap); | ||
77 | EXPORT_SYMBOL(_ramvec); | ||
78 | #endif /* CONFIG_COLDFIRE */ | ||
diff --git a/arch/m68k/kernel/module.c b/arch/m68k/kernel/module.c index cd6bcb1c957e..7ea203ce6b1a 100644 --- a/arch/m68k/kernel/module.c +++ b/arch/m68k/kernel/module.c | |||
@@ -1,155 +1,5 @@ | |||
1 | /* | 1 | #ifdef CONFIG_MMU |
2 | * This file is subject to the terms and conditions of the GNU General Public | 2 | #include "module_mm.c" |
3 | * License. See the file COPYING in the main directory of this archive | ||
4 | * for more details. | ||
5 | */ | ||
6 | |||
7 | #include <linux/moduleloader.h> | ||
8 | #include <linux/elf.h> | ||
9 | #include <linux/vmalloc.h> | ||
10 | #include <linux/fs.h> | ||
11 | #include <linux/string.h> | ||
12 | #include <linux/kernel.h> | ||
13 | |||
14 | #if 0 | ||
15 | #define DEBUGP printk | ||
16 | #else | 3 | #else |
17 | #define DEBUGP(fmt...) | 4 | #include "module_no.c" |
18 | #endif | 5 | #endif |
19 | |||
20 | #ifdef CONFIG_MODULES | ||
21 | |||
22 | void *module_alloc(unsigned long size) | ||
23 | { | ||
24 | if (size == 0) | ||
25 | return NULL; | ||
26 | return vmalloc(size); | ||
27 | } | ||
28 | |||
29 | |||
30 | /* Free memory returned from module_alloc */ | ||
31 | void module_free(struct module *mod, void *module_region) | ||
32 | { | ||
33 | vfree(module_region); | ||
34 | } | ||
35 | |||
36 | /* We don't need anything special. */ | ||
37 | int module_frob_arch_sections(Elf_Ehdr *hdr, | ||
38 | Elf_Shdr *sechdrs, | ||
39 | char *secstrings, | ||
40 | struct module *mod) | ||
41 | { | ||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | int apply_relocate(Elf32_Shdr *sechdrs, | ||
46 | const char *strtab, | ||
47 | unsigned int symindex, | ||
48 | unsigned int relsec, | ||
49 | struct module *me) | ||
50 | { | ||
51 | unsigned int i; | ||
52 | Elf32_Rel *rel = (void *)sechdrs[relsec].sh_addr; | ||
53 | Elf32_Sym *sym; | ||
54 | uint32_t *location; | ||
55 | |||
56 | DEBUGP("Applying relocate section %u to %u\n", relsec, | ||
57 | sechdrs[relsec].sh_info); | ||
58 | for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { | ||
59 | /* This is where to make the change */ | ||
60 | location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr | ||
61 | + rel[i].r_offset; | ||
62 | /* This is the symbol it is referring to. Note that all | ||
63 | undefined symbols have been resolved. */ | ||
64 | sym = (Elf32_Sym *)sechdrs[symindex].sh_addr | ||
65 | + ELF32_R_SYM(rel[i].r_info); | ||
66 | |||
67 | switch (ELF32_R_TYPE(rel[i].r_info)) { | ||
68 | case R_68K_32: | ||
69 | /* We add the value into the location given */ | ||
70 | *location += sym->st_value; | ||
71 | break; | ||
72 | case R_68K_PC32: | ||
73 | /* Add the value, subtract its postition */ | ||
74 | *location += sym->st_value - (uint32_t)location; | ||
75 | break; | ||
76 | default: | ||
77 | printk(KERN_ERR "module %s: Unknown relocation: %u\n", | ||
78 | me->name, ELF32_R_TYPE(rel[i].r_info)); | ||
79 | return -ENOEXEC; | ||
80 | } | ||
81 | } | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | int apply_relocate_add(Elf32_Shdr *sechdrs, | ||
86 | const char *strtab, | ||
87 | unsigned int symindex, | ||
88 | unsigned int relsec, | ||
89 | struct module *me) | ||
90 | { | ||
91 | unsigned int i; | ||
92 | Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr; | ||
93 | Elf32_Sym *sym; | ||
94 | uint32_t *location; | ||
95 | |||
96 | DEBUGP("Applying relocate_add section %u to %u\n", relsec, | ||
97 | sechdrs[relsec].sh_info); | ||
98 | for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { | ||
99 | /* This is where to make the change */ | ||
100 | location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr | ||
101 | + rel[i].r_offset; | ||
102 | /* This is the symbol it is referring to. Note that all | ||
103 | undefined symbols have been resolved. */ | ||
104 | sym = (Elf32_Sym *)sechdrs[symindex].sh_addr | ||
105 | + ELF32_R_SYM(rel[i].r_info); | ||
106 | |||
107 | switch (ELF32_R_TYPE(rel[i].r_info)) { | ||
108 | case R_68K_32: | ||
109 | /* We add the value into the location given */ | ||
110 | *location = rel[i].r_addend + sym->st_value; | ||
111 | break; | ||
112 | case R_68K_PC32: | ||
113 | /* Add the value, subtract its postition */ | ||
114 | *location = rel[i].r_addend + sym->st_value - (uint32_t)location; | ||
115 | break; | ||
116 | default: | ||
117 | printk(KERN_ERR "module %s: Unknown relocation: %u\n", | ||
118 | me->name, ELF32_R_TYPE(rel[i].r_info)); | ||
119 | return -ENOEXEC; | ||
120 | } | ||
121 | } | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | int module_finalize(const Elf_Ehdr *hdr, | ||
126 | const Elf_Shdr *sechdrs, | ||
127 | struct module *mod) | ||
128 | { | ||
129 | module_fixup(mod, mod->arch.fixup_start, mod->arch.fixup_end); | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | void module_arch_cleanup(struct module *mod) | ||
135 | { | ||
136 | } | ||
137 | |||
138 | #endif /* CONFIG_MODULES */ | ||
139 | |||
140 | void module_fixup(struct module *mod, struct m68k_fixup_info *start, | ||
141 | struct m68k_fixup_info *end) | ||
142 | { | ||
143 | struct m68k_fixup_info *fixup; | ||
144 | |||
145 | for (fixup = start; fixup < end; fixup++) { | ||
146 | switch (fixup->type) { | ||
147 | case m68k_fixup_memoffset: | ||
148 | *(u32 *)fixup->addr = m68k_memoffset; | ||
149 | break; | ||
150 | case m68k_fixup_vnode_shift: | ||
151 | *(u16 *)fixup->addr += m68k_virt_to_node_shift; | ||
152 | break; | ||
153 | } | ||
154 | } | ||
155 | } | ||
diff --git a/arch/m68k/kernel/module_mm.c b/arch/m68k/kernel/module_mm.c new file mode 100644 index 000000000000..cd6bcb1c957e --- /dev/null +++ b/arch/m68k/kernel/module_mm.c | |||
@@ -0,0 +1,155 @@ | |||
1 | /* | ||
2 | * This file is subject to the terms and conditions of the GNU General Public | ||
3 | * License. See the file COPYING in the main directory of this archive | ||
4 | * for more details. | ||
5 | */ | ||
6 | |||
7 | #include <linux/moduleloader.h> | ||
8 | #include <linux/elf.h> | ||
9 | #include <linux/vmalloc.h> | ||
10 | #include <linux/fs.h> | ||
11 | #include <linux/string.h> | ||
12 | #include <linux/kernel.h> | ||
13 | |||
14 | #if 0 | ||
15 | #define DEBUGP printk | ||
16 | #else | ||
17 | #define DEBUGP(fmt...) | ||
18 | #endif | ||
19 | |||
20 | #ifdef CONFIG_MODULES | ||
21 | |||
22 | void *module_alloc(unsigned long size) | ||
23 | { | ||
24 | if (size == 0) | ||
25 | return NULL; | ||
26 | return vmalloc(size); | ||
27 | } | ||
28 | |||
29 | |||
30 | /* Free memory returned from module_alloc */ | ||
31 | void module_free(struct module *mod, void *module_region) | ||
32 | { | ||
33 | vfree(module_region); | ||
34 | } | ||
35 | |||
36 | /* We don't need anything special. */ | ||
37 | int module_frob_arch_sections(Elf_Ehdr *hdr, | ||
38 | Elf_Shdr *sechdrs, | ||
39 | char *secstrings, | ||
40 | struct module *mod) | ||
41 | { | ||
42 | return 0; | ||
43 | } | ||
44 | |||
45 | int apply_relocate(Elf32_Shdr *sechdrs, | ||
46 | const char *strtab, | ||
47 | unsigned int symindex, | ||
48 | unsigned int relsec, | ||
49 | struct module *me) | ||
50 | { | ||
51 | unsigned int i; | ||
52 | Elf32_Rel *rel = (void *)sechdrs[relsec].sh_addr; | ||
53 | Elf32_Sym *sym; | ||
54 | uint32_t *location; | ||
55 | |||
56 | DEBUGP("Applying relocate section %u to %u\n", relsec, | ||
57 | sechdrs[relsec].sh_info); | ||
58 | for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { | ||
59 | /* This is where to make the change */ | ||
60 | location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr | ||
61 | + rel[i].r_offset; | ||
62 | /* This is the symbol it is referring to. Note that all | ||
63 | undefined symbols have been resolved. */ | ||
64 | sym = (Elf32_Sym *)sechdrs[symindex].sh_addr | ||
65 | + ELF32_R_SYM(rel[i].r_info); | ||
66 | |||
67 | switch (ELF32_R_TYPE(rel[i].r_info)) { | ||
68 | case R_68K_32: | ||
69 | /* We add the value into the location given */ | ||
70 | *location += sym->st_value; | ||
71 | break; | ||
72 | case R_68K_PC32: | ||
73 | /* Add the value, subtract its postition */ | ||
74 | *location += sym->st_value - (uint32_t)location; | ||
75 | break; | ||
76 | default: | ||
77 | printk(KERN_ERR "module %s: Unknown relocation: %u\n", | ||
78 | me->name, ELF32_R_TYPE(rel[i].r_info)); | ||
79 | return -ENOEXEC; | ||
80 | } | ||
81 | } | ||
82 | return 0; | ||
83 | } | ||
84 | |||
85 | int apply_relocate_add(Elf32_Shdr *sechdrs, | ||
86 | const char *strtab, | ||
87 | unsigned int symindex, | ||
88 | unsigned int relsec, | ||
89 | struct module *me) | ||
90 | { | ||
91 | unsigned int i; | ||
92 | Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr; | ||
93 | Elf32_Sym *sym; | ||
94 | uint32_t *location; | ||
95 | |||
96 | DEBUGP("Applying relocate_add section %u to %u\n", relsec, | ||
97 | sechdrs[relsec].sh_info); | ||
98 | for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { | ||
99 | /* This is where to make the change */ | ||
100 | location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr | ||
101 | + rel[i].r_offset; | ||
102 | /* This is the symbol it is referring to. Note that all | ||
103 | undefined symbols have been resolved. */ | ||
104 | sym = (Elf32_Sym *)sechdrs[symindex].sh_addr | ||
105 | + ELF32_R_SYM(rel[i].r_info); | ||
106 | |||
107 | switch (ELF32_R_TYPE(rel[i].r_info)) { | ||
108 | case R_68K_32: | ||
109 | /* We add the value into the location given */ | ||
110 | *location = rel[i].r_addend + sym->st_value; | ||
111 | break; | ||
112 | case R_68K_PC32: | ||
113 | /* Add the value, subtract its postition */ | ||
114 | *location = rel[i].r_addend + sym->st_value - (uint32_t)location; | ||
115 | break; | ||
116 | default: | ||
117 | printk(KERN_ERR "module %s: Unknown relocation: %u\n", | ||
118 | me->name, ELF32_R_TYPE(rel[i].r_info)); | ||
119 | return -ENOEXEC; | ||
120 | } | ||
121 | } | ||
122 | return 0; | ||
123 | } | ||
124 | |||
125 | int module_finalize(const Elf_Ehdr *hdr, | ||
126 | const Elf_Shdr *sechdrs, | ||
127 | struct module *mod) | ||
128 | { | ||
129 | module_fixup(mod, mod->arch.fixup_start, mod->arch.fixup_end); | ||
130 | |||
131 | return 0; | ||
132 | } | ||
133 | |||
134 | void module_arch_cleanup(struct module *mod) | ||
135 | { | ||
136 | } | ||
137 | |||
138 | #endif /* CONFIG_MODULES */ | ||
139 | |||
140 | void module_fixup(struct module *mod, struct m68k_fixup_info *start, | ||
141 | struct m68k_fixup_info *end) | ||
142 | { | ||
143 | struct m68k_fixup_info *fixup; | ||
144 | |||
145 | for (fixup = start; fixup < end; fixup++) { | ||
146 | switch (fixup->type) { | ||
147 | case m68k_fixup_memoffset: | ||
148 | *(u32 *)fixup->addr = m68k_memoffset; | ||
149 | break; | ||
150 | case m68k_fixup_vnode_shift: | ||
151 | *(u16 *)fixup->addr += m68k_virt_to_node_shift; | ||
152 | break; | ||
153 | } | ||
154 | } | ||
155 | } | ||
diff --git a/arch/m68k/kernel/module_no.c b/arch/m68k/kernel/module_no.c new file mode 100644 index 000000000000..d11ffae7956a --- /dev/null +++ b/arch/m68k/kernel/module_no.c | |||
@@ -0,0 +1,126 @@ | |||
1 | #include <linux/moduleloader.h> | ||
2 | #include <linux/elf.h> | ||
3 | #include <linux/vmalloc.h> | ||
4 | #include <linux/fs.h> | ||
5 | #include <linux/string.h> | ||
6 | #include <linux/kernel.h> | ||
7 | |||
8 | #if 0 | ||
9 | #define DEBUGP printk | ||
10 | #else | ||
11 | #define DEBUGP(fmt...) | ||
12 | #endif | ||
13 | |||
14 | void *module_alloc(unsigned long size) | ||
15 | { | ||
16 | if (size == 0) | ||
17 | return NULL; | ||
18 | return vmalloc(size); | ||
19 | } | ||
20 | |||
21 | |||
22 | /* Free memory returned from module_alloc */ | ||
23 | void module_free(struct module *mod, void *module_region) | ||
24 | { | ||
25 | vfree(module_region); | ||
26 | } | ||
27 | |||
28 | /* We don't need anything special. */ | ||
29 | int module_frob_arch_sections(Elf_Ehdr *hdr, | ||
30 | Elf_Shdr *sechdrs, | ||
31 | char *secstrings, | ||
32 | struct module *mod) | ||
33 | { | ||
34 | return 0; | ||
35 | } | ||
36 | |||
37 | int apply_relocate(Elf32_Shdr *sechdrs, | ||
38 | const char *strtab, | ||
39 | unsigned int symindex, | ||
40 | unsigned int relsec, | ||
41 | struct module *me) | ||
42 | { | ||
43 | unsigned int i; | ||
44 | Elf32_Rel *rel = (void *)sechdrs[relsec].sh_addr; | ||
45 | Elf32_Sym *sym; | ||
46 | uint32_t *location; | ||
47 | |||
48 | DEBUGP("Applying relocate section %u to %u\n", relsec, | ||
49 | sechdrs[relsec].sh_info); | ||
50 | for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { | ||
51 | /* This is where to make the change */ | ||
52 | location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr | ||
53 | + rel[i].r_offset; | ||
54 | /* This is the symbol it is referring to. Note that all | ||
55 | undefined symbols have been resolved. */ | ||
56 | sym = (Elf32_Sym *)sechdrs[symindex].sh_addr | ||
57 | + ELF32_R_SYM(rel[i].r_info); | ||
58 | |||
59 | switch (ELF32_R_TYPE(rel[i].r_info)) { | ||
60 | case R_68K_32: | ||
61 | /* We add the value into the location given */ | ||
62 | *location += sym->st_value; | ||
63 | break; | ||
64 | case R_68K_PC32: | ||
65 | /* Add the value, subtract its postition */ | ||
66 | *location += sym->st_value - (uint32_t)location; | ||
67 | break; | ||
68 | default: | ||
69 | printk(KERN_ERR "module %s: Unknown relocation: %u\n", | ||
70 | me->name, ELF32_R_TYPE(rel[i].r_info)); | ||
71 | return -ENOEXEC; | ||
72 | } | ||
73 | } | ||
74 | return 0; | ||
75 | } | ||
76 | |||
77 | int apply_relocate_add(Elf32_Shdr *sechdrs, | ||
78 | const char *strtab, | ||
79 | unsigned int symindex, | ||
80 | unsigned int relsec, | ||
81 | struct module *me) | ||
82 | { | ||
83 | unsigned int i; | ||
84 | Elf32_Rela *rel = (void *)sechdrs[relsec].sh_addr; | ||
85 | Elf32_Sym *sym; | ||
86 | uint32_t *location; | ||
87 | |||
88 | DEBUGP("Applying relocate_add section %u to %u\n", relsec, | ||
89 | sechdrs[relsec].sh_info); | ||
90 | for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rel); i++) { | ||
91 | /* This is where to make the change */ | ||
92 | location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr | ||
93 | + rel[i].r_offset; | ||
94 | /* This is the symbol it is referring to. Note that all | ||
95 | undefined symbols have been resolved. */ | ||
96 | sym = (Elf32_Sym *)sechdrs[symindex].sh_addr | ||
97 | + ELF32_R_SYM(rel[i].r_info); | ||
98 | |||
99 | switch (ELF32_R_TYPE(rel[i].r_info)) { | ||
100 | case R_68K_32: | ||
101 | /* We add the value into the location given */ | ||
102 | *location = rel[i].r_addend + sym->st_value; | ||
103 | break; | ||
104 | case R_68K_PC32: | ||
105 | /* Add the value, subtract its postition */ | ||
106 | *location = rel[i].r_addend + sym->st_value - (uint32_t)location; | ||
107 | break; | ||
108 | default: | ||
109 | printk(KERN_ERR "module %s: Unknown relocation: %u\n", | ||
110 | me->name, ELF32_R_TYPE(rel[i].r_info)); | ||
111 | return -ENOEXEC; | ||
112 | } | ||
113 | } | ||
114 | return 0; | ||
115 | } | ||
116 | |||
117 | int module_finalize(const Elf_Ehdr *hdr, | ||
118 | const Elf_Shdr *sechdrs, | ||
119 | struct module *me) | ||
120 | { | ||
121 | return 0; | ||
122 | } | ||
123 | |||
124 | void module_arch_cleanup(struct module *mod) | ||
125 | { | ||
126 | } | ||
diff --git a/arch/m68k/kernel/process.c b/arch/m68k/kernel/process.c index c2a1fc23dd75..6cf4bd6e34f8 100644 --- a/arch/m68k/kernel/process.c +++ b/arch/m68k/kernel/process.c | |||
@@ -1,354 +1,5 @@ | |||
1 | /* | 1 | #ifdef CONFIG_MMU |
2 | * linux/arch/m68k/kernel/process.c | 2 | #include "process_mm.c" |
3 | * | ||
4 | * Copyright (C) 1995 Hamish Macdonald | ||
5 | * | ||
6 | * 68060 fixes by Jesper Skov | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * This file handles the architecture-dependent parts of process handling.. | ||
11 | */ | ||
12 | |||
13 | #include <linux/errno.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/mm.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/fs.h> | ||
20 | #include <linux/smp.h> | ||
21 | #include <linux/stddef.h> | ||
22 | #include <linux/unistd.h> | ||
23 | #include <linux/ptrace.h> | ||
24 | #include <linux/user.h> | ||
25 | #include <linux/reboot.h> | ||
26 | #include <linux/init_task.h> | ||
27 | #include <linux/mqueue.h> | ||
28 | |||
29 | #include <asm/uaccess.h> | ||
30 | #include <asm/system.h> | ||
31 | #include <asm/traps.h> | ||
32 | #include <asm/machdep.h> | ||
33 | #include <asm/setup.h> | ||
34 | #include <asm/pgtable.h> | ||
35 | |||
36 | /* | ||
37 | * Initial task/thread structure. Make this a per-architecture thing, | ||
38 | * because different architectures tend to have different | ||
39 | * alignment requirements and potentially different initial | ||
40 | * setup. | ||
41 | */ | ||
42 | static struct signal_struct init_signals = INIT_SIGNALS(init_signals); | ||
43 | static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); | ||
44 | union thread_union init_thread_union __init_task_data | ||
45 | __attribute__((aligned(THREAD_SIZE))) = | ||
46 | { INIT_THREAD_INFO(init_task) }; | ||
47 | |||
48 | /* initial task structure */ | ||
49 | struct task_struct init_task = INIT_TASK(init_task); | ||
50 | |||
51 | EXPORT_SYMBOL(init_task); | ||
52 | |||
53 | asmlinkage void ret_from_fork(void); | ||
54 | |||
55 | |||
56 | /* | ||
57 | * Return saved PC from a blocked thread | ||
58 | */ | ||
59 | unsigned long thread_saved_pc(struct task_struct *tsk) | ||
60 | { | ||
61 | struct switch_stack *sw = (struct switch_stack *)tsk->thread.ksp; | ||
62 | /* Check whether the thread is blocked in resume() */ | ||
63 | if (in_sched_functions(sw->retpc)) | ||
64 | return ((unsigned long *)sw->a6)[1]; | ||
65 | else | ||
66 | return sw->retpc; | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * The idle loop on an m68k.. | ||
71 | */ | ||
72 | static void default_idle(void) | ||
73 | { | ||
74 | if (!need_resched()) | ||
75 | #if defined(MACH_ATARI_ONLY) | ||
76 | /* block out HSYNC on the atari (falcon) */ | ||
77 | __asm__("stop #0x2200" : : : "cc"); | ||
78 | #else | 3 | #else |
79 | __asm__("stop #0x2000" : : : "cc"); | 4 | #include "process_no.c" |
80 | #endif | 5 | #endif |
81 | } | ||
82 | |||
83 | void (*idle)(void) = default_idle; | ||
84 | |||
85 | /* | ||
86 | * The idle thread. There's no useful work to be | ||
87 | * done, so just try to conserve power and have a | ||
88 | * low exit latency (ie sit in a loop waiting for | ||
89 | * somebody to say that they'd like to reschedule) | ||
90 | */ | ||
91 | void cpu_idle(void) | ||
92 | { | ||
93 | /* endless idle loop with no priority at all */ | ||
94 | while (1) { | ||
95 | while (!need_resched()) | ||
96 | idle(); | ||
97 | preempt_enable_no_resched(); | ||
98 | schedule(); | ||
99 | preempt_disable(); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | void machine_restart(char * __unused) | ||
104 | { | ||
105 | if (mach_reset) | ||
106 | mach_reset(); | ||
107 | for (;;); | ||
108 | } | ||
109 | |||
110 | void machine_halt(void) | ||
111 | { | ||
112 | if (mach_halt) | ||
113 | mach_halt(); | ||
114 | for (;;); | ||
115 | } | ||
116 | |||
117 | void machine_power_off(void) | ||
118 | { | ||
119 | if (mach_power_off) | ||
120 | mach_power_off(); | ||
121 | for (;;); | ||
122 | } | ||
123 | |||
124 | void (*pm_power_off)(void) = machine_power_off; | ||
125 | EXPORT_SYMBOL(pm_power_off); | ||
126 | |||
127 | void show_regs(struct pt_regs * regs) | ||
128 | { | ||
129 | printk("\n"); | ||
130 | printk("Format %02x Vector: %04x PC: %08lx Status: %04x %s\n", | ||
131 | regs->format, regs->vector, regs->pc, regs->sr, print_tainted()); | ||
132 | printk("ORIG_D0: %08lx D0: %08lx A2: %08lx A1: %08lx\n", | ||
133 | regs->orig_d0, regs->d0, regs->a2, regs->a1); | ||
134 | printk("A0: %08lx D5: %08lx D4: %08lx\n", | ||
135 | regs->a0, regs->d5, regs->d4); | ||
136 | printk("D3: %08lx D2: %08lx D1: %08lx\n", | ||
137 | regs->d3, regs->d2, regs->d1); | ||
138 | if (!(regs->sr & PS_S)) | ||
139 | printk("USP: %08lx\n", rdusp()); | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * Create a kernel thread | ||
144 | */ | ||
145 | int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) | ||
146 | { | ||
147 | int pid; | ||
148 | mm_segment_t fs; | ||
149 | |||
150 | fs = get_fs(); | ||
151 | set_fs (KERNEL_DS); | ||
152 | |||
153 | { | ||
154 | register long retval __asm__ ("d0"); | ||
155 | register long clone_arg __asm__ ("d1") = flags | CLONE_VM | CLONE_UNTRACED; | ||
156 | |||
157 | retval = __NR_clone; | ||
158 | __asm__ __volatile__ | ||
159 | ("clrl %%d2\n\t" | ||
160 | "trap #0\n\t" /* Linux/m68k system call */ | ||
161 | "tstl %0\n\t" /* child or parent */ | ||
162 | "jne 1f\n\t" /* parent - jump */ | ||
163 | "lea %%sp@(%c7),%6\n\t" /* reload current */ | ||
164 | "movel %6@,%6\n\t" | ||
165 | "movel %3,%%sp@-\n\t" /* push argument */ | ||
166 | "jsr %4@\n\t" /* call fn */ | ||
167 | "movel %0,%%d1\n\t" /* pass exit value */ | ||
168 | "movel %2,%%d0\n\t" /* exit */ | ||
169 | "trap #0\n" | ||
170 | "1:" | ||
171 | : "+d" (retval) | ||
172 | : "i" (__NR_clone), "i" (__NR_exit), | ||
173 | "r" (arg), "a" (fn), "d" (clone_arg), "r" (current), | ||
174 | "i" (-THREAD_SIZE) | ||
175 | : "d2"); | ||
176 | |||
177 | pid = retval; | ||
178 | } | ||
179 | |||
180 | set_fs (fs); | ||
181 | return pid; | ||
182 | } | ||
183 | EXPORT_SYMBOL(kernel_thread); | ||
184 | |||
185 | void flush_thread(void) | ||
186 | { | ||
187 | unsigned long zero = 0; | ||
188 | set_fs(USER_DS); | ||
189 | current->thread.fs = __USER_DS; | ||
190 | if (!FPU_IS_EMU) | ||
191 | asm volatile (".chip 68k/68881\n\t" | ||
192 | "frestore %0@\n\t" | ||
193 | ".chip 68k" : : "a" (&zero)); | ||
194 | } | ||
195 | |||
196 | /* | ||
197 | * "m68k_fork()".. By the time we get here, the | ||
198 | * non-volatile registers have also been saved on the | ||
199 | * stack. We do some ugly pointer stuff here.. (see | ||
200 | * also copy_thread) | ||
201 | */ | ||
202 | |||
203 | asmlinkage int m68k_fork(struct pt_regs *regs) | ||
204 | { | ||
205 | return do_fork(SIGCHLD, rdusp(), regs, 0, NULL, NULL); | ||
206 | } | ||
207 | |||
208 | asmlinkage int m68k_vfork(struct pt_regs *regs) | ||
209 | { | ||
210 | return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, | ||
211 | NULL, NULL); | ||
212 | } | ||
213 | |||
214 | asmlinkage int m68k_clone(struct pt_regs *regs) | ||
215 | { | ||
216 | unsigned long clone_flags; | ||
217 | unsigned long newsp; | ||
218 | int __user *parent_tidptr, *child_tidptr; | ||
219 | |||
220 | /* syscall2 puts clone_flags in d1 and usp in d2 */ | ||
221 | clone_flags = regs->d1; | ||
222 | newsp = regs->d2; | ||
223 | parent_tidptr = (int __user *)regs->d3; | ||
224 | child_tidptr = (int __user *)regs->d4; | ||
225 | if (!newsp) | ||
226 | newsp = rdusp(); | ||
227 | return do_fork(clone_flags, newsp, regs, 0, | ||
228 | parent_tidptr, child_tidptr); | ||
229 | } | ||
230 | |||
231 | int copy_thread(unsigned long clone_flags, unsigned long usp, | ||
232 | unsigned long unused, | ||
233 | struct task_struct * p, struct pt_regs * regs) | ||
234 | { | ||
235 | struct pt_regs * childregs; | ||
236 | struct switch_stack * childstack, *stack; | ||
237 | unsigned long *retp; | ||
238 | |||
239 | childregs = (struct pt_regs *) (task_stack_page(p) + THREAD_SIZE) - 1; | ||
240 | |||
241 | *childregs = *regs; | ||
242 | childregs->d0 = 0; | ||
243 | |||
244 | retp = ((unsigned long *) regs); | ||
245 | stack = ((struct switch_stack *) retp) - 1; | ||
246 | |||
247 | childstack = ((struct switch_stack *) childregs) - 1; | ||
248 | *childstack = *stack; | ||
249 | childstack->retpc = (unsigned long)ret_from_fork; | ||
250 | |||
251 | p->thread.usp = usp; | ||
252 | p->thread.ksp = (unsigned long)childstack; | ||
253 | |||
254 | if (clone_flags & CLONE_SETTLS) | ||
255 | task_thread_info(p)->tp_value = regs->d5; | ||
256 | |||
257 | /* | ||
258 | * Must save the current SFC/DFC value, NOT the value when | ||
259 | * the parent was last descheduled - RGH 10-08-96 | ||
260 | */ | ||
261 | p->thread.fs = get_fs().seg; | ||
262 | |||
263 | if (!FPU_IS_EMU) { | ||
264 | /* Copy the current fpu state */ | ||
265 | asm volatile ("fsave %0" : : "m" (p->thread.fpstate[0]) : "memory"); | ||
266 | |||
267 | if (!CPU_IS_060 ? p->thread.fpstate[0] : p->thread.fpstate[2]) | ||
268 | asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t" | ||
269 | "fmoveml %/fpiar/%/fpcr/%/fpsr,%1" | ||
270 | : : "m" (p->thread.fp[0]), "m" (p->thread.fpcntl[0]) | ||
271 | : "memory"); | ||
272 | /* Restore the state in case the fpu was busy */ | ||
273 | asm volatile ("frestore %0" : : "m" (p->thread.fpstate[0])); | ||
274 | } | ||
275 | |||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | /* Fill in the fpu structure for a core dump. */ | ||
280 | |||
281 | int dump_fpu (struct pt_regs *regs, struct user_m68kfp_struct *fpu) | ||
282 | { | ||
283 | char fpustate[216]; | ||
284 | |||
285 | if (FPU_IS_EMU) { | ||
286 | int i; | ||
287 | |||
288 | memcpy(fpu->fpcntl, current->thread.fpcntl, 12); | ||
289 | memcpy(fpu->fpregs, current->thread.fp, 96); | ||
290 | /* Convert internal fpu reg representation | ||
291 | * into long double format | ||
292 | */ | ||
293 | for (i = 0; i < 24; i += 3) | ||
294 | fpu->fpregs[i] = ((fpu->fpregs[i] & 0xffff0000) << 15) | | ||
295 | ((fpu->fpregs[i] & 0x0000ffff) << 16); | ||
296 | return 1; | ||
297 | } | ||
298 | |||
299 | /* First dump the fpu context to avoid protocol violation. */ | ||
300 | asm volatile ("fsave %0" :: "m" (fpustate[0]) : "memory"); | ||
301 | if (!CPU_IS_060 ? !fpustate[0] : !fpustate[2]) | ||
302 | return 0; | ||
303 | |||
304 | asm volatile ("fmovem %/fpiar/%/fpcr/%/fpsr,%0" | ||
305 | :: "m" (fpu->fpcntl[0]) | ||
306 | : "memory"); | ||
307 | asm volatile ("fmovemx %/fp0-%/fp7,%0" | ||
308 | :: "m" (fpu->fpregs[0]) | ||
309 | : "memory"); | ||
310 | return 1; | ||
311 | } | ||
312 | EXPORT_SYMBOL(dump_fpu); | ||
313 | |||
314 | /* | ||
315 | * sys_execve() executes a new program. | ||
316 | */ | ||
317 | asmlinkage int sys_execve(const char __user *name, | ||
318 | const char __user *const __user *argv, | ||
319 | const char __user *const __user *envp) | ||
320 | { | ||
321 | int error; | ||
322 | char * filename; | ||
323 | struct pt_regs *regs = (struct pt_regs *) &name; | ||
324 | |||
325 | filename = getname(name); | ||
326 | error = PTR_ERR(filename); | ||
327 | if (IS_ERR(filename)) | ||
328 | return error; | ||
329 | error = do_execve(filename, argv, envp, regs); | ||
330 | putname(filename); | ||
331 | return error; | ||
332 | } | ||
333 | |||
334 | unsigned long get_wchan(struct task_struct *p) | ||
335 | { | ||
336 | unsigned long fp, pc; | ||
337 | unsigned long stack_page; | ||
338 | int count = 0; | ||
339 | if (!p || p == current || p->state == TASK_RUNNING) | ||
340 | return 0; | ||
341 | |||
342 | stack_page = (unsigned long)task_stack_page(p); | ||
343 | fp = ((struct switch_stack *)p->thread.ksp)->a6; | ||
344 | do { | ||
345 | if (fp < stack_page+sizeof(struct thread_info) || | ||
346 | fp >= 8184+stack_page) | ||
347 | return 0; | ||
348 | pc = ((unsigned long *)fp)[1]; | ||
349 | if (!in_sched_functions(pc)) | ||
350 | return pc; | ||
351 | fp = *(unsigned long *) fp; | ||
352 | } while (count++ < 16); | ||
353 | return 0; | ||
354 | } | ||
diff --git a/arch/m68k/kernel/process_mm.c b/arch/m68k/kernel/process_mm.c new file mode 100644 index 000000000000..c2a1fc23dd75 --- /dev/null +++ b/arch/m68k/kernel/process_mm.c | |||
@@ -0,0 +1,354 @@ | |||
1 | /* | ||
2 | * linux/arch/m68k/kernel/process.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Hamish Macdonald | ||
5 | * | ||
6 | * 68060 fixes by Jesper Skov | ||
7 | */ | ||
8 | |||
9 | /* | ||
10 | * This file handles the architecture-dependent parts of process handling.. | ||
11 | */ | ||
12 | |||
13 | #include <linux/errno.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/mm.h> | ||
18 | #include <linux/slab.h> | ||
19 | #include <linux/fs.h> | ||
20 | #include <linux/smp.h> | ||
21 | #include <linux/stddef.h> | ||
22 | #include <linux/unistd.h> | ||
23 | #include <linux/ptrace.h> | ||
24 | #include <linux/user.h> | ||
25 | #include <linux/reboot.h> | ||
26 | #include <linux/init_task.h> | ||
27 | #include <linux/mqueue.h> | ||
28 | |||
29 | #include <asm/uaccess.h> | ||
30 | #include <asm/system.h> | ||
31 | #include <asm/traps.h> | ||
32 | #include <asm/machdep.h> | ||
33 | #include <asm/setup.h> | ||
34 | #include <asm/pgtable.h> | ||
35 | |||
36 | /* | ||
37 | * Initial task/thread structure. Make this a per-architecture thing, | ||
38 | * because different architectures tend to have different | ||
39 | * alignment requirements and potentially different initial | ||
40 | * setup. | ||
41 | */ | ||
42 | static struct signal_struct init_signals = INIT_SIGNALS(init_signals); | ||
43 | static struct sighand_struct init_sighand = INIT_SIGHAND(init_sighand); | ||
44 | union thread_union init_thread_union __init_task_data | ||
45 | __attribute__((aligned(THREAD_SIZE))) = | ||
46 | { INIT_THREAD_INFO(init_task) }; | ||
47 | |||
48 | /* initial task structure */ | ||
49 | struct task_struct init_task = INIT_TASK(init_task); | ||
50 | |||
51 | EXPORT_SYMBOL(init_task); | ||
52 | |||
53 | asmlinkage void ret_from_fork(void); | ||
54 | |||
55 | |||
56 | /* | ||
57 | * Return saved PC from a blocked thread | ||
58 | */ | ||
59 | unsigned long thread_saved_pc(struct task_struct *tsk) | ||
60 | { | ||
61 | struct switch_stack *sw = (struct switch_stack *)tsk->thread.ksp; | ||
62 | /* Check whether the thread is blocked in resume() */ | ||
63 | if (in_sched_functions(sw->retpc)) | ||
64 | return ((unsigned long *)sw->a6)[1]; | ||
65 | else | ||
66 | return sw->retpc; | ||
67 | } | ||
68 | |||
69 | /* | ||
70 | * The idle loop on an m68k.. | ||
71 | */ | ||
72 | static void default_idle(void) | ||
73 | { | ||
74 | if (!need_resched()) | ||
75 | #if defined(MACH_ATARI_ONLY) | ||
76 | /* block out HSYNC on the atari (falcon) */ | ||
77 | __asm__("stop #0x2200" : : : "cc"); | ||
78 | #else | ||
79 | __asm__("stop #0x2000" : : : "cc"); | ||
80 | #endif | ||
81 | } | ||
82 | |||
83 | void (*idle)(void) = default_idle; | ||
84 | |||
85 | /* | ||
86 | * The idle thread. There's no useful work to be | ||
87 | * done, so just try to conserve power and have a | ||
88 | * low exit latency (ie sit in a loop waiting for | ||
89 | * somebody to say that they'd like to reschedule) | ||
90 | */ | ||
91 | void cpu_idle(void) | ||
92 | { | ||
93 | /* endless idle loop with no priority at all */ | ||
94 | while (1) { | ||
95 | while (!need_resched()) | ||
96 | idle(); | ||
97 | preempt_enable_no_resched(); | ||
98 | schedule(); | ||
99 | preempt_disable(); | ||
100 | } | ||
101 | } | ||
102 | |||
103 | void machine_restart(char * __unused) | ||
104 | { | ||
105 | if (mach_reset) | ||
106 | mach_reset(); | ||
107 | for (;;); | ||
108 | } | ||
109 | |||
110 | void machine_halt(void) | ||
111 | { | ||
112 | if (mach_halt) | ||
113 | mach_halt(); | ||
114 | for (;;); | ||
115 | } | ||
116 | |||
117 | void machine_power_off(void) | ||
118 | { | ||
119 | if (mach_power_off) | ||
120 | mach_power_off(); | ||
121 | for (;;); | ||
122 | } | ||
123 | |||
124 | void (*pm_power_off)(void) = machine_power_off; | ||
125 | EXPORT_SYMBOL(pm_power_off); | ||
126 | |||
127 | void show_regs(struct pt_regs * regs) | ||
128 | { | ||
129 | printk("\n"); | ||
130 | printk("Format %02x Vector: %04x PC: %08lx Status: %04x %s\n", | ||
131 | regs->format, regs->vector, regs->pc, regs->sr, print_tainted()); | ||
132 | printk("ORIG_D0: %08lx D0: %08lx A2: %08lx A1: %08lx\n", | ||
133 | regs->orig_d0, regs->d0, regs->a2, regs->a1); | ||
134 | printk("A0: %08lx D5: %08lx D4: %08lx\n", | ||
135 | regs->a0, regs->d5, regs->d4); | ||
136 | printk("D3: %08lx D2: %08lx D1: %08lx\n", | ||
137 | regs->d3, regs->d2, regs->d1); | ||
138 | if (!(regs->sr & PS_S)) | ||
139 | printk("USP: %08lx\n", rdusp()); | ||
140 | } | ||
141 | |||
142 | /* | ||
143 | * Create a kernel thread | ||
144 | */ | ||
145 | int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) | ||
146 | { | ||
147 | int pid; | ||
148 | mm_segment_t fs; | ||
149 | |||
150 | fs = get_fs(); | ||
151 | set_fs (KERNEL_DS); | ||
152 | |||
153 | { | ||
154 | register long retval __asm__ ("d0"); | ||
155 | register long clone_arg __asm__ ("d1") = flags | CLONE_VM | CLONE_UNTRACED; | ||
156 | |||
157 | retval = __NR_clone; | ||
158 | __asm__ __volatile__ | ||
159 | ("clrl %%d2\n\t" | ||
160 | "trap #0\n\t" /* Linux/m68k system call */ | ||
161 | "tstl %0\n\t" /* child or parent */ | ||
162 | "jne 1f\n\t" /* parent - jump */ | ||
163 | "lea %%sp@(%c7),%6\n\t" /* reload current */ | ||
164 | "movel %6@,%6\n\t" | ||
165 | "movel %3,%%sp@-\n\t" /* push argument */ | ||
166 | "jsr %4@\n\t" /* call fn */ | ||
167 | "movel %0,%%d1\n\t" /* pass exit value */ | ||
168 | "movel %2,%%d0\n\t" /* exit */ | ||
169 | "trap #0\n" | ||
170 | "1:" | ||
171 | : "+d" (retval) | ||
172 | : "i" (__NR_clone), "i" (__NR_exit), | ||
173 | "r" (arg), "a" (fn), "d" (clone_arg), "r" (current), | ||
174 | "i" (-THREAD_SIZE) | ||
175 | : "d2"); | ||
176 | |||
177 | pid = retval; | ||
178 | } | ||
179 | |||
180 | set_fs (fs); | ||
181 | return pid; | ||
182 | } | ||
183 | EXPORT_SYMBOL(kernel_thread); | ||
184 | |||
185 | void flush_thread(void) | ||
186 | { | ||
187 | unsigned long zero = 0; | ||
188 | set_fs(USER_DS); | ||
189 | current->thread.fs = __USER_DS; | ||
190 | if (!FPU_IS_EMU) | ||
191 | asm volatile (".chip 68k/68881\n\t" | ||
192 | "frestore %0@\n\t" | ||
193 | ".chip 68k" : : "a" (&zero)); | ||
194 | } | ||
195 | |||
196 | /* | ||
197 | * "m68k_fork()".. By the time we get here, the | ||
198 | * non-volatile registers have also been saved on the | ||
199 | * stack. We do some ugly pointer stuff here.. (see | ||
200 | * also copy_thread) | ||
201 | */ | ||
202 | |||
203 | asmlinkage int m68k_fork(struct pt_regs *regs) | ||
204 | { | ||
205 | return do_fork(SIGCHLD, rdusp(), regs, 0, NULL, NULL); | ||
206 | } | ||
207 | |||
208 | asmlinkage int m68k_vfork(struct pt_regs *regs) | ||
209 | { | ||
210 | return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, | ||
211 | NULL, NULL); | ||
212 | } | ||
213 | |||
214 | asmlinkage int m68k_clone(struct pt_regs *regs) | ||
215 | { | ||
216 | unsigned long clone_flags; | ||
217 | unsigned long newsp; | ||
218 | int __user *parent_tidptr, *child_tidptr; | ||
219 | |||
220 | /* syscall2 puts clone_flags in d1 and usp in d2 */ | ||
221 | clone_flags = regs->d1; | ||
222 | newsp = regs->d2; | ||
223 | parent_tidptr = (int __user *)regs->d3; | ||
224 | child_tidptr = (int __user *)regs->d4; | ||
225 | if (!newsp) | ||
226 | newsp = rdusp(); | ||
227 | return do_fork(clone_flags, newsp, regs, 0, | ||
228 | parent_tidptr, child_tidptr); | ||
229 | } | ||
230 | |||
231 | int copy_thread(unsigned long clone_flags, unsigned long usp, | ||
232 | unsigned long unused, | ||
233 | struct task_struct * p, struct pt_regs * regs) | ||
234 | { | ||
235 | struct pt_regs * childregs; | ||
236 | struct switch_stack * childstack, *stack; | ||
237 | unsigned long *retp; | ||
238 | |||
239 | childregs = (struct pt_regs *) (task_stack_page(p) + THREAD_SIZE) - 1; | ||
240 | |||
241 | *childregs = *regs; | ||
242 | childregs->d0 = 0; | ||
243 | |||
244 | retp = ((unsigned long *) regs); | ||
245 | stack = ((struct switch_stack *) retp) - 1; | ||
246 | |||
247 | childstack = ((struct switch_stack *) childregs) - 1; | ||
248 | *childstack = *stack; | ||
249 | childstack->retpc = (unsigned long)ret_from_fork; | ||
250 | |||
251 | p->thread.usp = usp; | ||
252 | p->thread.ksp = (unsigned long)childstack; | ||
253 | |||
254 | if (clone_flags & CLONE_SETTLS) | ||
255 | task_thread_info(p)->tp_value = regs->d5; | ||
256 | |||
257 | /* | ||
258 | * Must save the current SFC/DFC value, NOT the value when | ||
259 | * the parent was last descheduled - RGH 10-08-96 | ||
260 | */ | ||
261 | p->thread.fs = get_fs().seg; | ||
262 | |||
263 | if (!FPU_IS_EMU) { | ||
264 | /* Copy the current fpu state */ | ||
265 | asm volatile ("fsave %0" : : "m" (p->thread.fpstate[0]) : "memory"); | ||
266 | |||
267 | if (!CPU_IS_060 ? p->thread.fpstate[0] : p->thread.fpstate[2]) | ||
268 | asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t" | ||
269 | "fmoveml %/fpiar/%/fpcr/%/fpsr,%1" | ||
270 | : : "m" (p->thread.fp[0]), "m" (p->thread.fpcntl[0]) | ||
271 | : "memory"); | ||
272 | /* Restore the state in case the fpu was busy */ | ||
273 | asm volatile ("frestore %0" : : "m" (p->thread.fpstate[0])); | ||
274 | } | ||
275 | |||
276 | return 0; | ||
277 | } | ||
278 | |||
279 | /* Fill in the fpu structure for a core dump. */ | ||
280 | |||
281 | int dump_fpu (struct pt_regs *regs, struct user_m68kfp_struct *fpu) | ||
282 | { | ||
283 | char fpustate[216]; | ||
284 | |||
285 | if (FPU_IS_EMU) { | ||
286 | int i; | ||
287 | |||
288 | memcpy(fpu->fpcntl, current->thread.fpcntl, 12); | ||
289 | memcpy(fpu->fpregs, current->thread.fp, 96); | ||
290 | /* Convert internal fpu reg representation | ||
291 | * into long double format | ||
292 | */ | ||
293 | for (i = 0; i < 24; i += 3) | ||
294 | fpu->fpregs[i] = ((fpu->fpregs[i] & 0xffff0000) << 15) | | ||
295 | ((fpu->fpregs[i] & 0x0000ffff) << 16); | ||
296 | return 1; | ||
297 | } | ||
298 | |||
299 | /* First dump the fpu context to avoid protocol violation. */ | ||
300 | asm volatile ("fsave %0" :: "m" (fpustate[0]) : "memory"); | ||
301 | if (!CPU_IS_060 ? !fpustate[0] : !fpustate[2]) | ||
302 | return 0; | ||
303 | |||
304 | asm volatile ("fmovem %/fpiar/%/fpcr/%/fpsr,%0" | ||
305 | :: "m" (fpu->fpcntl[0]) | ||
306 | : "memory"); | ||
307 | asm volatile ("fmovemx %/fp0-%/fp7,%0" | ||
308 | :: "m" (fpu->fpregs[0]) | ||
309 | : "memory"); | ||
310 | return 1; | ||
311 | } | ||
312 | EXPORT_SYMBOL(dump_fpu); | ||
313 | |||
314 | /* | ||
315 | * sys_execve() executes a new program. | ||
316 | */ | ||
317 | asmlinkage int sys_execve(const char __user *name, | ||
318 | const char __user *const __user *argv, | ||
319 | const char __user *const __user *envp) | ||
320 | { | ||
321 | int error; | ||
322 | char * filename; | ||
323 | struct pt_regs *regs = (struct pt_regs *) &name; | ||
324 | |||
325 | filename = getname(name); | ||
326 | error = PTR_ERR(filename); | ||
327 | if (IS_ERR(filename)) | ||
328 | return error; | ||
329 | error = do_execve(filename, argv, envp, regs); | ||
330 | putname(filename); | ||
331 | return error; | ||
332 | } | ||
333 | |||
334 | unsigned long get_wchan(struct task_struct *p) | ||
335 | { | ||
336 | unsigned long fp, pc; | ||
337 | unsigned long stack_page; | ||
338 | int count = 0; | ||
339 | if (!p || p == current || p->state == TASK_RUNNING) | ||
340 | return 0; | ||
341 | |||
342 | stack_page = (unsigned long)task_stack_page(p); | ||
343 | fp = ((struct switch_stack *)p->thread.ksp)->a6; | ||
344 | do { | ||
345 | if (fp < stack_page+sizeof(struct thread_info) || | ||
346 | fp >= 8184+stack_page) | ||
347 | return 0; | ||
348 | pc = ((unsigned long *)fp)[1]; | ||
349 | if (!in_sched_functions(pc)) | ||
350 | return pc; | ||
351 | fp = *(unsigned long *) fp; | ||
352 | } while (count++ < 16); | ||
353 | return 0; | ||
354 | } | ||
diff --git a/arch/m68k/kernel/process_no.c b/arch/m68k/kernel/process_no.c new file mode 100644 index 000000000000..e2a63af5d517 --- /dev/null +++ b/arch/m68k/kernel/process_no.c | |||
@@ -0,0 +1,404 @@ | |||
1 | /* | ||
2 | * linux/arch/m68knommu/kernel/process.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Hamish Macdonald | ||
5 | * | ||
6 | * 68060 fixes by Jesper Skov | ||
7 | * | ||
8 | * uClinux changes | ||
9 | * Copyright (C) 2000-2002, David McCullough <davidm@snapgear.com> | ||
10 | */ | ||
11 | |||
12 | /* | ||
13 | * This file handles the architecture-dependent parts of process handling.. | ||
14 | */ | ||
15 | |||
16 | #include <linux/module.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/sched.h> | ||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/mm.h> | ||
21 | #include <linux/smp.h> | ||
22 | #include <linux/stddef.h> | ||
23 | #include <linux/unistd.h> | ||
24 | #include <linux/ptrace.h> | ||
25 | #include <linux/user.h> | ||
26 | #include <linux/interrupt.h> | ||
27 | #include <linux/reboot.h> | ||
28 | #include <linux/fs.h> | ||
29 | #include <linux/slab.h> | ||
30 | |||
31 | #include <asm/uaccess.h> | ||
32 | #include <asm/system.h> | ||
33 | #include <asm/traps.h> | ||
34 | #include <asm/machdep.h> | ||
35 | #include <asm/setup.h> | ||
36 | #include <asm/pgtable.h> | ||
37 | |||
38 | asmlinkage void ret_from_fork(void); | ||
39 | |||
40 | /* | ||
41 | * The following aren't currently used. | ||
42 | */ | ||
43 | void (*pm_idle)(void); | ||
44 | EXPORT_SYMBOL(pm_idle); | ||
45 | |||
46 | void (*pm_power_off)(void); | ||
47 | EXPORT_SYMBOL(pm_power_off); | ||
48 | |||
49 | /* | ||
50 | * The idle loop on an m68knommu.. | ||
51 | */ | ||
52 | static void default_idle(void) | ||
53 | { | ||
54 | local_irq_disable(); | ||
55 | while (!need_resched()) { | ||
56 | /* This stop will re-enable interrupts */ | ||
57 | __asm__("stop #0x2000" : : : "cc"); | ||
58 | local_irq_disable(); | ||
59 | } | ||
60 | local_irq_enable(); | ||
61 | } | ||
62 | |||
63 | void (*idle)(void) = default_idle; | ||
64 | |||
65 | /* | ||
66 | * The idle thread. There's no useful work to be | ||
67 | * done, so just try to conserve power and have a | ||
68 | * low exit latency (ie sit in a loop waiting for | ||
69 | * somebody to say that they'd like to reschedule) | ||
70 | */ | ||
71 | void cpu_idle(void) | ||
72 | { | ||
73 | /* endless idle loop with no priority at all */ | ||
74 | while (1) { | ||
75 | idle(); | ||
76 | preempt_enable_no_resched(); | ||
77 | schedule(); | ||
78 | preempt_disable(); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | void machine_restart(char * __unused) | ||
83 | { | ||
84 | if (mach_reset) | ||
85 | mach_reset(); | ||
86 | for (;;); | ||
87 | } | ||
88 | |||
89 | void machine_halt(void) | ||
90 | { | ||
91 | if (mach_halt) | ||
92 | mach_halt(); | ||
93 | for (;;); | ||
94 | } | ||
95 | |||
96 | void machine_power_off(void) | ||
97 | { | ||
98 | if (mach_power_off) | ||
99 | mach_power_off(); | ||
100 | for (;;); | ||
101 | } | ||
102 | |||
103 | void show_regs(struct pt_regs * regs) | ||
104 | { | ||
105 | printk(KERN_NOTICE "\n"); | ||
106 | printk(KERN_NOTICE "Format %02x Vector: %04x PC: %08lx Status: %04x %s\n", | ||
107 | regs->format, regs->vector, regs->pc, regs->sr, print_tainted()); | ||
108 | printk(KERN_NOTICE "ORIG_D0: %08lx D0: %08lx A2: %08lx A1: %08lx\n", | ||
109 | regs->orig_d0, regs->d0, regs->a2, regs->a1); | ||
110 | printk(KERN_NOTICE "A0: %08lx D5: %08lx D4: %08lx\n", | ||
111 | regs->a0, regs->d5, regs->d4); | ||
112 | printk(KERN_NOTICE "D3: %08lx D2: %08lx D1: %08lx\n", | ||
113 | regs->d3, regs->d2, regs->d1); | ||
114 | if (!(regs->sr & PS_S)) | ||
115 | printk(KERN_NOTICE "USP: %08lx\n", rdusp()); | ||
116 | } | ||
117 | |||
118 | /* | ||
119 | * Create a kernel thread | ||
120 | */ | ||
121 | int kernel_thread(int (*fn)(void *), void * arg, unsigned long flags) | ||
122 | { | ||
123 | int retval; | ||
124 | long clone_arg = flags | CLONE_VM; | ||
125 | mm_segment_t fs; | ||
126 | |||
127 | fs = get_fs(); | ||
128 | set_fs(KERNEL_DS); | ||
129 | |||
130 | __asm__ __volatile__ ( | ||
131 | "movel %%sp, %%d2\n\t" | ||
132 | "movel %5, %%d1\n\t" | ||
133 | "movel %1, %%d0\n\t" | ||
134 | "trap #0\n\t" | ||
135 | "cmpl %%sp, %%d2\n\t" | ||
136 | "jeq 1f\n\t" | ||
137 | "movel %3, %%sp@-\n\t" | ||
138 | "jsr %4@\n\t" | ||
139 | "movel %2, %%d0\n\t" | ||
140 | "trap #0\n" | ||
141 | "1:\n\t" | ||
142 | "movel %%d0, %0\n" | ||
143 | : "=d" (retval) | ||
144 | : "i" (__NR_clone), | ||
145 | "i" (__NR_exit), | ||
146 | "a" (arg), | ||
147 | "a" (fn), | ||
148 | "a" (clone_arg) | ||
149 | : "cc", "%d0", "%d1", "%d2"); | ||
150 | |||
151 | set_fs(fs); | ||
152 | return retval; | ||
153 | } | ||
154 | |||
155 | void flush_thread(void) | ||
156 | { | ||
157 | #ifdef CONFIG_FPU | ||
158 | unsigned long zero = 0; | ||
159 | #endif | ||
160 | set_fs(USER_DS); | ||
161 | current->thread.fs = __USER_DS; | ||
162 | #ifdef CONFIG_FPU | ||
163 | if (!FPU_IS_EMU) | ||
164 | asm volatile (".chip 68k/68881\n\t" | ||
165 | "frestore %0@\n\t" | ||
166 | ".chip 68k" : : "a" (&zero)); | ||
167 | #endif | ||
168 | } | ||
169 | |||
170 | /* | ||
171 | * "m68k_fork()".. By the time we get here, the | ||
172 | * non-volatile registers have also been saved on the | ||
173 | * stack. We do some ugly pointer stuff here.. (see | ||
174 | * also copy_thread) | ||
175 | */ | ||
176 | |||
177 | asmlinkage int m68k_fork(struct pt_regs *regs) | ||
178 | { | ||
179 | /* fork almost works, enough to trick you into looking elsewhere :-( */ | ||
180 | return(-EINVAL); | ||
181 | } | ||
182 | |||
183 | asmlinkage int m68k_vfork(struct pt_regs *regs) | ||
184 | { | ||
185 | return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, rdusp(), regs, 0, NULL, NULL); | ||
186 | } | ||
187 | |||
188 | asmlinkage int m68k_clone(struct pt_regs *regs) | ||
189 | { | ||
190 | unsigned long clone_flags; | ||
191 | unsigned long newsp; | ||
192 | |||
193 | /* syscall2 puts clone_flags in d1 and usp in d2 */ | ||
194 | clone_flags = regs->d1; | ||
195 | newsp = regs->d2; | ||
196 | if (!newsp) | ||
197 | newsp = rdusp(); | ||
198 | return do_fork(clone_flags, newsp, regs, 0, NULL, NULL); | ||
199 | } | ||
200 | |||
201 | int copy_thread(unsigned long clone_flags, | ||
202 | unsigned long usp, unsigned long topstk, | ||
203 | struct task_struct * p, struct pt_regs * regs) | ||
204 | { | ||
205 | struct pt_regs * childregs; | ||
206 | struct switch_stack * childstack, *stack; | ||
207 | unsigned long *retp; | ||
208 | |||
209 | childregs = (struct pt_regs *) (task_stack_page(p) + THREAD_SIZE) - 1; | ||
210 | |||
211 | *childregs = *regs; | ||
212 | childregs->d0 = 0; | ||
213 | |||
214 | retp = ((unsigned long *) regs); | ||
215 | stack = ((struct switch_stack *) retp) - 1; | ||
216 | |||
217 | childstack = ((struct switch_stack *) childregs) - 1; | ||
218 | *childstack = *stack; | ||
219 | childstack->retpc = (unsigned long)ret_from_fork; | ||
220 | |||
221 | p->thread.usp = usp; | ||
222 | p->thread.ksp = (unsigned long)childstack; | ||
223 | |||
224 | if (clone_flags & CLONE_SETTLS) | ||
225 | task_thread_info(p)->tp_value = regs->d5; | ||
226 | |||
227 | /* | ||
228 | * Must save the current SFC/DFC value, NOT the value when | ||
229 | * the parent was last descheduled - RGH 10-08-96 | ||
230 | */ | ||
231 | p->thread.fs = get_fs().seg; | ||
232 | |||
233 | #ifdef CONFIG_FPU | ||
234 | if (!FPU_IS_EMU) { | ||
235 | /* Copy the current fpu state */ | ||
236 | asm volatile ("fsave %0" : : "m" (p->thread.fpstate[0]) : "memory"); | ||
237 | |||
238 | if (p->thread.fpstate[0]) | ||
239 | asm volatile ("fmovemx %/fp0-%/fp7,%0\n\t" | ||
240 | "fmoveml %/fpiar/%/fpcr/%/fpsr,%1" | ||
241 | : : "m" (p->thread.fp[0]), "m" (p->thread.fpcntl[0]) | ||
242 | : "memory"); | ||
243 | /* Restore the state in case the fpu was busy */ | ||
244 | asm volatile ("frestore %0" : : "m" (p->thread.fpstate[0])); | ||
245 | } | ||
246 | #endif | ||
247 | |||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | /* Fill in the fpu structure for a core dump. */ | ||
252 | |||
253 | int dump_fpu(struct pt_regs *regs, struct user_m68kfp_struct *fpu) | ||
254 | { | ||
255 | #ifdef CONFIG_FPU | ||
256 | char fpustate[216]; | ||
257 | |||
258 | if (FPU_IS_EMU) { | ||
259 | int i; | ||
260 | |||
261 | memcpy(fpu->fpcntl, current->thread.fpcntl, 12); | ||
262 | memcpy(fpu->fpregs, current->thread.fp, 96); | ||
263 | /* Convert internal fpu reg representation | ||
264 | * into long double format | ||
265 | */ | ||
266 | for (i = 0; i < 24; i += 3) | ||
267 | fpu->fpregs[i] = ((fpu->fpregs[i] & 0xffff0000) << 15) | | ||
268 | ((fpu->fpregs[i] & 0x0000ffff) << 16); | ||
269 | return 1; | ||
270 | } | ||
271 | |||
272 | /* First dump the fpu context to avoid protocol violation. */ | ||
273 | asm volatile ("fsave %0" :: "m" (fpustate[0]) : "memory"); | ||
274 | if (!fpustate[0]) | ||
275 | return 0; | ||
276 | |||
277 | asm volatile ("fmovem %/fpiar/%/fpcr/%/fpsr,%0" | ||
278 | :: "m" (fpu->fpcntl[0]) | ||
279 | : "memory"); | ||
280 | asm volatile ("fmovemx %/fp0-%/fp7,%0" | ||
281 | :: "m" (fpu->fpregs[0]) | ||
282 | : "memory"); | ||
283 | #endif | ||
284 | return 1; | ||
285 | } | ||
286 | |||
287 | /* | ||
288 | * Generic dumping code. Used for panic and debug. | ||
289 | */ | ||
290 | void dump(struct pt_regs *fp) | ||
291 | { | ||
292 | unsigned long *sp; | ||
293 | unsigned char *tp; | ||
294 | int i; | ||
295 | |||
296 | printk(KERN_EMERG "\nCURRENT PROCESS:\n\n"); | ||
297 | printk(KERN_EMERG "COMM=%s PID=%d\n", current->comm, current->pid); | ||
298 | |||
299 | if (current->mm) { | ||
300 | printk(KERN_EMERG "TEXT=%08x-%08x DATA=%08x-%08x BSS=%08x-%08x\n", | ||
301 | (int) current->mm->start_code, | ||
302 | (int) current->mm->end_code, | ||
303 | (int) current->mm->start_data, | ||
304 | (int) current->mm->end_data, | ||
305 | (int) current->mm->end_data, | ||
306 | (int) current->mm->brk); | ||
307 | printk(KERN_EMERG "USER-STACK=%08x KERNEL-STACK=%08x\n\n", | ||
308 | (int) current->mm->start_stack, | ||
309 | (int)(((unsigned long) current) + THREAD_SIZE)); | ||
310 | } | ||
311 | |||
312 | printk(KERN_EMERG "PC: %08lx\n", fp->pc); | ||
313 | printk(KERN_EMERG "SR: %08lx SP: %08lx\n", (long) fp->sr, (long) fp); | ||
314 | printk(KERN_EMERG "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", | ||
315 | fp->d0, fp->d1, fp->d2, fp->d3); | ||
316 | printk(KERN_EMERG "d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n", | ||
317 | fp->d4, fp->d5, fp->a0, fp->a1); | ||
318 | printk(KERN_EMERG "\nUSP: %08x TRAPFRAME: %p\n", | ||
319 | (unsigned int) rdusp(), fp); | ||
320 | |||
321 | printk(KERN_EMERG "\nCODE:"); | ||
322 | tp = ((unsigned char *) fp->pc) - 0x20; | ||
323 | for (sp = (unsigned long *) tp, i = 0; (i < 0x40); i += 4) { | ||
324 | if ((i % 0x10) == 0) | ||
325 | printk(KERN_EMERG "%p: ", tp + i); | ||
326 | printk("%08x ", (int) *sp++); | ||
327 | } | ||
328 | printk(KERN_EMERG "\n"); | ||
329 | |||
330 | printk(KERN_EMERG "KERNEL STACK:"); | ||
331 | tp = ((unsigned char *) fp) - 0x40; | ||
332 | for (sp = (unsigned long *) tp, i = 0; (i < 0xc0); i += 4) { | ||
333 | if ((i % 0x10) == 0) | ||
334 | printk(KERN_EMERG "%p: ", tp + i); | ||
335 | printk("%08x ", (int) *sp++); | ||
336 | } | ||
337 | printk(KERN_EMERG "\n"); | ||
338 | |||
339 | printk(KERN_EMERG "USER STACK:"); | ||
340 | tp = (unsigned char *) (rdusp() - 0x10); | ||
341 | for (sp = (unsigned long *) tp, i = 0; (i < 0x80); i += 4) { | ||
342 | if ((i % 0x10) == 0) | ||
343 | printk(KERN_EMERG "%p: ", tp + i); | ||
344 | printk("%08x ", (int) *sp++); | ||
345 | } | ||
346 | printk(KERN_EMERG "\n"); | ||
347 | } | ||
348 | |||
349 | /* | ||
350 | * sys_execve() executes a new program. | ||
351 | */ | ||
352 | asmlinkage int sys_execve(const char *name, | ||
353 | const char *const *argv, | ||
354 | const char *const *envp) | ||
355 | { | ||
356 | int error; | ||
357 | char * filename; | ||
358 | struct pt_regs *regs = (struct pt_regs *) &name; | ||
359 | |||
360 | filename = getname(name); | ||
361 | error = PTR_ERR(filename); | ||
362 | if (IS_ERR(filename)) | ||
363 | return error; | ||
364 | error = do_execve(filename, argv, envp, regs); | ||
365 | putname(filename); | ||
366 | return error; | ||
367 | } | ||
368 | |||
369 | unsigned long get_wchan(struct task_struct *p) | ||
370 | { | ||
371 | unsigned long fp, pc; | ||
372 | unsigned long stack_page; | ||
373 | int count = 0; | ||
374 | if (!p || p == current || p->state == TASK_RUNNING) | ||
375 | return 0; | ||
376 | |||
377 | stack_page = (unsigned long)p; | ||
378 | fp = ((struct switch_stack *)p->thread.ksp)->a6; | ||
379 | do { | ||
380 | if (fp < stack_page+sizeof(struct thread_info) || | ||
381 | fp >= THREAD_SIZE-8+stack_page) | ||
382 | return 0; | ||
383 | pc = ((unsigned long *)fp)[1]; | ||
384 | if (!in_sched_functions(pc)) | ||
385 | return pc; | ||
386 | fp = *(unsigned long *) fp; | ||
387 | } while (count++ < 16); | ||
388 | return 0; | ||
389 | } | ||
390 | |||
391 | /* | ||
392 | * Return saved PC of a blocked thread. | ||
393 | */ | ||
394 | unsigned long thread_saved_pc(struct task_struct *tsk) | ||
395 | { | ||
396 | struct switch_stack *sw = (struct switch_stack *)tsk->thread.ksp; | ||
397 | |||
398 | /* Check whether the thread is blocked in resume() */ | ||
399 | if (in_sched_functions(sw->retpc)) | ||
400 | return ((unsigned long *)sw->a6)[1]; | ||
401 | else | ||
402 | return sw->retpc; | ||
403 | } | ||
404 | |||
diff --git a/arch/m68k/kernel/ptrace.c b/arch/m68k/kernel/ptrace.c index 0b252683cefb..07a417550e94 100644 --- a/arch/m68k/kernel/ptrace.c +++ b/arch/m68k/kernel/ptrace.c | |||
@@ -1,277 +1,5 @@ | |||
1 | /* | 1 | #ifdef CONFIG_MMU |
2 | * linux/arch/m68k/kernel/ptrace.c | 2 | #include "ptrace_mm.c" |
3 | * | 3 | #else |
4 | * Copyright (C) 1994 by Hamish Macdonald | 4 | #include "ptrace_no.c" |
5 | * Taken from linux/kernel/ptrace.c and modified for M680x0. | 5 | #endif |
6 | * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds | ||
7 | * | ||
8 | * This file is subject to the terms and conditions of the GNU General | ||
9 | * Public License. See the file COPYING in the main directory of | ||
10 | * this archive for more details. | ||
11 | */ | ||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/mm.h> | ||
16 | #include <linux/smp.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/ptrace.h> | ||
19 | #include <linux/user.h> | ||
20 | #include <linux/signal.h> | ||
21 | |||
22 | #include <asm/uaccess.h> | ||
23 | #include <asm/page.h> | ||
24 | #include <asm/pgtable.h> | ||
25 | #include <asm/system.h> | ||
26 | #include <asm/processor.h> | ||
27 | |||
28 | /* | ||
29 | * does not yet catch signals sent when the child dies. | ||
30 | * in exit.c or in signal.c. | ||
31 | */ | ||
32 | |||
33 | /* determines which bits in the SR the user has access to. */ | ||
34 | /* 1 = access 0 = no access */ | ||
35 | #define SR_MASK 0x001f | ||
36 | |||
37 | /* sets the trace bits. */ | ||
38 | #define TRACE_BITS 0xC000 | ||
39 | #define T1_BIT 0x8000 | ||
40 | #define T0_BIT 0x4000 | ||
41 | |||
42 | /* Find the stack offset for a register, relative to thread.esp0. */ | ||
43 | #define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg) | ||
44 | #define SW_REG(reg) ((long)&((struct switch_stack *)0)->reg \ | ||
45 | - sizeof(struct switch_stack)) | ||
46 | /* Mapping from PT_xxx to the stack offset at which the register is | ||
47 | saved. Notice that usp has no stack-slot and needs to be treated | ||
48 | specially (see get_reg/put_reg below). */ | ||
49 | static const int regoff[] = { | ||
50 | [0] = PT_REG(d1), | ||
51 | [1] = PT_REG(d2), | ||
52 | [2] = PT_REG(d3), | ||
53 | [3] = PT_REG(d4), | ||
54 | [4] = PT_REG(d5), | ||
55 | [5] = SW_REG(d6), | ||
56 | [6] = SW_REG(d7), | ||
57 | [7] = PT_REG(a0), | ||
58 | [8] = PT_REG(a1), | ||
59 | [9] = PT_REG(a2), | ||
60 | [10] = SW_REG(a3), | ||
61 | [11] = SW_REG(a4), | ||
62 | [12] = SW_REG(a5), | ||
63 | [13] = SW_REG(a6), | ||
64 | [14] = PT_REG(d0), | ||
65 | [15] = -1, | ||
66 | [16] = PT_REG(orig_d0), | ||
67 | [17] = PT_REG(sr), | ||
68 | [18] = PT_REG(pc), | ||
69 | }; | ||
70 | |||
71 | /* | ||
72 | * Get contents of register REGNO in task TASK. | ||
73 | */ | ||
74 | static inline long get_reg(struct task_struct *task, int regno) | ||
75 | { | ||
76 | unsigned long *addr; | ||
77 | |||
78 | if (regno == PT_USP) | ||
79 | addr = &task->thread.usp; | ||
80 | else if (regno < ARRAY_SIZE(regoff)) | ||
81 | addr = (unsigned long *)(task->thread.esp0 + regoff[regno]); | ||
82 | else | ||
83 | return 0; | ||
84 | /* Need to take stkadj into account. */ | ||
85 | if (regno == PT_SR || regno == PT_PC) { | ||
86 | long stkadj = *(long *)(task->thread.esp0 + PT_REG(stkadj)); | ||
87 | addr = (unsigned long *) ((unsigned long)addr + stkadj); | ||
88 | /* The sr is actually a 16 bit register. */ | ||
89 | if (regno == PT_SR) | ||
90 | return *(unsigned short *)addr; | ||
91 | } | ||
92 | return *addr; | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * Write contents of register REGNO in task TASK. | ||
97 | */ | ||
98 | static inline int put_reg(struct task_struct *task, int regno, | ||
99 | unsigned long data) | ||
100 | { | ||
101 | unsigned long *addr; | ||
102 | |||
103 | if (regno == PT_USP) | ||
104 | addr = &task->thread.usp; | ||
105 | else if (regno < ARRAY_SIZE(regoff)) | ||
106 | addr = (unsigned long *)(task->thread.esp0 + regoff[regno]); | ||
107 | else | ||
108 | return -1; | ||
109 | /* Need to take stkadj into account. */ | ||
110 | if (regno == PT_SR || regno == PT_PC) { | ||
111 | long stkadj = *(long *)(task->thread.esp0 + PT_REG(stkadj)); | ||
112 | addr = (unsigned long *) ((unsigned long)addr + stkadj); | ||
113 | /* The sr is actually a 16 bit register. */ | ||
114 | if (regno == PT_SR) { | ||
115 | *(unsigned short *)addr = data; | ||
116 | return 0; | ||
117 | } | ||
118 | } | ||
119 | *addr = data; | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | * Make sure the single step bit is not set. | ||
125 | */ | ||
126 | static inline void singlestep_disable(struct task_struct *child) | ||
127 | { | ||
128 | unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS; | ||
129 | put_reg(child, PT_SR, tmp); | ||
130 | clear_tsk_thread_flag(child, TIF_DELAYED_TRACE); | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * Called by kernel/ptrace.c when detaching.. | ||
135 | */ | ||
136 | void ptrace_disable(struct task_struct *child) | ||
137 | { | ||
138 | singlestep_disable(child); | ||
139 | } | ||
140 | |||
141 | void user_enable_single_step(struct task_struct *child) | ||
142 | { | ||
143 | unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS; | ||
144 | put_reg(child, PT_SR, tmp | T1_BIT); | ||
145 | set_tsk_thread_flag(child, TIF_DELAYED_TRACE); | ||
146 | } | ||
147 | |||
148 | void user_enable_block_step(struct task_struct *child) | ||
149 | { | ||
150 | unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS; | ||
151 | put_reg(child, PT_SR, tmp | T0_BIT); | ||
152 | } | ||
153 | |||
154 | void user_disable_single_step(struct task_struct *child) | ||
155 | { | ||
156 | singlestep_disable(child); | ||
157 | } | ||
158 | |||
159 | long arch_ptrace(struct task_struct *child, long request, | ||
160 | unsigned long addr, unsigned long data) | ||
161 | { | ||
162 | unsigned long tmp; | ||
163 | int i, ret = 0; | ||
164 | int regno = addr >> 2; /* temporary hack. */ | ||
165 | unsigned long __user *datap = (unsigned long __user *) data; | ||
166 | |||
167 | switch (request) { | ||
168 | /* read the word at location addr in the USER area. */ | ||
169 | case PTRACE_PEEKUSR: | ||
170 | if (addr & 3) | ||
171 | goto out_eio; | ||
172 | |||
173 | if (regno >= 0 && regno < 19) { | ||
174 | tmp = get_reg(child, regno); | ||
175 | } else if (regno >= 21 && regno < 49) { | ||
176 | tmp = child->thread.fp[regno - 21]; | ||
177 | /* Convert internal fpu reg representation | ||
178 | * into long double format | ||
179 | */ | ||
180 | if (FPU_IS_EMU && (regno < 45) && !(regno % 3)) | ||
181 | tmp = ((tmp & 0xffff0000) << 15) | | ||
182 | ((tmp & 0x0000ffff) << 16); | ||
183 | } else | ||
184 | goto out_eio; | ||
185 | ret = put_user(tmp, datap); | ||
186 | break; | ||
187 | |||
188 | case PTRACE_POKEUSR: | ||
189 | /* write the word at location addr in the USER area */ | ||
190 | if (addr & 3) | ||
191 | goto out_eio; | ||
192 | |||
193 | if (regno == PT_SR) { | ||
194 | data &= SR_MASK; | ||
195 | data |= get_reg(child, PT_SR) & ~SR_MASK; | ||
196 | } | ||
197 | if (regno >= 0 && regno < 19) { | ||
198 | if (put_reg(child, regno, data)) | ||
199 | goto out_eio; | ||
200 | } else if (regno >= 21 && regno < 48) { | ||
201 | /* Convert long double format | ||
202 | * into internal fpu reg representation | ||
203 | */ | ||
204 | if (FPU_IS_EMU && (regno < 45) && !(regno % 3)) { | ||
205 | data <<= 15; | ||
206 | data = (data & 0xffff0000) | | ||
207 | ((data & 0x0000ffff) >> 1); | ||
208 | } | ||
209 | child->thread.fp[regno - 21] = data; | ||
210 | } else | ||
211 | goto out_eio; | ||
212 | break; | ||
213 | |||
214 | case PTRACE_GETREGS: /* Get all gp regs from the child. */ | ||
215 | for (i = 0; i < 19; i++) { | ||
216 | tmp = get_reg(child, i); | ||
217 | ret = put_user(tmp, datap); | ||
218 | if (ret) | ||
219 | break; | ||
220 | datap++; | ||
221 | } | ||
222 | break; | ||
223 | |||
224 | case PTRACE_SETREGS: /* Set all gp regs in the child. */ | ||
225 | for (i = 0; i < 19; i++) { | ||
226 | ret = get_user(tmp, datap); | ||
227 | if (ret) | ||
228 | break; | ||
229 | if (i == PT_SR) { | ||
230 | tmp &= SR_MASK; | ||
231 | tmp |= get_reg(child, PT_SR) & ~SR_MASK; | ||
232 | } | ||
233 | put_reg(child, i, tmp); | ||
234 | datap++; | ||
235 | } | ||
236 | break; | ||
237 | |||
238 | case PTRACE_GETFPREGS: /* Get the child FPU state. */ | ||
239 | if (copy_to_user(datap, &child->thread.fp, | ||
240 | sizeof(struct user_m68kfp_struct))) | ||
241 | ret = -EFAULT; | ||
242 | break; | ||
243 | |||
244 | case PTRACE_SETFPREGS: /* Set the child FPU state. */ | ||
245 | if (copy_from_user(&child->thread.fp, datap, | ||
246 | sizeof(struct user_m68kfp_struct))) | ||
247 | ret = -EFAULT; | ||
248 | break; | ||
249 | |||
250 | case PTRACE_GET_THREAD_AREA: | ||
251 | ret = put_user(task_thread_info(child)->tp_value, datap); | ||
252 | break; | ||
253 | |||
254 | default: | ||
255 | ret = ptrace_request(child, request, addr, data); | ||
256 | break; | ||
257 | } | ||
258 | |||
259 | return ret; | ||
260 | out_eio: | ||
261 | return -EIO; | ||
262 | } | ||
263 | |||
264 | asmlinkage void syscall_trace(void) | ||
265 | { | ||
266 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) | ||
267 | ? 0x80 : 0)); | ||
268 | /* | ||
269 | * this isn't the same as continuing with a signal, but it will do | ||
270 | * for normal use. strace only continues with a signal if the | ||
271 | * stopping signal is not SIGTRAP. -brl | ||
272 | */ | ||
273 | if (current->exit_code) { | ||
274 | send_sig(current->exit_code, current, 1); | ||
275 | current->exit_code = 0; | ||
276 | } | ||
277 | } | ||
diff --git a/arch/m68k/kernel/ptrace_mm.c b/arch/m68k/kernel/ptrace_mm.c new file mode 100644 index 000000000000..0b252683cefb --- /dev/null +++ b/arch/m68k/kernel/ptrace_mm.c | |||
@@ -0,0 +1,277 @@ | |||
1 | /* | ||
2 | * linux/arch/m68k/kernel/ptrace.c | ||
3 | * | ||
4 | * Copyright (C) 1994 by Hamish Macdonald | ||
5 | * Taken from linux/kernel/ptrace.c and modified for M680x0. | ||
6 | * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds | ||
7 | * | ||
8 | * This file is subject to the terms and conditions of the GNU General | ||
9 | * Public License. See the file COPYING in the main directory of | ||
10 | * this archive for more details. | ||
11 | */ | ||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/mm.h> | ||
16 | #include <linux/smp.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/ptrace.h> | ||
19 | #include <linux/user.h> | ||
20 | #include <linux/signal.h> | ||
21 | |||
22 | #include <asm/uaccess.h> | ||
23 | #include <asm/page.h> | ||
24 | #include <asm/pgtable.h> | ||
25 | #include <asm/system.h> | ||
26 | #include <asm/processor.h> | ||
27 | |||
28 | /* | ||
29 | * does not yet catch signals sent when the child dies. | ||
30 | * in exit.c or in signal.c. | ||
31 | */ | ||
32 | |||
33 | /* determines which bits in the SR the user has access to. */ | ||
34 | /* 1 = access 0 = no access */ | ||
35 | #define SR_MASK 0x001f | ||
36 | |||
37 | /* sets the trace bits. */ | ||
38 | #define TRACE_BITS 0xC000 | ||
39 | #define T1_BIT 0x8000 | ||
40 | #define T0_BIT 0x4000 | ||
41 | |||
42 | /* Find the stack offset for a register, relative to thread.esp0. */ | ||
43 | #define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg) | ||
44 | #define SW_REG(reg) ((long)&((struct switch_stack *)0)->reg \ | ||
45 | - sizeof(struct switch_stack)) | ||
46 | /* Mapping from PT_xxx to the stack offset at which the register is | ||
47 | saved. Notice that usp has no stack-slot and needs to be treated | ||
48 | specially (see get_reg/put_reg below). */ | ||
49 | static const int regoff[] = { | ||
50 | [0] = PT_REG(d1), | ||
51 | [1] = PT_REG(d2), | ||
52 | [2] = PT_REG(d3), | ||
53 | [3] = PT_REG(d4), | ||
54 | [4] = PT_REG(d5), | ||
55 | [5] = SW_REG(d6), | ||
56 | [6] = SW_REG(d7), | ||
57 | [7] = PT_REG(a0), | ||
58 | [8] = PT_REG(a1), | ||
59 | [9] = PT_REG(a2), | ||
60 | [10] = SW_REG(a3), | ||
61 | [11] = SW_REG(a4), | ||
62 | [12] = SW_REG(a5), | ||
63 | [13] = SW_REG(a6), | ||
64 | [14] = PT_REG(d0), | ||
65 | [15] = -1, | ||
66 | [16] = PT_REG(orig_d0), | ||
67 | [17] = PT_REG(sr), | ||
68 | [18] = PT_REG(pc), | ||
69 | }; | ||
70 | |||
71 | /* | ||
72 | * Get contents of register REGNO in task TASK. | ||
73 | */ | ||
74 | static inline long get_reg(struct task_struct *task, int regno) | ||
75 | { | ||
76 | unsigned long *addr; | ||
77 | |||
78 | if (regno == PT_USP) | ||
79 | addr = &task->thread.usp; | ||
80 | else if (regno < ARRAY_SIZE(regoff)) | ||
81 | addr = (unsigned long *)(task->thread.esp0 + regoff[regno]); | ||
82 | else | ||
83 | return 0; | ||
84 | /* Need to take stkadj into account. */ | ||
85 | if (regno == PT_SR || regno == PT_PC) { | ||
86 | long stkadj = *(long *)(task->thread.esp0 + PT_REG(stkadj)); | ||
87 | addr = (unsigned long *) ((unsigned long)addr + stkadj); | ||
88 | /* The sr is actually a 16 bit register. */ | ||
89 | if (regno == PT_SR) | ||
90 | return *(unsigned short *)addr; | ||
91 | } | ||
92 | return *addr; | ||
93 | } | ||
94 | |||
95 | /* | ||
96 | * Write contents of register REGNO in task TASK. | ||
97 | */ | ||
98 | static inline int put_reg(struct task_struct *task, int regno, | ||
99 | unsigned long data) | ||
100 | { | ||
101 | unsigned long *addr; | ||
102 | |||
103 | if (regno == PT_USP) | ||
104 | addr = &task->thread.usp; | ||
105 | else if (regno < ARRAY_SIZE(regoff)) | ||
106 | addr = (unsigned long *)(task->thread.esp0 + regoff[regno]); | ||
107 | else | ||
108 | return -1; | ||
109 | /* Need to take stkadj into account. */ | ||
110 | if (regno == PT_SR || regno == PT_PC) { | ||
111 | long stkadj = *(long *)(task->thread.esp0 + PT_REG(stkadj)); | ||
112 | addr = (unsigned long *) ((unsigned long)addr + stkadj); | ||
113 | /* The sr is actually a 16 bit register. */ | ||
114 | if (regno == PT_SR) { | ||
115 | *(unsigned short *)addr = data; | ||
116 | return 0; | ||
117 | } | ||
118 | } | ||
119 | *addr = data; | ||
120 | return 0; | ||
121 | } | ||
122 | |||
123 | /* | ||
124 | * Make sure the single step bit is not set. | ||
125 | */ | ||
126 | static inline void singlestep_disable(struct task_struct *child) | ||
127 | { | ||
128 | unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS; | ||
129 | put_reg(child, PT_SR, tmp); | ||
130 | clear_tsk_thread_flag(child, TIF_DELAYED_TRACE); | ||
131 | } | ||
132 | |||
133 | /* | ||
134 | * Called by kernel/ptrace.c when detaching.. | ||
135 | */ | ||
136 | void ptrace_disable(struct task_struct *child) | ||
137 | { | ||
138 | singlestep_disable(child); | ||
139 | } | ||
140 | |||
141 | void user_enable_single_step(struct task_struct *child) | ||
142 | { | ||
143 | unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS; | ||
144 | put_reg(child, PT_SR, tmp | T1_BIT); | ||
145 | set_tsk_thread_flag(child, TIF_DELAYED_TRACE); | ||
146 | } | ||
147 | |||
148 | void user_enable_block_step(struct task_struct *child) | ||
149 | { | ||
150 | unsigned long tmp = get_reg(child, PT_SR) & ~TRACE_BITS; | ||
151 | put_reg(child, PT_SR, tmp | T0_BIT); | ||
152 | } | ||
153 | |||
154 | void user_disable_single_step(struct task_struct *child) | ||
155 | { | ||
156 | singlestep_disable(child); | ||
157 | } | ||
158 | |||
159 | long arch_ptrace(struct task_struct *child, long request, | ||
160 | unsigned long addr, unsigned long data) | ||
161 | { | ||
162 | unsigned long tmp; | ||
163 | int i, ret = 0; | ||
164 | int regno = addr >> 2; /* temporary hack. */ | ||
165 | unsigned long __user *datap = (unsigned long __user *) data; | ||
166 | |||
167 | switch (request) { | ||
168 | /* read the word at location addr in the USER area. */ | ||
169 | case PTRACE_PEEKUSR: | ||
170 | if (addr & 3) | ||
171 | goto out_eio; | ||
172 | |||
173 | if (regno >= 0 && regno < 19) { | ||
174 | tmp = get_reg(child, regno); | ||
175 | } else if (regno >= 21 && regno < 49) { | ||
176 | tmp = child->thread.fp[regno - 21]; | ||
177 | /* Convert internal fpu reg representation | ||
178 | * into long double format | ||
179 | */ | ||
180 | if (FPU_IS_EMU && (regno < 45) && !(regno % 3)) | ||
181 | tmp = ((tmp & 0xffff0000) << 15) | | ||
182 | ((tmp & 0x0000ffff) << 16); | ||
183 | } else | ||
184 | goto out_eio; | ||
185 | ret = put_user(tmp, datap); | ||
186 | break; | ||
187 | |||
188 | case PTRACE_POKEUSR: | ||
189 | /* write the word at location addr in the USER area */ | ||
190 | if (addr & 3) | ||
191 | goto out_eio; | ||
192 | |||
193 | if (regno == PT_SR) { | ||
194 | data &= SR_MASK; | ||
195 | data |= get_reg(child, PT_SR) & ~SR_MASK; | ||
196 | } | ||
197 | if (regno >= 0 && regno < 19) { | ||
198 | if (put_reg(child, regno, data)) | ||
199 | goto out_eio; | ||
200 | } else if (regno >= 21 && regno < 48) { | ||
201 | /* Convert long double format | ||
202 | * into internal fpu reg representation | ||
203 | */ | ||
204 | if (FPU_IS_EMU && (regno < 45) && !(regno % 3)) { | ||
205 | data <<= 15; | ||
206 | data = (data & 0xffff0000) | | ||
207 | ((data & 0x0000ffff) >> 1); | ||
208 | } | ||
209 | child->thread.fp[regno - 21] = data; | ||
210 | } else | ||
211 | goto out_eio; | ||
212 | break; | ||
213 | |||
214 | case PTRACE_GETREGS: /* Get all gp regs from the child. */ | ||
215 | for (i = 0; i < 19; i++) { | ||
216 | tmp = get_reg(child, i); | ||
217 | ret = put_user(tmp, datap); | ||
218 | if (ret) | ||
219 | break; | ||
220 | datap++; | ||
221 | } | ||
222 | break; | ||
223 | |||
224 | case PTRACE_SETREGS: /* Set all gp regs in the child. */ | ||
225 | for (i = 0; i < 19; i++) { | ||
226 | ret = get_user(tmp, datap); | ||
227 | if (ret) | ||
228 | break; | ||
229 | if (i == PT_SR) { | ||
230 | tmp &= SR_MASK; | ||
231 | tmp |= get_reg(child, PT_SR) & ~SR_MASK; | ||
232 | } | ||
233 | put_reg(child, i, tmp); | ||
234 | datap++; | ||
235 | } | ||
236 | break; | ||
237 | |||
238 | case PTRACE_GETFPREGS: /* Get the child FPU state. */ | ||
239 | if (copy_to_user(datap, &child->thread.fp, | ||
240 | sizeof(struct user_m68kfp_struct))) | ||
241 | ret = -EFAULT; | ||
242 | break; | ||
243 | |||
244 | case PTRACE_SETFPREGS: /* Set the child FPU state. */ | ||
245 | if (copy_from_user(&child->thread.fp, datap, | ||
246 | sizeof(struct user_m68kfp_struct))) | ||
247 | ret = -EFAULT; | ||
248 | break; | ||
249 | |||
250 | case PTRACE_GET_THREAD_AREA: | ||
251 | ret = put_user(task_thread_info(child)->tp_value, datap); | ||
252 | break; | ||
253 | |||
254 | default: | ||
255 | ret = ptrace_request(child, request, addr, data); | ||
256 | break; | ||
257 | } | ||
258 | |||
259 | return ret; | ||
260 | out_eio: | ||
261 | return -EIO; | ||
262 | } | ||
263 | |||
264 | asmlinkage void syscall_trace(void) | ||
265 | { | ||
266 | ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD) | ||
267 | ? 0x80 : 0)); | ||
268 | /* | ||
269 | * this isn't the same as continuing with a signal, but it will do | ||
270 | * for normal use. strace only continues with a signal if the | ||
271 | * stopping signal is not SIGTRAP. -brl | ||
272 | */ | ||
273 | if (current->exit_code) { | ||
274 | send_sig(current->exit_code, current, 1); | ||
275 | current->exit_code = 0; | ||
276 | } | ||
277 | } | ||
diff --git a/arch/m68k/kernel/ptrace_no.c b/arch/m68k/kernel/ptrace_no.c new file mode 100644 index 000000000000..6709fb707335 --- /dev/null +++ b/arch/m68k/kernel/ptrace_no.c | |||
@@ -0,0 +1,255 @@ | |||
1 | /* | ||
2 | * linux/arch/m68knommu/kernel/ptrace.c | ||
3 | * | ||
4 | * Copyright (C) 1994 by Hamish Macdonald | ||
5 | * Taken from linux/kernel/ptrace.c and modified for M680x0. | ||
6 | * linux/kernel/ptrace.c is by Ross Biro 1/23/92, edited by Linus Torvalds | ||
7 | * | ||
8 | * This file is subject to the terms and conditions of the GNU General | ||
9 | * Public License. See the file COPYING in the main directory of | ||
10 | * this archive for more details. | ||
11 | */ | ||
12 | |||
13 | #include <linux/kernel.h> | ||
14 | #include <linux/sched.h> | ||
15 | #include <linux/mm.h> | ||
16 | #include <linux/smp.h> | ||
17 | #include <linux/errno.h> | ||
18 | #include <linux/ptrace.h> | ||
19 | #include <linux/user.h> | ||
20 | #include <linux/signal.h> | ||
21 | #include <linux/tracehook.h> | ||
22 | |||
23 | #include <asm/uaccess.h> | ||
24 | #include <asm/page.h> | ||
25 | #include <asm/pgtable.h> | ||
26 | #include <asm/system.h> | ||
27 | #include <asm/processor.h> | ||
28 | |||
29 | /* | ||
30 | * does not yet catch signals sent when the child dies. | ||
31 | * in exit.c or in signal.c. | ||
32 | */ | ||
33 | |||
34 | /* determines which bits in the SR the user has access to. */ | ||
35 | /* 1 = access 0 = no access */ | ||
36 | #define SR_MASK 0x001f | ||
37 | |||
38 | /* sets the trace bits. */ | ||
39 | #define TRACE_BITS 0x8000 | ||
40 | |||
41 | /* Find the stack offset for a register, relative to thread.esp0. */ | ||
42 | #define PT_REG(reg) ((long)&((struct pt_regs *)0)->reg) | ||
43 | #define SW_REG(reg) ((long)&((struct switch_stack *)0)->reg \ | ||
44 | - sizeof(struct switch_stack)) | ||
45 | /* Mapping from PT_xxx to the stack offset at which the register is | ||
46 | saved. Notice that usp has no stack-slot and needs to be treated | ||
47 | specially (see get_reg/put_reg below). */ | ||
48 | static int regoff[] = { | ||
49 | PT_REG(d1), PT_REG(d2), PT_REG(d3), PT_REG(d4), | ||
50 | PT_REG(d5), SW_REG(d6), SW_REG(d7), PT_REG(a0), | ||
51 | PT_REG(a1), PT_REG(a2), SW_REG(a3), SW_REG(a4), | ||
52 | SW_REG(a5), SW_REG(a6), PT_REG(d0), -1, | ||
53 | PT_REG(orig_d0), PT_REG(sr), PT_REG(pc), | ||
54 | }; | ||
55 | |||
56 | /* | ||
57 | * Get contents of register REGNO in task TASK. | ||
58 | */ | ||
59 | static inline long get_reg(struct task_struct *task, int regno) | ||
60 | { | ||
61 | unsigned long *addr; | ||
62 | |||
63 | if (regno == PT_USP) | ||
64 | addr = &task->thread.usp; | ||
65 | else if (regno < ARRAY_SIZE(regoff)) | ||
66 | addr = (unsigned long *)(task->thread.esp0 + regoff[regno]); | ||
67 | else | ||
68 | return 0; | ||
69 | return *addr; | ||
70 | } | ||
71 | |||
72 | /* | ||
73 | * Write contents of register REGNO in task TASK. | ||
74 | */ | ||
75 | static inline int put_reg(struct task_struct *task, int regno, | ||
76 | unsigned long data) | ||
77 | { | ||
78 | unsigned long *addr; | ||
79 | |||
80 | if (regno == PT_USP) | ||
81 | addr = &task->thread.usp; | ||
82 | else if (regno < ARRAY_SIZE(regoff)) | ||
83 | addr = (unsigned long *) (task->thread.esp0 + regoff[regno]); | ||
84 | else | ||
85 | return -1; | ||
86 | *addr = data; | ||
87 | return 0; | ||
88 | } | ||
89 | |||
90 | void user_enable_single_step(struct task_struct *task) | ||
91 | { | ||
92 | unsigned long srflags; | ||
93 | srflags = get_reg(task, PT_SR) | (TRACE_BITS << 16); | ||
94 | put_reg(task, PT_SR, srflags); | ||
95 | } | ||
96 | |||
97 | void user_disable_single_step(struct task_struct *task) | ||
98 | { | ||
99 | unsigned long srflags; | ||
100 | srflags = get_reg(task, PT_SR) & ~(TRACE_BITS << 16); | ||
101 | put_reg(task, PT_SR, srflags); | ||
102 | } | ||
103 | |||
104 | /* | ||
105 | * Called by kernel/ptrace.c when detaching.. | ||
106 | * | ||
107 | * Make sure the single step bit is not set. | ||
108 | */ | ||
109 | void ptrace_disable(struct task_struct *child) | ||
110 | { | ||
111 | /* make sure the single step bit is not set. */ | ||
112 | user_disable_single_step(child); | ||
113 | } | ||
114 | |||
115 | long arch_ptrace(struct task_struct *child, long request, | ||
116 | unsigned long addr, unsigned long data) | ||
117 | { | ||
118 | int ret; | ||
119 | int regno = addr >> 2; | ||
120 | unsigned long __user *datap = (unsigned long __user *) data; | ||
121 | |||
122 | switch (request) { | ||
123 | /* read the word at location addr in the USER area. */ | ||
124 | case PTRACE_PEEKUSR: { | ||
125 | unsigned long tmp; | ||
126 | |||
127 | ret = -EIO; | ||
128 | if ((addr & 3) || addr > sizeof(struct user) - 3) | ||
129 | break; | ||
130 | |||
131 | tmp = 0; /* Default return condition */ | ||
132 | ret = -EIO; | ||
133 | if (regno < 19) { | ||
134 | tmp = get_reg(child, regno); | ||
135 | if (regno == PT_SR) | ||
136 | tmp >>= 16; | ||
137 | } else if (regno >= 21 && regno < 49) { | ||
138 | tmp = child->thread.fp[regno - 21]; | ||
139 | } else if (regno == 49) { | ||
140 | tmp = child->mm->start_code; | ||
141 | } else if (regno == 50) { | ||
142 | tmp = child->mm->start_data; | ||
143 | } else if (regno == 51) { | ||
144 | tmp = child->mm->end_code; | ||
145 | } else | ||
146 | break; | ||
147 | ret = put_user(tmp, datap); | ||
148 | break; | ||
149 | } | ||
150 | |||
151 | case PTRACE_POKEUSR: /* write the word at location addr in the USER area */ | ||
152 | ret = -EIO; | ||
153 | if ((addr & 3) || addr > sizeof(struct user) - 3) | ||
154 | break; | ||
155 | |||
156 | if (regno == PT_SR) { | ||
157 | data &= SR_MASK; | ||
158 | data <<= 16; | ||
159 | data |= get_reg(child, PT_SR) & ~(SR_MASK << 16); | ||
160 | } | ||
161 | if (regno < 19) { | ||
162 | if (put_reg(child, regno, data)) | ||
163 | break; | ||
164 | ret = 0; | ||
165 | break; | ||
166 | } | ||
167 | if (regno >= 21 && regno < 48) | ||
168 | { | ||
169 | child->thread.fp[regno - 21] = data; | ||
170 | ret = 0; | ||
171 | } | ||
172 | break; | ||
173 | |||
174 | case PTRACE_GETREGS: { /* Get all gp regs from the child. */ | ||
175 | int i; | ||
176 | unsigned long tmp; | ||
177 | for (i = 0; i < 19; i++) { | ||
178 | tmp = get_reg(child, i); | ||
179 | if (i == PT_SR) | ||
180 | tmp >>= 16; | ||
181 | if (put_user(tmp, datap)) { | ||
182 | ret = -EFAULT; | ||
183 | break; | ||
184 | } | ||
185 | datap++; | ||
186 | } | ||
187 | ret = 0; | ||
188 | break; | ||
189 | } | ||
190 | |||
191 | case PTRACE_SETREGS: { /* Set all gp regs in the child. */ | ||
192 | int i; | ||
193 | unsigned long tmp; | ||
194 | for (i = 0; i < 19; i++) { | ||
195 | if (get_user(tmp, datap)) { | ||
196 | ret = -EFAULT; | ||
197 | break; | ||
198 | } | ||
199 | if (i == PT_SR) { | ||
200 | tmp &= SR_MASK; | ||
201 | tmp <<= 16; | ||
202 | tmp |= get_reg(child, PT_SR) & ~(SR_MASK << 16); | ||
203 | } | ||
204 | put_reg(child, i, tmp); | ||
205 | datap++; | ||
206 | } | ||
207 | ret = 0; | ||
208 | break; | ||
209 | } | ||
210 | |||
211 | #ifdef PTRACE_GETFPREGS | ||
212 | case PTRACE_GETFPREGS: { /* Get the child FPU state. */ | ||
213 | ret = 0; | ||
214 | if (copy_to_user(datap, &child->thread.fp, | ||
215 | sizeof(struct user_m68kfp_struct))) | ||
216 | ret = -EFAULT; | ||
217 | break; | ||
218 | } | ||
219 | #endif | ||
220 | |||
221 | #ifdef PTRACE_SETFPREGS | ||
222 | case PTRACE_SETFPREGS: { /* Set the child FPU state. */ | ||
223 | ret = 0; | ||
224 | if (copy_from_user(&child->thread.fp, datap, | ||
225 | sizeof(struct user_m68kfp_struct))) | ||
226 | ret = -EFAULT; | ||
227 | break; | ||
228 | } | ||
229 | #endif | ||
230 | |||
231 | case PTRACE_GET_THREAD_AREA: | ||
232 | ret = put_user(task_thread_info(child)->tp_value, datap); | ||
233 | break; | ||
234 | |||
235 | default: | ||
236 | ret = ptrace_request(child, request, addr, data); | ||
237 | break; | ||
238 | } | ||
239 | return ret; | ||
240 | } | ||
241 | |||
242 | asmlinkage int syscall_trace_enter(void) | ||
243 | { | ||
244 | int ret = 0; | ||
245 | |||
246 | if (test_thread_flag(TIF_SYSCALL_TRACE)) | ||
247 | ret = tracehook_report_syscall_entry(task_pt_regs(current)); | ||
248 | return ret; | ||
249 | } | ||
250 | |||
251 | asmlinkage void syscall_trace_leave(void) | ||
252 | { | ||
253 | if (test_thread_flag(TIF_SYSCALL_TRACE)) | ||
254 | tracehook_report_syscall_exit(task_pt_regs(current), 0); | ||
255 | } | ||
diff --git a/arch/m68k/kernel/setup.c b/arch/m68k/kernel/setup.c index 334d83640376..4bf129f1d2e2 100644 --- a/arch/m68k/kernel/setup.c +++ b/arch/m68k/kernel/setup.c | |||
@@ -1,533 +1,5 @@ | |||
1 | /* | 1 | #ifdef CONFIG_MMU |
2 | * linux/arch/m68k/kernel/setup.c | 2 | #include "setup_mm.c" |
3 | * | ||
4 | * Copyright (C) 1995 Hamish Macdonald | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * This file handles the architecture-dependent parts of system setup | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/fs.h> | ||
17 | #include <linux/console.h> | ||
18 | #include <linux/genhd.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/string.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/bootmem.h> | ||
23 | #include <linux/proc_fs.h> | ||
24 | #include <linux/seq_file.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/initrd.h> | ||
27 | |||
28 | #include <asm/bootinfo.h> | ||
29 | #include <asm/sections.h> | ||
30 | #include <asm/setup.h> | ||
31 | #include <asm/fpu.h> | ||
32 | #include <asm/irq.h> | ||
33 | #include <asm/io.h> | ||
34 | #include <asm/machdep.h> | ||
35 | #ifdef CONFIG_AMIGA | ||
36 | #include <asm/amigahw.h> | ||
37 | #endif | ||
38 | #ifdef CONFIG_ATARI | ||
39 | #include <asm/atarihw.h> | ||
40 | #include <asm/atari_stram.h> | ||
41 | #endif | ||
42 | #ifdef CONFIG_SUN3X | ||
43 | #include <asm/dvma.h> | ||
44 | #endif | ||
45 | #include <asm/natfeat.h> | ||
46 | |||
47 | #if !FPSTATESIZE || !NR_IRQS | ||
48 | #warning No CPU/platform type selected, your kernel will not work! | ||
49 | #warning Are you building an allnoconfig kernel? | ||
50 | #endif | ||
51 | |||
52 | unsigned long m68k_machtype; | ||
53 | EXPORT_SYMBOL(m68k_machtype); | ||
54 | unsigned long m68k_cputype; | ||
55 | EXPORT_SYMBOL(m68k_cputype); | ||
56 | unsigned long m68k_fputype; | ||
57 | unsigned long m68k_mmutype; | ||
58 | EXPORT_SYMBOL(m68k_mmutype); | ||
59 | #ifdef CONFIG_VME | ||
60 | unsigned long vme_brdtype; | ||
61 | EXPORT_SYMBOL(vme_brdtype); | ||
62 | #endif | ||
63 | |||
64 | int m68k_is040or060; | ||
65 | EXPORT_SYMBOL(m68k_is040or060); | ||
66 | |||
67 | extern unsigned long availmem; | ||
68 | |||
69 | int m68k_num_memory; | ||
70 | EXPORT_SYMBOL(m68k_num_memory); | ||
71 | int m68k_realnum_memory; | ||
72 | EXPORT_SYMBOL(m68k_realnum_memory); | ||
73 | unsigned long m68k_memoffset; | ||
74 | struct mem_info m68k_memory[NUM_MEMINFO]; | ||
75 | EXPORT_SYMBOL(m68k_memory); | ||
76 | |||
77 | struct mem_info m68k_ramdisk; | ||
78 | |||
79 | static char m68k_command_line[CL_SIZE]; | ||
80 | |||
81 | void (*mach_sched_init) (irq_handler_t handler) __initdata = NULL; | ||
82 | /* machine dependent irq functions */ | ||
83 | void (*mach_init_IRQ) (void) __initdata = NULL; | ||
84 | void (*mach_get_model) (char *model); | ||
85 | void (*mach_get_hardware_list) (struct seq_file *m); | ||
86 | /* machine dependent timer functions */ | ||
87 | unsigned long (*mach_gettimeoffset) (void); | ||
88 | int (*mach_hwclk) (int, struct rtc_time*); | ||
89 | EXPORT_SYMBOL(mach_hwclk); | ||
90 | int (*mach_set_clock_mmss) (unsigned long); | ||
91 | unsigned int (*mach_get_ss)(void); | ||
92 | int (*mach_get_rtc_pll)(struct rtc_pll_info *); | ||
93 | int (*mach_set_rtc_pll)(struct rtc_pll_info *); | ||
94 | EXPORT_SYMBOL(mach_get_ss); | ||
95 | EXPORT_SYMBOL(mach_get_rtc_pll); | ||
96 | EXPORT_SYMBOL(mach_set_rtc_pll); | ||
97 | void (*mach_reset)( void ); | ||
98 | void (*mach_halt)( void ); | ||
99 | void (*mach_power_off)( void ); | ||
100 | long mach_max_dma_address = 0x00ffffff; /* default set to the lower 16MB */ | ||
101 | #ifdef CONFIG_HEARTBEAT | ||
102 | void (*mach_heartbeat) (int); | ||
103 | EXPORT_SYMBOL(mach_heartbeat); | ||
104 | #endif | ||
105 | #ifdef CONFIG_M68K_L2_CACHE | ||
106 | void (*mach_l2_flush) (int); | ||
107 | #endif | ||
108 | #if defined(CONFIG_INPUT_M68K_BEEP) || defined(CONFIG_INPUT_M68K_BEEP_MODULE) | ||
109 | void (*mach_beep)(unsigned int, unsigned int); | ||
110 | EXPORT_SYMBOL(mach_beep); | ||
111 | #endif | ||
112 | #if defined(CONFIG_ISA) && defined(MULTI_ISA) | ||
113 | int isa_type; | ||
114 | int isa_sex; | ||
115 | EXPORT_SYMBOL(isa_type); | ||
116 | EXPORT_SYMBOL(isa_sex); | ||
117 | #endif | ||
118 | |||
119 | extern int amiga_parse_bootinfo(const struct bi_record *); | ||
120 | extern int atari_parse_bootinfo(const struct bi_record *); | ||
121 | extern int mac_parse_bootinfo(const struct bi_record *); | ||
122 | extern int q40_parse_bootinfo(const struct bi_record *); | ||
123 | extern int bvme6000_parse_bootinfo(const struct bi_record *); | ||
124 | extern int mvme16x_parse_bootinfo(const struct bi_record *); | ||
125 | extern int mvme147_parse_bootinfo(const struct bi_record *); | ||
126 | extern int hp300_parse_bootinfo(const struct bi_record *); | ||
127 | extern int apollo_parse_bootinfo(const struct bi_record *); | ||
128 | |||
129 | extern void config_amiga(void); | ||
130 | extern void config_atari(void); | ||
131 | extern void config_mac(void); | ||
132 | extern void config_sun3(void); | ||
133 | extern void config_apollo(void); | ||
134 | extern void config_mvme147(void); | ||
135 | extern void config_mvme16x(void); | ||
136 | extern void config_bvme6000(void); | ||
137 | extern void config_hp300(void); | ||
138 | extern void config_q40(void); | ||
139 | extern void config_sun3x(void); | ||
140 | |||
141 | #define MASK_256K 0xfffc0000 | ||
142 | |||
143 | extern void paging_init(void); | ||
144 | |||
145 | static void __init m68k_parse_bootinfo(const struct bi_record *record) | ||
146 | { | ||
147 | while (record->tag != BI_LAST) { | ||
148 | int unknown = 0; | ||
149 | const unsigned long *data = record->data; | ||
150 | |||
151 | switch (record->tag) { | ||
152 | case BI_MACHTYPE: | ||
153 | case BI_CPUTYPE: | ||
154 | case BI_FPUTYPE: | ||
155 | case BI_MMUTYPE: | ||
156 | /* Already set up by head.S */ | ||
157 | break; | ||
158 | |||
159 | case BI_MEMCHUNK: | ||
160 | if (m68k_num_memory < NUM_MEMINFO) { | ||
161 | m68k_memory[m68k_num_memory].addr = data[0]; | ||
162 | m68k_memory[m68k_num_memory].size = data[1]; | ||
163 | m68k_num_memory++; | ||
164 | } else | ||
165 | printk("m68k_parse_bootinfo: too many memory chunks\n"); | ||
166 | break; | ||
167 | |||
168 | case BI_RAMDISK: | ||
169 | m68k_ramdisk.addr = data[0]; | ||
170 | m68k_ramdisk.size = data[1]; | ||
171 | break; | ||
172 | |||
173 | case BI_COMMAND_LINE: | ||
174 | strlcpy(m68k_command_line, (const char *)data, | ||
175 | sizeof(m68k_command_line)); | ||
176 | break; | ||
177 | |||
178 | default: | ||
179 | if (MACH_IS_AMIGA) | ||
180 | unknown = amiga_parse_bootinfo(record); | ||
181 | else if (MACH_IS_ATARI) | ||
182 | unknown = atari_parse_bootinfo(record); | ||
183 | else if (MACH_IS_MAC) | ||
184 | unknown = mac_parse_bootinfo(record); | ||
185 | else if (MACH_IS_Q40) | ||
186 | unknown = q40_parse_bootinfo(record); | ||
187 | else if (MACH_IS_BVME6000) | ||
188 | unknown = bvme6000_parse_bootinfo(record); | ||
189 | else if (MACH_IS_MVME16x) | ||
190 | unknown = mvme16x_parse_bootinfo(record); | ||
191 | else if (MACH_IS_MVME147) | ||
192 | unknown = mvme147_parse_bootinfo(record); | ||
193 | else if (MACH_IS_HP300) | ||
194 | unknown = hp300_parse_bootinfo(record); | ||
195 | else if (MACH_IS_APOLLO) | ||
196 | unknown = apollo_parse_bootinfo(record); | ||
197 | else | ||
198 | unknown = 1; | ||
199 | } | ||
200 | if (unknown) | ||
201 | printk("m68k_parse_bootinfo: unknown tag 0x%04x ignored\n", | ||
202 | record->tag); | ||
203 | record = (struct bi_record *)((unsigned long)record + | ||
204 | record->size); | ||
205 | } | ||
206 | |||
207 | m68k_realnum_memory = m68k_num_memory; | ||
208 | #ifdef CONFIG_SINGLE_MEMORY_CHUNK | ||
209 | if (m68k_num_memory > 1) { | ||
210 | printk("Ignoring last %i chunks of physical memory\n", | ||
211 | (m68k_num_memory - 1)); | ||
212 | m68k_num_memory = 1; | ||
213 | } | ||
214 | #endif | ||
215 | } | ||
216 | |||
217 | void __init setup_arch(char **cmdline_p) | ||
218 | { | ||
219 | int i; | ||
220 | |||
221 | /* The bootinfo is located right after the kernel bss */ | ||
222 | m68k_parse_bootinfo((const struct bi_record *)_end); | ||
223 | |||
224 | if (CPU_IS_040) | ||
225 | m68k_is040or060 = 4; | ||
226 | else if (CPU_IS_060) | ||
227 | m68k_is040or060 = 6; | ||
228 | |||
229 | /* FIXME: m68k_fputype is passed in by Penguin booter, which can | ||
230 | * be confused by software FPU emulation. BEWARE. | ||
231 | * We should really do our own FPU check at startup. | ||
232 | * [what do we do with buggy 68LC040s? if we have problems | ||
233 | * with them, we should add a test to check_bugs() below] */ | ||
234 | #ifndef CONFIG_M68KFPU_EMU_ONLY | ||
235 | /* clear the fpu if we have one */ | ||
236 | if (m68k_fputype & (FPU_68881|FPU_68882|FPU_68040|FPU_68060)) { | ||
237 | volatile int zero = 0; | ||
238 | asm volatile ("frestore %0" : : "m" (zero)); | ||
239 | } | ||
240 | #endif | ||
241 | |||
242 | if (CPU_IS_060) { | ||
243 | u32 pcr; | ||
244 | |||
245 | asm (".chip 68060; movec %%pcr,%0; .chip 68k" | ||
246 | : "=d" (pcr)); | ||
247 | if (((pcr >> 8) & 0xff) <= 5) { | ||
248 | printk("Enabling workaround for errata I14\n"); | ||
249 | asm (".chip 68060; movec %0,%%pcr; .chip 68k" | ||
250 | : : "d" (pcr | 0x20)); | ||
251 | } | ||
252 | } | ||
253 | |||
254 | init_mm.start_code = PAGE_OFFSET; | ||
255 | init_mm.end_code = (unsigned long)_etext; | ||
256 | init_mm.end_data = (unsigned long)_edata; | ||
257 | init_mm.brk = (unsigned long)_end; | ||
258 | |||
259 | *cmdline_p = m68k_command_line; | ||
260 | memcpy(boot_command_line, *cmdline_p, CL_SIZE); | ||
261 | |||
262 | parse_early_param(); | ||
263 | |||
264 | #ifdef CONFIG_DUMMY_CONSOLE | ||
265 | conswitchp = &dummy_con; | ||
266 | #endif | ||
267 | |||
268 | switch (m68k_machtype) { | ||
269 | #ifdef CONFIG_AMIGA | ||
270 | case MACH_AMIGA: | ||
271 | config_amiga(); | ||
272 | break; | ||
273 | #endif | ||
274 | #ifdef CONFIG_ATARI | ||
275 | case MACH_ATARI: | ||
276 | config_atari(); | ||
277 | break; | ||
278 | #endif | ||
279 | #ifdef CONFIG_MAC | ||
280 | case MACH_MAC: | ||
281 | config_mac(); | ||
282 | break; | ||
283 | #endif | ||
284 | #ifdef CONFIG_SUN3 | ||
285 | case MACH_SUN3: | ||
286 | config_sun3(); | ||
287 | break; | ||
288 | #endif | ||
289 | #ifdef CONFIG_APOLLO | ||
290 | case MACH_APOLLO: | ||
291 | config_apollo(); | ||
292 | break; | ||
293 | #endif | ||
294 | #ifdef CONFIG_MVME147 | ||
295 | case MACH_MVME147: | ||
296 | config_mvme147(); | ||
297 | break; | ||
298 | #endif | ||
299 | #ifdef CONFIG_MVME16x | ||
300 | case MACH_MVME16x: | ||
301 | config_mvme16x(); | ||
302 | break; | ||
303 | #endif | ||
304 | #ifdef CONFIG_BVME6000 | ||
305 | case MACH_BVME6000: | ||
306 | config_bvme6000(); | ||
307 | break; | ||
308 | #endif | ||
309 | #ifdef CONFIG_HP300 | ||
310 | case MACH_HP300: | ||
311 | config_hp300(); | ||
312 | break; | ||
313 | #endif | ||
314 | #ifdef CONFIG_Q40 | ||
315 | case MACH_Q40: | ||
316 | config_q40(); | ||
317 | break; | ||
318 | #endif | ||
319 | #ifdef CONFIG_SUN3X | ||
320 | case MACH_SUN3X: | ||
321 | config_sun3x(); | ||
322 | break; | ||
323 | #endif | ||
324 | default: | ||
325 | panic("No configuration setup"); | ||
326 | } | ||
327 | |||
328 | #ifdef CONFIG_NATFEAT | ||
329 | nf_init(); | ||
330 | #endif | ||
331 | |||
332 | paging_init(); | ||
333 | |||
334 | #ifndef CONFIG_SUN3 | ||
335 | for (i = 1; i < m68k_num_memory; i++) | ||
336 | free_bootmem_node(NODE_DATA(i), m68k_memory[i].addr, | ||
337 | m68k_memory[i].size); | ||
338 | #ifdef CONFIG_BLK_DEV_INITRD | ||
339 | if (m68k_ramdisk.size) { | ||
340 | reserve_bootmem_node(__virt_to_node(phys_to_virt(m68k_ramdisk.addr)), | ||
341 | m68k_ramdisk.addr, m68k_ramdisk.size, | ||
342 | BOOTMEM_DEFAULT); | ||
343 | initrd_start = (unsigned long)phys_to_virt(m68k_ramdisk.addr); | ||
344 | initrd_end = initrd_start + m68k_ramdisk.size; | ||
345 | printk("initrd: %08lx - %08lx\n", initrd_start, initrd_end); | ||
346 | } | ||
347 | #endif | ||
348 | |||
349 | #ifdef CONFIG_ATARI | ||
350 | if (MACH_IS_ATARI) | ||
351 | atari_stram_reserve_pages((void *)availmem); | ||
352 | #endif | ||
353 | #ifdef CONFIG_SUN3X | ||
354 | if (MACH_IS_SUN3X) { | ||
355 | dvma_init(); | ||
356 | } | ||
357 | #endif | ||
358 | |||
359 | #endif /* !CONFIG_SUN3 */ | ||
360 | |||
361 | /* set ISA defs early as possible */ | ||
362 | #if defined(CONFIG_ISA) && defined(MULTI_ISA) | ||
363 | if (MACH_IS_Q40) { | ||
364 | isa_type = ISA_TYPE_Q40; | ||
365 | isa_sex = 0; | ||
366 | } | ||
367 | #ifdef CONFIG_AMIGA_PCMCIA | ||
368 | if (MACH_IS_AMIGA && AMIGAHW_PRESENT(PCMCIA)) { | ||
369 | isa_type = ISA_TYPE_AG; | ||
370 | isa_sex = 1; | ||
371 | } | ||
372 | #endif | ||
373 | #endif | ||
374 | } | ||
375 | |||
376 | static int show_cpuinfo(struct seq_file *m, void *v) | ||
377 | { | ||
378 | const char *cpu, *mmu, *fpu; | ||
379 | unsigned long clockfreq, clockfactor; | ||
380 | |||
381 | #define LOOP_CYCLES_68020 (8) | ||
382 | #define LOOP_CYCLES_68030 (8) | ||
383 | #define LOOP_CYCLES_68040 (3) | ||
384 | #define LOOP_CYCLES_68060 (1) | ||
385 | |||
386 | if (CPU_IS_020) { | ||
387 | cpu = "68020"; | ||
388 | clockfactor = LOOP_CYCLES_68020; | ||
389 | } else if (CPU_IS_030) { | ||
390 | cpu = "68030"; | ||
391 | clockfactor = LOOP_CYCLES_68030; | ||
392 | } else if (CPU_IS_040) { | ||
393 | cpu = "68040"; | ||
394 | clockfactor = LOOP_CYCLES_68040; | ||
395 | } else if (CPU_IS_060) { | ||
396 | cpu = "68060"; | ||
397 | clockfactor = LOOP_CYCLES_68060; | ||
398 | } else { | ||
399 | cpu = "680x0"; | ||
400 | clockfactor = 0; | ||
401 | } | ||
402 | |||
403 | #ifdef CONFIG_M68KFPU_EMU_ONLY | ||
404 | fpu = "none(soft float)"; | ||
405 | #else | 3 | #else |
406 | if (m68k_fputype & FPU_68881) | 4 | #include "setup_no.c" |
407 | fpu = "68881"; | ||
408 | else if (m68k_fputype & FPU_68882) | ||
409 | fpu = "68882"; | ||
410 | else if (m68k_fputype & FPU_68040) | ||
411 | fpu = "68040"; | ||
412 | else if (m68k_fputype & FPU_68060) | ||
413 | fpu = "68060"; | ||
414 | else if (m68k_fputype & FPU_SUNFPA) | ||
415 | fpu = "Sun FPA"; | ||
416 | else | ||
417 | fpu = "none"; | ||
418 | #endif | ||
419 | |||
420 | if (m68k_mmutype & MMU_68851) | ||
421 | mmu = "68851"; | ||
422 | else if (m68k_mmutype & MMU_68030) | ||
423 | mmu = "68030"; | ||
424 | else if (m68k_mmutype & MMU_68040) | ||
425 | mmu = "68040"; | ||
426 | else if (m68k_mmutype & MMU_68060) | ||
427 | mmu = "68060"; | ||
428 | else if (m68k_mmutype & MMU_SUN3) | ||
429 | mmu = "Sun-3"; | ||
430 | else if (m68k_mmutype & MMU_APOLLO) | ||
431 | mmu = "Apollo"; | ||
432 | else | ||
433 | mmu = "unknown"; | ||
434 | |||
435 | clockfreq = loops_per_jiffy * HZ * clockfactor; | ||
436 | |||
437 | seq_printf(m, "CPU:\t\t%s\n" | ||
438 | "MMU:\t\t%s\n" | ||
439 | "FPU:\t\t%s\n" | ||
440 | "Clocking:\t%lu.%1luMHz\n" | ||
441 | "BogoMips:\t%lu.%02lu\n" | ||
442 | "Calibration:\t%lu loops\n", | ||
443 | cpu, mmu, fpu, | ||
444 | clockfreq/1000000,(clockfreq/100000)%10, | ||
445 | loops_per_jiffy/(500000/HZ),(loops_per_jiffy/(5000/HZ))%100, | ||
446 | loops_per_jiffy); | ||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | static void *c_start(struct seq_file *m, loff_t *pos) | ||
451 | { | ||
452 | return *pos < 1 ? (void *)1 : NULL; | ||
453 | } | ||
454 | static void *c_next(struct seq_file *m, void *v, loff_t *pos) | ||
455 | { | ||
456 | ++*pos; | ||
457 | return NULL; | ||
458 | } | ||
459 | static void c_stop(struct seq_file *m, void *v) | ||
460 | { | ||
461 | } | ||
462 | const struct seq_operations cpuinfo_op = { | ||
463 | .start = c_start, | ||
464 | .next = c_next, | ||
465 | .stop = c_stop, | ||
466 | .show = show_cpuinfo, | ||
467 | }; | ||
468 | |||
469 | #ifdef CONFIG_PROC_HARDWARE | ||
470 | static int hardware_proc_show(struct seq_file *m, void *v) | ||
471 | { | ||
472 | char model[80]; | ||
473 | unsigned long mem; | ||
474 | int i; | ||
475 | |||
476 | if (mach_get_model) | ||
477 | mach_get_model(model); | ||
478 | else | ||
479 | strcpy(model, "Unknown m68k"); | ||
480 | |||
481 | seq_printf(m, "Model:\t\t%s\n", model); | ||
482 | for (mem = 0, i = 0; i < m68k_num_memory; i++) | ||
483 | mem += m68k_memory[i].size; | ||
484 | seq_printf(m, "System Memory:\t%ldK\n", mem >> 10); | ||
485 | |||
486 | if (mach_get_hardware_list) | ||
487 | mach_get_hardware_list(m); | ||
488 | |||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | static int hardware_proc_open(struct inode *inode, struct file *file) | ||
493 | { | ||
494 | return single_open(file, hardware_proc_show, NULL); | ||
495 | } | ||
496 | |||
497 | static const struct file_operations hardware_proc_fops = { | ||
498 | .open = hardware_proc_open, | ||
499 | .read = seq_read, | ||
500 | .llseek = seq_lseek, | ||
501 | .release = single_release, | ||
502 | }; | ||
503 | |||
504 | static int __init proc_hardware_init(void) | ||
505 | { | ||
506 | proc_create("hardware", 0, NULL, &hardware_proc_fops); | ||
507 | return 0; | ||
508 | } | ||
509 | module_init(proc_hardware_init); | ||
510 | #endif | 5 | #endif |
511 | |||
512 | void check_bugs(void) | ||
513 | { | ||
514 | #ifndef CONFIG_M68KFPU_EMU | ||
515 | if (m68k_fputype == 0) { | ||
516 | printk(KERN_EMERG "*** YOU DO NOT HAVE A FLOATING POINT UNIT, " | ||
517 | "WHICH IS REQUIRED BY LINUX/M68K ***\n"); | ||
518 | printk(KERN_EMERG "Upgrade your hardware or join the FPU " | ||
519 | "emulation project\n"); | ||
520 | panic("no FPU"); | ||
521 | } | ||
522 | #endif /* !CONFIG_M68KFPU_EMU */ | ||
523 | } | ||
524 | |||
525 | #ifdef CONFIG_ADB | ||
526 | static int __init adb_probe_sync_enable (char *str) { | ||
527 | extern int __adb_probe_sync; | ||
528 | __adb_probe_sync = 1; | ||
529 | return 1; | ||
530 | } | ||
531 | |||
532 | __setup("adb_sync", adb_probe_sync_enable); | ||
533 | #endif /* CONFIG_ADB */ | ||
diff --git a/arch/m68k/kernel/setup_mm.c b/arch/m68k/kernel/setup_mm.c new file mode 100644 index 000000000000..334d83640376 --- /dev/null +++ b/arch/m68k/kernel/setup_mm.c | |||
@@ -0,0 +1,533 @@ | |||
1 | /* | ||
2 | * linux/arch/m68k/kernel/setup.c | ||
3 | * | ||
4 | * Copyright (C) 1995 Hamish Macdonald | ||
5 | */ | ||
6 | |||
7 | /* | ||
8 | * This file handles the architecture-dependent parts of system setup | ||
9 | */ | ||
10 | |||
11 | #include <linux/kernel.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/sched.h> | ||
14 | #include <linux/delay.h> | ||
15 | #include <linux/interrupt.h> | ||
16 | #include <linux/fs.h> | ||
17 | #include <linux/console.h> | ||
18 | #include <linux/genhd.h> | ||
19 | #include <linux/errno.h> | ||
20 | #include <linux/string.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/bootmem.h> | ||
23 | #include <linux/proc_fs.h> | ||
24 | #include <linux/seq_file.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/initrd.h> | ||
27 | |||
28 | #include <asm/bootinfo.h> | ||
29 | #include <asm/sections.h> | ||
30 | #include <asm/setup.h> | ||
31 | #include <asm/fpu.h> | ||
32 | #include <asm/irq.h> | ||
33 | #include <asm/io.h> | ||
34 | #include <asm/machdep.h> | ||
35 | #ifdef CONFIG_AMIGA | ||
36 | #include <asm/amigahw.h> | ||
37 | #endif | ||
38 | #ifdef CONFIG_ATARI | ||
39 | #include <asm/atarihw.h> | ||
40 | #include <asm/atari_stram.h> | ||
41 | #endif | ||
42 | #ifdef CONFIG_SUN3X | ||
43 | #include <asm/dvma.h> | ||
44 | #endif | ||
45 | #include <asm/natfeat.h> | ||
46 | |||
47 | #if !FPSTATESIZE || !NR_IRQS | ||
48 | #warning No CPU/platform type selected, your kernel will not work! | ||
49 | #warning Are you building an allnoconfig kernel? | ||
50 | #endif | ||
51 | |||
52 | unsigned long m68k_machtype; | ||
53 | EXPORT_SYMBOL(m68k_machtype); | ||
54 | unsigned long m68k_cputype; | ||
55 | EXPORT_SYMBOL(m68k_cputype); | ||
56 | unsigned long m68k_fputype; | ||
57 | unsigned long m68k_mmutype; | ||
58 | EXPORT_SYMBOL(m68k_mmutype); | ||
59 | #ifdef CONFIG_VME | ||
60 | unsigned long vme_brdtype; | ||
61 | EXPORT_SYMBOL(vme_brdtype); | ||
62 | #endif | ||
63 | |||
64 | int m68k_is040or060; | ||
65 | EXPORT_SYMBOL(m68k_is040or060); | ||
66 | |||
67 | extern unsigned long availmem; | ||
68 | |||
69 | int m68k_num_memory; | ||
70 | EXPORT_SYMBOL(m68k_num_memory); | ||
71 | int m68k_realnum_memory; | ||
72 | EXPORT_SYMBOL(m68k_realnum_memory); | ||
73 | unsigned long m68k_memoffset; | ||
74 | struct mem_info m68k_memory[NUM_MEMINFO]; | ||
75 | EXPORT_SYMBOL(m68k_memory); | ||
76 | |||
77 | struct mem_info m68k_ramdisk; | ||
78 | |||
79 | static char m68k_command_line[CL_SIZE]; | ||
80 | |||
81 | void (*mach_sched_init) (irq_handler_t handler) __initdata = NULL; | ||
82 | /* machine dependent irq functions */ | ||
83 | void (*mach_init_IRQ) (void) __initdata = NULL; | ||
84 | void (*mach_get_model) (char *model); | ||
85 | void (*mach_get_hardware_list) (struct seq_file *m); | ||
86 | /* machine dependent timer functions */ | ||
87 | unsigned long (*mach_gettimeoffset) (void); | ||
88 | int (*mach_hwclk) (int, struct rtc_time*); | ||
89 | EXPORT_SYMBOL(mach_hwclk); | ||
90 | int (*mach_set_clock_mmss) (unsigned long); | ||
91 | unsigned int (*mach_get_ss)(void); | ||
92 | int (*mach_get_rtc_pll)(struct rtc_pll_info *); | ||
93 | int (*mach_set_rtc_pll)(struct rtc_pll_info *); | ||
94 | EXPORT_SYMBOL(mach_get_ss); | ||
95 | EXPORT_SYMBOL(mach_get_rtc_pll); | ||
96 | EXPORT_SYMBOL(mach_set_rtc_pll); | ||
97 | void (*mach_reset)( void ); | ||
98 | void (*mach_halt)( void ); | ||
99 | void (*mach_power_off)( void ); | ||
100 | long mach_max_dma_address = 0x00ffffff; /* default set to the lower 16MB */ | ||
101 | #ifdef CONFIG_HEARTBEAT | ||
102 | void (*mach_heartbeat) (int); | ||
103 | EXPORT_SYMBOL(mach_heartbeat); | ||
104 | #endif | ||
105 | #ifdef CONFIG_M68K_L2_CACHE | ||
106 | void (*mach_l2_flush) (int); | ||
107 | #endif | ||
108 | #if defined(CONFIG_INPUT_M68K_BEEP) || defined(CONFIG_INPUT_M68K_BEEP_MODULE) | ||
109 | void (*mach_beep)(unsigned int, unsigned int); | ||
110 | EXPORT_SYMBOL(mach_beep); | ||
111 | #endif | ||
112 | #if defined(CONFIG_ISA) && defined(MULTI_ISA) | ||
113 | int isa_type; | ||
114 | int isa_sex; | ||
115 | EXPORT_SYMBOL(isa_type); | ||
116 | EXPORT_SYMBOL(isa_sex); | ||
117 | #endif | ||
118 | |||
119 | extern int amiga_parse_bootinfo(const struct bi_record *); | ||
120 | extern int atari_parse_bootinfo(const struct bi_record *); | ||
121 | extern int mac_parse_bootinfo(const struct bi_record *); | ||
122 | extern int q40_parse_bootinfo(const struct bi_record *); | ||
123 | extern int bvme6000_parse_bootinfo(const struct bi_record *); | ||
124 | extern int mvme16x_parse_bootinfo(const struct bi_record *); | ||
125 | extern int mvme147_parse_bootinfo(const struct bi_record *); | ||
126 | extern int hp300_parse_bootinfo(const struct bi_record *); | ||
127 | extern int apollo_parse_bootinfo(const struct bi_record *); | ||
128 | |||
129 | extern void config_amiga(void); | ||
130 | extern void config_atari(void); | ||
131 | extern void config_mac(void); | ||
132 | extern void config_sun3(void); | ||
133 | extern void config_apollo(void); | ||
134 | extern void config_mvme147(void); | ||
135 | extern void config_mvme16x(void); | ||
136 | extern void config_bvme6000(void); | ||
137 | extern void config_hp300(void); | ||
138 | extern void config_q40(void); | ||
139 | extern void config_sun3x(void); | ||
140 | |||
141 | #define MASK_256K 0xfffc0000 | ||
142 | |||
143 | extern void paging_init(void); | ||
144 | |||
145 | static void __init m68k_parse_bootinfo(const struct bi_record *record) | ||
146 | { | ||
147 | while (record->tag != BI_LAST) { | ||
148 | int unknown = 0; | ||
149 | const unsigned long *data = record->data; | ||
150 | |||
151 | switch (record->tag) { | ||
152 | case BI_MACHTYPE: | ||
153 | case BI_CPUTYPE: | ||
154 | case BI_FPUTYPE: | ||
155 | case BI_MMUTYPE: | ||
156 | /* Already set up by head.S */ | ||
157 | break; | ||
158 | |||
159 | case BI_MEMCHUNK: | ||
160 | if (m68k_num_memory < NUM_MEMINFO) { | ||
161 | m68k_memory[m68k_num_memory].addr = data[0]; | ||
162 | m68k_memory[m68k_num_memory].size = data[1]; | ||
163 | m68k_num_memory++; | ||
164 | } else | ||
165 | printk("m68k_parse_bootinfo: too many memory chunks\n"); | ||
166 | break; | ||
167 | |||
168 | case BI_RAMDISK: | ||
169 | m68k_ramdisk.addr = data[0]; | ||
170 | m68k_ramdisk.size = data[1]; | ||
171 | break; | ||
172 | |||
173 | case BI_COMMAND_LINE: | ||
174 | strlcpy(m68k_command_line, (const char *)data, | ||
175 | sizeof(m68k_command_line)); | ||
176 | break; | ||
177 | |||
178 | default: | ||
179 | if (MACH_IS_AMIGA) | ||
180 | unknown = amiga_parse_bootinfo(record); | ||
181 | else if (MACH_IS_ATARI) | ||
182 | unknown = atari_parse_bootinfo(record); | ||
183 | else if (MACH_IS_MAC) | ||
184 | unknown = mac_parse_bootinfo(record); | ||
185 | else if (MACH_IS_Q40) | ||
186 | unknown = q40_parse_bootinfo(record); | ||
187 | else if (MACH_IS_BVME6000) | ||
188 | unknown = bvme6000_parse_bootinfo(record); | ||
189 | else if (MACH_IS_MVME16x) | ||
190 | unknown = mvme16x_parse_bootinfo(record); | ||
191 | else if (MACH_IS_MVME147) | ||
192 | unknown = mvme147_parse_bootinfo(record); | ||
193 | else if (MACH_IS_HP300) | ||
194 | unknown = hp300_parse_bootinfo(record); | ||
195 | else if (MACH_IS_APOLLO) | ||
196 | unknown = apollo_parse_bootinfo(record); | ||
197 | else | ||
198 | unknown = 1; | ||
199 | } | ||
200 | if (unknown) | ||
201 | printk("m68k_parse_bootinfo: unknown tag 0x%04x ignored\n", | ||
202 | record->tag); | ||
203 | record = (struct bi_record *)((unsigned long)record + | ||
204 | record->size); | ||
205 | } | ||
206 | |||
207 | m68k_realnum_memory = m68k_num_memory; | ||
208 | #ifdef CONFIG_SINGLE_MEMORY_CHUNK | ||
209 | if (m68k_num_memory > 1) { | ||
210 | printk("Ignoring last %i chunks of physical memory\n", | ||
211 | (m68k_num_memory - 1)); | ||
212 | m68k_num_memory = 1; | ||
213 | } | ||
214 | #endif | ||
215 | } | ||
216 | |||
217 | void __init setup_arch(char **cmdline_p) | ||
218 | { | ||
219 | int i; | ||
220 | |||
221 | /* The bootinfo is located right after the kernel bss */ | ||
222 | m68k_parse_bootinfo((const struct bi_record *)_end); | ||
223 | |||
224 | if (CPU_IS_040) | ||
225 | m68k_is040or060 = 4; | ||
226 | else if (CPU_IS_060) | ||
227 | m68k_is040or060 = 6; | ||
228 | |||
229 | /* FIXME: m68k_fputype is passed in by Penguin booter, which can | ||
230 | * be confused by software FPU emulation. BEWARE. | ||
231 | * We should really do our own FPU check at startup. | ||
232 | * [what do we do with buggy 68LC040s? if we have problems | ||
233 | * with them, we should add a test to check_bugs() below] */ | ||
234 | #ifndef CONFIG_M68KFPU_EMU_ONLY | ||
235 | /* clear the fpu if we have one */ | ||
236 | if (m68k_fputype & (FPU_68881|FPU_68882|FPU_68040|FPU_68060)) { | ||
237 | volatile int zero = 0; | ||
238 | asm volatile ("frestore %0" : : "m" (zero)); | ||
239 | } | ||
240 | #endif | ||
241 | |||
242 | if (CPU_IS_060) { | ||
243 | u32 pcr; | ||
244 | |||
245 | asm (".chip 68060; movec %%pcr,%0; .chip 68k" | ||
246 | : "=d" (pcr)); | ||
247 | if (((pcr >> 8) & 0xff) <= 5) { | ||
248 | printk("Enabling workaround for errata I14\n"); | ||
249 | asm (".chip 68060; movec %0,%%pcr; .chip 68k" | ||
250 | : : "d" (pcr | 0x20)); | ||
251 | } | ||
252 | } | ||
253 | |||
254 | init_mm.start_code = PAGE_OFFSET; | ||
255 | init_mm.end_code = (unsigned long)_etext; | ||
256 | init_mm.end_data = (unsigned long)_edata; | ||
257 | init_mm.brk = (unsigned long)_end; | ||
258 | |||
259 | *cmdline_p = m68k_command_line; | ||
260 | memcpy(boot_command_line, *cmdline_p, CL_SIZE); | ||
261 | |||
262 | parse_early_param(); | ||
263 | |||
264 | #ifdef CONFIG_DUMMY_CONSOLE | ||
265 | conswitchp = &dummy_con; | ||
266 | #endif | ||
267 | |||
268 | switch (m68k_machtype) { | ||
269 | #ifdef CONFIG_AMIGA | ||
270 | case MACH_AMIGA: | ||
271 | config_amiga(); | ||
272 | break; | ||
273 | #endif | ||
274 | #ifdef CONFIG_ATARI | ||
275 | case MACH_ATARI: | ||
276 | config_atari(); | ||
277 | break; | ||
278 | #endif | ||
279 | #ifdef CONFIG_MAC | ||
280 | case MACH_MAC: | ||
281 | config_mac(); | ||
282 | break; | ||
283 | #endif | ||
284 | #ifdef CONFIG_SUN3 | ||
285 | case MACH_SUN3: | ||
286 | config_sun3(); | ||
287 | break; | ||
288 | #endif | ||
289 | #ifdef CONFIG_APOLLO | ||
290 | case MACH_APOLLO: | ||
291 | config_apollo(); | ||
292 | break; | ||
293 | #endif | ||
294 | #ifdef CONFIG_MVME147 | ||
295 | case MACH_MVME147: | ||
296 | config_mvme147(); | ||
297 | break; | ||
298 | #endif | ||
299 | #ifdef CONFIG_MVME16x | ||
300 | case MACH_MVME16x: | ||
301 | config_mvme16x(); | ||
302 | break; | ||
303 | #endif | ||
304 | #ifdef CONFIG_BVME6000 | ||
305 | case MACH_BVME6000: | ||
306 | config_bvme6000(); | ||
307 | break; | ||
308 | #endif | ||
309 | #ifdef CONFIG_HP300 | ||
310 | case MACH_HP300: | ||
311 | config_hp300(); | ||
312 | break; | ||
313 | #endif | ||
314 | #ifdef CONFIG_Q40 | ||
315 | case MACH_Q40: | ||
316 | config_q40(); | ||
317 | break; | ||
318 | #endif | ||
319 | #ifdef CONFIG_SUN3X | ||
320 | case MACH_SUN3X: | ||
321 | config_sun3x(); | ||
322 | break; | ||
323 | #endif | ||
324 | default: | ||
325 | panic("No configuration setup"); | ||
326 | } | ||
327 | |||
328 | #ifdef CONFIG_NATFEAT | ||
329 | nf_init(); | ||
330 | #endif | ||
331 | |||
332 | paging_init(); | ||
333 | |||
334 | #ifndef CONFIG_SUN3 | ||
335 | for (i = 1; i < m68k_num_memory; i++) | ||
336 | free_bootmem_node(NODE_DATA(i), m68k_memory[i].addr, | ||
337 | m68k_memory[i].size); | ||
338 | #ifdef CONFIG_BLK_DEV_INITRD | ||
339 | if (m68k_ramdisk.size) { | ||
340 | reserve_bootmem_node(__virt_to_node(phys_to_virt(m68k_ramdisk.addr)), | ||
341 | m68k_ramdisk.addr, m68k_ramdisk.size, | ||
342 | BOOTMEM_DEFAULT); | ||
343 | initrd_start = (unsigned long)phys_to_virt(m68k_ramdisk.addr); | ||
344 | initrd_end = initrd_start + m68k_ramdisk.size; | ||
345 | printk("initrd: %08lx - %08lx\n", initrd_start, initrd_end); | ||
346 | } | ||
347 | #endif | ||
348 | |||
349 | #ifdef CONFIG_ATARI | ||
350 | if (MACH_IS_ATARI) | ||
351 | atari_stram_reserve_pages((void *)availmem); | ||
352 | #endif | ||
353 | #ifdef CONFIG_SUN3X | ||
354 | if (MACH_IS_SUN3X) { | ||
355 | dvma_init(); | ||
356 | } | ||
357 | #endif | ||
358 | |||
359 | #endif /* !CONFIG_SUN3 */ | ||
360 | |||
361 | /* set ISA defs early as possible */ | ||
362 | #if defined(CONFIG_ISA) && defined(MULTI_ISA) | ||
363 | if (MACH_IS_Q40) { | ||
364 | isa_type = ISA_TYPE_Q40; | ||
365 | isa_sex = 0; | ||
366 | } | ||
367 | #ifdef CONFIG_AMIGA_PCMCIA | ||
368 | if (MACH_IS_AMIGA && AMIGAHW_PRESENT(PCMCIA)) { | ||
369 | isa_type = ISA_TYPE_AG; | ||
370 | isa_sex = 1; | ||
371 | } | ||
372 | #endif | ||
373 | #endif | ||
374 | } | ||
375 | |||
376 | static int show_cpuinfo(struct seq_file *m, void *v) | ||
377 | { | ||
378 | const char *cpu, *mmu, *fpu; | ||
379 | unsigned long clockfreq, clockfactor; | ||
380 | |||
381 | #define LOOP_CYCLES_68020 (8) | ||
382 | #define LOOP_CYCLES_68030 (8) | ||
383 | #define LOOP_CYCLES_68040 (3) | ||
384 | #define LOOP_CYCLES_68060 (1) | ||
385 | |||
386 | if (CPU_IS_020) { | ||
387 | cpu = "68020"; | ||
388 | clockfactor = LOOP_CYCLES_68020; | ||
389 | } else if (CPU_IS_030) { | ||
390 | cpu = "68030"; | ||
391 | clockfactor = LOOP_CYCLES_68030; | ||
392 | } else if (CPU_IS_040) { | ||
393 | cpu = "68040"; | ||
394 | clockfactor = LOOP_CYCLES_68040; | ||
395 | } else if (CPU_IS_060) { | ||
396 | cpu = "68060"; | ||
397 | clockfactor = LOOP_CYCLES_68060; | ||
398 | } else { | ||
399 | cpu = "680x0"; | ||
400 | clockfactor = 0; | ||
401 | } | ||
402 | |||
403 | #ifdef CONFIG_M68KFPU_EMU_ONLY | ||
404 | fpu = "none(soft float)"; | ||
405 | #else | ||
406 | if (m68k_fputype & FPU_68881) | ||
407 | fpu = "68881"; | ||
408 | else if (m68k_fputype & FPU_68882) | ||
409 | fpu = "68882"; | ||
410 | else if (m68k_fputype & FPU_68040) | ||
411 | fpu = "68040"; | ||
412 | else if (m68k_fputype & FPU_68060) | ||
413 | fpu = "68060"; | ||
414 | else if (m68k_fputype & FPU_SUNFPA) | ||
415 | fpu = "Sun FPA"; | ||
416 | else | ||
417 | fpu = "none"; | ||
418 | #endif | ||
419 | |||
420 | if (m68k_mmutype & MMU_68851) | ||
421 | mmu = "68851"; | ||
422 | else if (m68k_mmutype & MMU_68030) | ||
423 | mmu = "68030"; | ||
424 | else if (m68k_mmutype & MMU_68040) | ||
425 | mmu = "68040"; | ||
426 | else if (m68k_mmutype & MMU_68060) | ||
427 | mmu = "68060"; | ||
428 | else if (m68k_mmutype & MMU_SUN3) | ||
429 | mmu = "Sun-3"; | ||
430 | else if (m68k_mmutype & MMU_APOLLO) | ||
431 | mmu = "Apollo"; | ||
432 | else | ||
433 | mmu = "unknown"; | ||
434 | |||
435 | clockfreq = loops_per_jiffy * HZ * clockfactor; | ||
436 | |||
437 | seq_printf(m, "CPU:\t\t%s\n" | ||
438 | "MMU:\t\t%s\n" | ||
439 | "FPU:\t\t%s\n" | ||
440 | "Clocking:\t%lu.%1luMHz\n" | ||
441 | "BogoMips:\t%lu.%02lu\n" | ||
442 | "Calibration:\t%lu loops\n", | ||
443 | cpu, mmu, fpu, | ||
444 | clockfreq/1000000,(clockfreq/100000)%10, | ||
445 | loops_per_jiffy/(500000/HZ),(loops_per_jiffy/(5000/HZ))%100, | ||
446 | loops_per_jiffy); | ||
447 | return 0; | ||
448 | } | ||
449 | |||
450 | static void *c_start(struct seq_file *m, loff_t *pos) | ||
451 | { | ||
452 | return *pos < 1 ? (void *)1 : NULL; | ||
453 | } | ||
454 | static void *c_next(struct seq_file *m, void *v, loff_t *pos) | ||
455 | { | ||
456 | ++*pos; | ||
457 | return NULL; | ||
458 | } | ||
459 | static void c_stop(struct seq_file *m, void *v) | ||
460 | { | ||
461 | } | ||
462 | const struct seq_operations cpuinfo_op = { | ||
463 | .start = c_start, | ||
464 | .next = c_next, | ||
465 | .stop = c_stop, | ||
466 | .show = show_cpuinfo, | ||
467 | }; | ||
468 | |||
469 | #ifdef CONFIG_PROC_HARDWARE | ||
470 | static int hardware_proc_show(struct seq_file *m, void *v) | ||
471 | { | ||
472 | char model[80]; | ||
473 | unsigned long mem; | ||
474 | int i; | ||
475 | |||
476 | if (mach_get_model) | ||
477 | mach_get_model(model); | ||
478 | else | ||
479 | strcpy(model, "Unknown m68k"); | ||
480 | |||
481 | seq_printf(m, "Model:\t\t%s\n", model); | ||
482 | for (mem = 0, i = 0; i < m68k_num_memory; i++) | ||
483 | mem += m68k_memory[i].size; | ||
484 | seq_printf(m, "System Memory:\t%ldK\n", mem >> 10); | ||
485 | |||
486 | if (mach_get_hardware_list) | ||
487 | mach_get_hardware_list(m); | ||
488 | |||
489 | return 0; | ||
490 | } | ||
491 | |||
492 | static int hardware_proc_open(struct inode *inode, struct file *file) | ||
493 | { | ||
494 | return single_open(file, hardware_proc_show, NULL); | ||
495 | } | ||
496 | |||
497 | static const struct file_operations hardware_proc_fops = { | ||
498 | .open = hardware_proc_open, | ||
499 | .read = seq_read, | ||
500 | .llseek = seq_lseek, | ||
501 | .release = single_release, | ||
502 | }; | ||
503 | |||
504 | static int __init proc_hardware_init(void) | ||
505 | { | ||
506 | proc_create("hardware", 0, NULL, &hardware_proc_fops); | ||
507 | return 0; | ||
508 | } | ||
509 | module_init(proc_hardware_init); | ||
510 | #endif | ||
511 | |||
512 | void check_bugs(void) | ||
513 | { | ||
514 | #ifndef CONFIG_M68KFPU_EMU | ||
515 | if (m68k_fputype == 0) { | ||
516 | printk(KERN_EMERG "*** YOU DO NOT HAVE A FLOATING POINT UNIT, " | ||
517 | "WHICH IS REQUIRED BY LINUX/M68K ***\n"); | ||
518 | printk(KERN_EMERG "Upgrade your hardware or join the FPU " | ||
519 | "emulation project\n"); | ||
520 | panic("no FPU"); | ||
521 | } | ||
522 | #endif /* !CONFIG_M68KFPU_EMU */ | ||
523 | } | ||
524 | |||
525 | #ifdef CONFIG_ADB | ||
526 | static int __init adb_probe_sync_enable (char *str) { | ||
527 | extern int __adb_probe_sync; | ||
528 | __adb_probe_sync = 1; | ||
529 | return 1; | ||
530 | } | ||
531 | |||
532 | __setup("adb_sync", adb_probe_sync_enable); | ||
533 | #endif /* CONFIG_ADB */ | ||
diff --git a/arch/m68k/kernel/setup_no.c b/arch/m68k/kernel/setup_no.c new file mode 100644 index 000000000000..16b2de7f5101 --- /dev/null +++ b/arch/m68k/kernel/setup_no.c | |||
@@ -0,0 +1,317 @@ | |||
1 | /* | ||
2 | * linux/arch/m68knommu/kernel/setup.c | ||
3 | * | ||
4 | * Copyright (C) 1999-2007 Greg Ungerer (gerg@snapgear.com) | ||
5 | * Copyright (C) 1998,1999 D. Jeff Dionne <jeff@uClinux.org> | ||
6 | * Copyleft ()) 2000 James D. Schettine {james@telos-systems.com} | ||
7 | * Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com> | ||
8 | * Copyright (C) 1995 Hamish Macdonald | ||
9 | * Copyright (C) 2000 Lineo Inc. (www.lineo.com) | ||
10 | * Copyright (C) 2001 Lineo, Inc. <www.lineo.com> | ||
11 | * | ||
12 | * 68VZ328 Fixes/support Evan Stawnyczy <e@lineo.ca> | ||
13 | */ | ||
14 | |||
15 | /* | ||
16 | * This file handles the architecture-dependent parts of system setup | ||
17 | */ | ||
18 | |||
19 | #include <linux/kernel.h> | ||
20 | #include <linux/sched.h> | ||
21 | #include <linux/delay.h> | ||
22 | #include <linux/interrupt.h> | ||
23 | #include <linux/fb.h> | ||
24 | #include <linux/module.h> | ||
25 | #include <linux/mm.h> | ||
26 | #include <linux/console.h> | ||
27 | #include <linux/errno.h> | ||
28 | #include <linux/string.h> | ||
29 | #include <linux/bootmem.h> | ||
30 | #include <linux/seq_file.h> | ||
31 | #include <linux/init.h> | ||
32 | #include <linux/initrd.h> | ||
33 | #include <linux/root_dev.h> | ||
34 | |||
35 | #include <asm/setup.h> | ||
36 | #include <asm/irq.h> | ||
37 | #include <asm/machdep.h> | ||
38 | #include <asm/pgtable.h> | ||
39 | |||
40 | unsigned long memory_start; | ||
41 | unsigned long memory_end; | ||
42 | |||
43 | EXPORT_SYMBOL(memory_start); | ||
44 | EXPORT_SYMBOL(memory_end); | ||
45 | |||
46 | char __initdata command_line[COMMAND_LINE_SIZE]; | ||
47 | |||
48 | /* machine dependent timer functions */ | ||
49 | void (*mach_gettod)(int*, int*, int*, int*, int*, int*); | ||
50 | int (*mach_set_clock_mmss)(unsigned long); | ||
51 | |||
52 | /* machine dependent reboot functions */ | ||
53 | void (*mach_reset)(void); | ||
54 | void (*mach_halt)(void); | ||
55 | void (*mach_power_off)(void); | ||
56 | |||
57 | #ifdef CONFIG_M68328 | ||
58 | #define CPU_NAME "MC68328" | ||
59 | #endif | ||
60 | #ifdef CONFIG_M68EZ328 | ||
61 | #define CPU_NAME "MC68EZ328" | ||
62 | #endif | ||
63 | #ifdef CONFIG_M68VZ328 | ||
64 | #define CPU_NAME "MC68VZ328" | ||
65 | #endif | ||
66 | #ifdef CONFIG_M68360 | ||
67 | #define CPU_NAME "MC68360" | ||
68 | #endif | ||
69 | #ifndef CPU_NAME | ||
70 | #define CPU_NAME "UNKNOWN" | ||
71 | #endif | ||
72 | |||
73 | /* | ||
74 | * Different cores have different instruction execution timings. | ||
75 | * The old/traditional 68000 cores are basically all the same, at 16. | ||
76 | * The ColdFire cores vary a little, their values are defined in their | ||
77 | * headers. We default to the standard 68000 value here. | ||
78 | */ | ||
79 | #ifndef CPU_INSTR_PER_JIFFY | ||
80 | #define CPU_INSTR_PER_JIFFY 16 | ||
81 | #endif | ||
82 | |||
83 | extern int _stext, _etext, _sdata, _edata, _sbss, _ebss, _end; | ||
84 | extern int _ramstart, _ramend; | ||
85 | |||
86 | #if defined(CONFIG_UBOOT) | ||
87 | /* | ||
88 | * parse_uboot_commandline | ||
89 | * | ||
90 | * Copies u-boot commandline arguments and store them in the proper linux | ||
91 | * variables. | ||
92 | * | ||
93 | * Assumes: | ||
94 | * _init_sp global contains the address in the stack pointer when the | ||
95 | * kernel starts (see head.S::_start) | ||
96 | * | ||
97 | * U-Boot calling convention: | ||
98 | * (*kernel) (kbd, initrd_start, initrd_end, cmd_start, cmd_end); | ||
99 | * | ||
100 | * _init_sp can be parsed as such | ||
101 | * | ||
102 | * _init_sp+00 = u-boot cmd after jsr into kernel (skip) | ||
103 | * _init_sp+04 = &kernel board_info (residual data) | ||
104 | * _init_sp+08 = &initrd_start | ||
105 | * _init_sp+12 = &initrd_end | ||
106 | * _init_sp+16 = &cmd_start | ||
107 | * _init_sp+20 = &cmd_end | ||
108 | * | ||
109 | * This also assumes that the memory locations pointed to are still | ||
110 | * unmodified. U-boot places them near the end of external SDRAM. | ||
111 | * | ||
112 | * Argument(s): | ||
113 | * commandp = the linux commandline arg container to fill. | ||
114 | * size = the sizeof commandp. | ||
115 | * | ||
116 | * Returns: | ||
117 | */ | ||
118 | void parse_uboot_commandline(char *commandp, int size) | ||
119 | { | ||
120 | extern unsigned long _init_sp; | ||
121 | unsigned long *sp; | ||
122 | unsigned long uboot_kbd; | ||
123 | unsigned long uboot_initrd_start, uboot_initrd_end; | ||
124 | unsigned long uboot_cmd_start, uboot_cmd_end; | ||
125 | |||
126 | |||
127 | sp = (unsigned long *)_init_sp; | ||
128 | uboot_kbd = sp[1]; | ||
129 | uboot_initrd_start = sp[2]; | ||
130 | uboot_initrd_end = sp[3]; | ||
131 | uboot_cmd_start = sp[4]; | ||
132 | uboot_cmd_end = sp[5]; | ||
133 | |||
134 | if (uboot_cmd_start && uboot_cmd_end) | ||
135 | strncpy(commandp, (const char *)uboot_cmd_start, size); | ||
136 | #if defined(CONFIG_BLK_DEV_INITRD) | ||
137 | if (uboot_initrd_start && uboot_initrd_end && | ||
138 | (uboot_initrd_end > uboot_initrd_start)) { | ||
139 | initrd_start = uboot_initrd_start; | ||
140 | initrd_end = uboot_initrd_end; | ||
141 | ROOT_DEV = Root_RAM0; | ||
142 | printk(KERN_INFO "initrd at 0x%lx:0x%lx\n", | ||
143 | initrd_start, initrd_end); | ||
144 | } | ||
145 | #endif /* if defined(CONFIG_BLK_DEV_INITRD) */ | ||
146 | } | ||
147 | #endif /* #if defined(CONFIG_UBOOT) */ | ||
148 | |||
149 | void __init setup_arch(char **cmdline_p) | ||
150 | { | ||
151 | int bootmap_size; | ||
152 | |||
153 | memory_start = PAGE_ALIGN(_ramstart); | ||
154 | memory_end = _ramend; | ||
155 | |||
156 | init_mm.start_code = (unsigned long) &_stext; | ||
157 | init_mm.end_code = (unsigned long) &_etext; | ||
158 | init_mm.end_data = (unsigned long) &_edata; | ||
159 | init_mm.brk = (unsigned long) 0; | ||
160 | |||
161 | config_BSP(&command_line[0], sizeof(command_line)); | ||
162 | |||
163 | #if defined(CONFIG_BOOTPARAM) | ||
164 | strncpy(&command_line[0], CONFIG_BOOTPARAM_STRING, sizeof(command_line)); | ||
165 | command_line[sizeof(command_line) - 1] = 0; | ||
166 | #endif /* CONFIG_BOOTPARAM */ | ||
167 | |||
168 | #if defined(CONFIG_UBOOT) | ||
169 | /* CONFIG_UBOOT and CONFIG_BOOTPARAM defined, concatenate cmdline */ | ||
170 | #if defined(CONFIG_BOOTPARAM) | ||
171 | /* Add the whitespace separator */ | ||
172 | command_line[strlen(CONFIG_BOOTPARAM_STRING)] = ' '; | ||
173 | /* Parse uboot command line into the rest of the buffer */ | ||
174 | parse_uboot_commandline( | ||
175 | &command_line[(strlen(CONFIG_BOOTPARAM_STRING)+1)], | ||
176 | (sizeof(command_line) - | ||
177 | (strlen(CONFIG_BOOTPARAM_STRING)+1))); | ||
178 | /* Only CONFIG_UBOOT defined, create cmdline */ | ||
179 | #else | ||
180 | parse_uboot_commandline(&command_line[0], sizeof(command_line)); | ||
181 | #endif /* CONFIG_BOOTPARAM */ | ||
182 | command_line[sizeof(command_line) - 1] = 0; | ||
183 | #endif /* CONFIG_UBOOT */ | ||
184 | |||
185 | printk(KERN_INFO "\x0F\r\n\nuClinux/" CPU_NAME "\n"); | ||
186 | |||
187 | #ifdef CONFIG_UCDIMM | ||
188 | printk(KERN_INFO "uCdimm by Lineo, Inc. <www.lineo.com>\n"); | ||
189 | #endif | ||
190 | #ifdef CONFIG_M68VZ328 | ||
191 | printk(KERN_INFO "M68VZ328 support by Evan Stawnyczy <e@lineo.ca>\n"); | ||
192 | #endif | ||
193 | #ifdef CONFIG_COLDFIRE | ||
194 | printk(KERN_INFO "COLDFIRE port done by Greg Ungerer, gerg@snapgear.com\n"); | ||
195 | #ifdef CONFIG_M5307 | ||
196 | printk(KERN_INFO "Modified for M5307 by Dave Miller, dmiller@intellistor.com\n"); | ||
197 | #endif | ||
198 | #ifdef CONFIG_ELITE | ||
199 | printk(KERN_INFO "Modified for M5206eLITE by Rob Scott, rscott@mtrob.fdns.net\n"); | ||
200 | #endif | ||
201 | #endif | ||
202 | printk(KERN_INFO "Flat model support (C) 1998,1999 Kenneth Albanowski, D. Jeff Dionne\n"); | ||
203 | |||
204 | #if defined( CONFIG_PILOT ) && defined( CONFIG_M68328 ) | ||
205 | printk(KERN_INFO "TRG SuperPilot FLASH card support <info@trgnet.com>\n"); | ||
206 | #endif | ||
207 | #if defined( CONFIG_PILOT ) && defined( CONFIG_M68EZ328 ) | ||
208 | printk(KERN_INFO "PalmV support by Lineo Inc. <jeff@uclinux.com>\n"); | ||
209 | #endif | ||
210 | #if defined (CONFIG_M68360) | ||
211 | printk(KERN_INFO "QUICC port done by SED Systems <hamilton@sedsystems.ca>,\n"); | ||
212 | printk(KERN_INFO "based on 2.0.38 port by Lineo Inc. <mleslie@lineo.com>.\n"); | ||
213 | #endif | ||
214 | #ifdef CONFIG_DRAGEN2 | ||
215 | printk(KERN_INFO "DragonEngine II board support by Georges Menie\n"); | ||
216 | #endif | ||
217 | #ifdef CONFIG_M5235EVB | ||
218 | printk(KERN_INFO "Motorola M5235EVB support (C)2005 Syn-tech Systems, Inc. (Jate Sujjavanich)\n"); | ||
219 | #endif | ||
220 | |||
221 | pr_debug("KERNEL -> TEXT=0x%06x-0x%06x DATA=0x%06x-0x%06x " | ||
222 | "BSS=0x%06x-0x%06x\n", (int) &_stext, (int) &_etext, | ||
223 | (int) &_sdata, (int) &_edata, | ||
224 | (int) &_sbss, (int) &_ebss); | ||
225 | pr_debug("MEMORY -> ROMFS=0x%06x-0x%06x MEM=0x%06x-0x%06x\n ", | ||
226 | (int) &_ebss, (int) memory_start, | ||
227 | (int) memory_start, (int) memory_end); | ||
228 | |||
229 | /* Keep a copy of command line */ | ||
230 | *cmdline_p = &command_line[0]; | ||
231 | memcpy(boot_command_line, command_line, COMMAND_LINE_SIZE); | ||
232 | boot_command_line[COMMAND_LINE_SIZE-1] = 0; | ||
233 | |||
234 | #if defined(CONFIG_FRAMEBUFFER_CONSOLE) && defined(CONFIG_DUMMY_CONSOLE) | ||
235 | conswitchp = &dummy_con; | ||
236 | #endif | ||
237 | |||
238 | /* | ||
239 | * Give all the memory to the bootmap allocator, tell it to put the | ||
240 | * boot mem_map at the start of memory. | ||
241 | */ | ||
242 | bootmap_size = init_bootmem_node( | ||
243 | NODE_DATA(0), | ||
244 | memory_start >> PAGE_SHIFT, /* map goes here */ | ||
245 | PAGE_OFFSET >> PAGE_SHIFT, /* 0 on coldfire */ | ||
246 | memory_end >> PAGE_SHIFT); | ||
247 | /* | ||
248 | * Free the usable memory, we have to make sure we do not free | ||
249 | * the bootmem bitmap so we then reserve it after freeing it :-) | ||
250 | */ | ||
251 | free_bootmem(memory_start, memory_end - memory_start); | ||
252 | reserve_bootmem(memory_start, bootmap_size, BOOTMEM_DEFAULT); | ||
253 | |||
254 | #if defined(CONFIG_UBOOT) && defined(CONFIG_BLK_DEV_INITRD) | ||
255 | if ((initrd_start > 0) && (initrd_start < initrd_end) && | ||
256 | (initrd_end < memory_end)) | ||
257 | reserve_bootmem(initrd_start, initrd_end - initrd_start, | ||
258 | BOOTMEM_DEFAULT); | ||
259 | #endif /* if defined(CONFIG_BLK_DEV_INITRD) */ | ||
260 | |||
261 | /* | ||
262 | * Get kmalloc into gear. | ||
263 | */ | ||
264 | paging_init(); | ||
265 | } | ||
266 | |||
267 | /* | ||
268 | * Get CPU information for use by the procfs. | ||
269 | */ | ||
270 | static int show_cpuinfo(struct seq_file *m, void *v) | ||
271 | { | ||
272 | char *cpu, *mmu, *fpu; | ||
273 | u_long clockfreq; | ||
274 | |||
275 | cpu = CPU_NAME; | ||
276 | mmu = "none"; | ||
277 | fpu = "none"; | ||
278 | clockfreq = (loops_per_jiffy * HZ) * CPU_INSTR_PER_JIFFY; | ||
279 | |||
280 | seq_printf(m, "CPU:\t\t%s\n" | ||
281 | "MMU:\t\t%s\n" | ||
282 | "FPU:\t\t%s\n" | ||
283 | "Clocking:\t%lu.%1luMHz\n" | ||
284 | "BogoMips:\t%lu.%02lu\n" | ||
285 | "Calibration:\t%lu loops\n", | ||
286 | cpu, mmu, fpu, | ||
287 | clockfreq / 1000000, | ||
288 | (clockfreq / 100000) % 10, | ||
289 | (loops_per_jiffy * HZ) / 500000, | ||
290 | ((loops_per_jiffy * HZ) / 5000) % 100, | ||
291 | (loops_per_jiffy * HZ)); | ||
292 | |||
293 | return 0; | ||
294 | } | ||
295 | |||
296 | static void *c_start(struct seq_file *m, loff_t *pos) | ||
297 | { | ||
298 | return *pos < NR_CPUS ? ((void *) 0x12345678) : NULL; | ||
299 | } | ||
300 | |||
301 | static void *c_next(struct seq_file *m, void *v, loff_t *pos) | ||
302 | { | ||
303 | ++*pos; | ||
304 | return c_start(m, pos); | ||
305 | } | ||
306 | |||
307 | static void c_stop(struct seq_file *m, void *v) | ||
308 | { | ||
309 | } | ||
310 | |||
311 | const struct seq_operations cpuinfo_op = { | ||
312 | .start = c_start, | ||
313 | .next = c_next, | ||
314 | .stop = c_stop, | ||
315 | .show = show_cpuinfo, | ||
316 | }; | ||
317 | |||
diff --git a/arch/m68k/kernel/signal.c b/arch/m68k/kernel/signal.c index a0afc239304e..2e25713e2ead 100644 --- a/arch/m68k/kernel/signal.c +++ b/arch/m68k/kernel/signal.c | |||
@@ -1,1017 +1,5 @@ | |||
1 | /* | 1 | #ifdef CONFIG_MMU |
2 | * linux/arch/m68k/kernel/signal.c | 2 | #include "signal_mm.c" |
3 | * | ||
4 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file COPYING in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * Linux/m68k support by Hamish Macdonald | ||
13 | * | ||
14 | * 68060 fixes by Jesper Skov | ||
15 | * | ||
16 | * 1997-12-01 Modified for POSIX.1b signals by Andreas Schwab | ||
17 | * | ||
18 | * mathemu support by Roman Zippel | ||
19 | * (Note: fpstate in the signal context is completely ignored for the emulator | ||
20 | * and the internal floating point format is put on stack) | ||
21 | */ | ||
22 | |||
23 | /* | ||
24 | * ++roman (07/09/96): implemented signal stacks (specially for tosemu on | ||
25 | * Atari :-) Current limitation: Only one sigstack can be active at one time. | ||
26 | * If a second signal with SA_ONSTACK set arrives while working on a sigstack, | ||
27 | * SA_ONSTACK is ignored. This behaviour avoids lots of trouble with nested | ||
28 | * signal handlers! | ||
29 | */ | ||
30 | |||
31 | #include <linux/sched.h> | ||
32 | #include <linux/mm.h> | ||
33 | #include <linux/kernel.h> | ||
34 | #include <linux/signal.h> | ||
35 | #include <linux/syscalls.h> | ||
36 | #include <linux/errno.h> | ||
37 | #include <linux/wait.h> | ||
38 | #include <linux/ptrace.h> | ||
39 | #include <linux/unistd.h> | ||
40 | #include <linux/stddef.h> | ||
41 | #include <linux/highuid.h> | ||
42 | #include <linux/personality.h> | ||
43 | #include <linux/tty.h> | ||
44 | #include <linux/binfmts.h> | ||
45 | #include <linux/module.h> | ||
46 | |||
47 | #include <asm/setup.h> | ||
48 | #include <asm/uaccess.h> | ||
49 | #include <asm/pgtable.h> | ||
50 | #include <asm/traps.h> | ||
51 | #include <asm/ucontext.h> | ||
52 | |||
53 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | ||
54 | |||
55 | static const int frame_extra_sizes[16] = { | ||
56 | [1] = -1, /* sizeof(((struct frame *)0)->un.fmt1), */ | ||
57 | [2] = sizeof(((struct frame *)0)->un.fmt2), | ||
58 | [3] = sizeof(((struct frame *)0)->un.fmt3), | ||
59 | [4] = sizeof(((struct frame *)0)->un.fmt4), | ||
60 | [5] = -1, /* sizeof(((struct frame *)0)->un.fmt5), */ | ||
61 | [6] = -1, /* sizeof(((struct frame *)0)->un.fmt6), */ | ||
62 | [7] = sizeof(((struct frame *)0)->un.fmt7), | ||
63 | [8] = -1, /* sizeof(((struct frame *)0)->un.fmt8), */ | ||
64 | [9] = sizeof(((struct frame *)0)->un.fmt9), | ||
65 | [10] = sizeof(((struct frame *)0)->un.fmta), | ||
66 | [11] = sizeof(((struct frame *)0)->un.fmtb), | ||
67 | [12] = -1, /* sizeof(((struct frame *)0)->un.fmtc), */ | ||
68 | [13] = -1, /* sizeof(((struct frame *)0)->un.fmtd), */ | ||
69 | [14] = -1, /* sizeof(((struct frame *)0)->un.fmte), */ | ||
70 | [15] = -1, /* sizeof(((struct frame *)0)->un.fmtf), */ | ||
71 | }; | ||
72 | |||
73 | int handle_kernel_fault(struct pt_regs *regs) | ||
74 | { | ||
75 | const struct exception_table_entry *fixup; | ||
76 | struct pt_regs *tregs; | ||
77 | |||
78 | /* Are we prepared to handle this kernel fault? */ | ||
79 | fixup = search_exception_tables(regs->pc); | ||
80 | if (!fixup) | ||
81 | return 0; | ||
82 | |||
83 | /* Create a new four word stack frame, discarding the old one. */ | ||
84 | regs->stkadj = frame_extra_sizes[regs->format]; | ||
85 | tregs = (struct pt_regs *)((long)regs + regs->stkadj); | ||
86 | tregs->vector = regs->vector; | ||
87 | tregs->format = 0; | ||
88 | tregs->pc = fixup->fixup; | ||
89 | tregs->sr = regs->sr; | ||
90 | |||
91 | return 1; | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * Atomically swap in the new signal mask, and wait for a signal. | ||
96 | */ | ||
97 | asmlinkage int | ||
98 | sys_sigsuspend(int unused0, int unused1, old_sigset_t mask) | ||
99 | { | ||
100 | mask &= _BLOCKABLE; | ||
101 | spin_lock_irq(¤t->sighand->siglock); | ||
102 | current->saved_sigmask = current->blocked; | ||
103 | siginitset(¤t->blocked, mask); | ||
104 | recalc_sigpending(); | ||
105 | spin_unlock_irq(¤t->sighand->siglock); | ||
106 | |||
107 | current->state = TASK_INTERRUPTIBLE; | ||
108 | schedule(); | ||
109 | set_restore_sigmask(); | ||
110 | |||
111 | return -ERESTARTNOHAND; | ||
112 | } | ||
113 | |||
114 | asmlinkage int | ||
115 | sys_sigaction(int sig, const struct old_sigaction __user *act, | ||
116 | struct old_sigaction __user *oact) | ||
117 | { | ||
118 | struct k_sigaction new_ka, old_ka; | ||
119 | int ret; | ||
120 | |||
121 | if (act) { | ||
122 | old_sigset_t mask; | ||
123 | if (!access_ok(VERIFY_READ, act, sizeof(*act)) || | ||
124 | __get_user(new_ka.sa.sa_handler, &act->sa_handler) || | ||
125 | __get_user(new_ka.sa.sa_restorer, &act->sa_restorer) || | ||
126 | __get_user(new_ka.sa.sa_flags, &act->sa_flags) || | ||
127 | __get_user(mask, &act->sa_mask)) | ||
128 | return -EFAULT; | ||
129 | siginitset(&new_ka.sa.sa_mask, mask); | ||
130 | } | ||
131 | |||
132 | ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); | ||
133 | |||
134 | if (!ret && oact) { | ||
135 | if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || | ||
136 | __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || | ||
137 | __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer) || | ||
138 | __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || | ||
139 | __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) | ||
140 | return -EFAULT; | ||
141 | } | ||
142 | |||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | asmlinkage int | ||
147 | sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss) | ||
148 | { | ||
149 | return do_sigaltstack(uss, uoss, rdusp()); | ||
150 | } | ||
151 | |||
152 | |||
153 | /* | ||
154 | * Do a signal return; undo the signal stack. | ||
155 | * | ||
156 | * Keep the return code on the stack quadword aligned! | ||
157 | * That makes the cache flush below easier. | ||
158 | */ | ||
159 | |||
160 | struct sigframe | ||
161 | { | ||
162 | char __user *pretcode; | ||
163 | int sig; | ||
164 | int code; | ||
165 | struct sigcontext __user *psc; | ||
166 | char retcode[8]; | ||
167 | unsigned long extramask[_NSIG_WORDS-1]; | ||
168 | struct sigcontext sc; | ||
169 | }; | ||
170 | |||
171 | struct rt_sigframe | ||
172 | { | ||
173 | char __user *pretcode; | ||
174 | int sig; | ||
175 | struct siginfo __user *pinfo; | ||
176 | void __user *puc; | ||
177 | char retcode[8]; | ||
178 | struct siginfo info; | ||
179 | struct ucontext uc; | ||
180 | }; | ||
181 | |||
182 | |||
183 | static unsigned char fpu_version; /* version number of fpu, set by setup_frame */ | ||
184 | |||
185 | static inline int restore_fpu_state(struct sigcontext *sc) | ||
186 | { | ||
187 | int err = 1; | ||
188 | |||
189 | if (FPU_IS_EMU) { | ||
190 | /* restore registers */ | ||
191 | memcpy(current->thread.fpcntl, sc->sc_fpcntl, 12); | ||
192 | memcpy(current->thread.fp, sc->sc_fpregs, 24); | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | if (CPU_IS_060 ? sc->sc_fpstate[2] : sc->sc_fpstate[0]) { | ||
197 | /* Verify the frame format. */ | ||
198 | if (!CPU_IS_060 && (sc->sc_fpstate[0] != fpu_version)) | ||
199 | goto out; | ||
200 | if (CPU_IS_020_OR_030) { | ||
201 | if (m68k_fputype & FPU_68881 && | ||
202 | !(sc->sc_fpstate[1] == 0x18 || sc->sc_fpstate[1] == 0xb4)) | ||
203 | goto out; | ||
204 | if (m68k_fputype & FPU_68882 && | ||
205 | !(sc->sc_fpstate[1] == 0x38 || sc->sc_fpstate[1] == 0xd4)) | ||
206 | goto out; | ||
207 | } else if (CPU_IS_040) { | ||
208 | if (!(sc->sc_fpstate[1] == 0x00 || | ||
209 | sc->sc_fpstate[1] == 0x28 || | ||
210 | sc->sc_fpstate[1] == 0x60)) | ||
211 | goto out; | ||
212 | } else if (CPU_IS_060) { | ||
213 | if (!(sc->sc_fpstate[3] == 0x00 || | ||
214 | sc->sc_fpstate[3] == 0x60 || | ||
215 | sc->sc_fpstate[3] == 0xe0)) | ||
216 | goto out; | ||
217 | } else | ||
218 | goto out; | ||
219 | |||
220 | __asm__ volatile (".chip 68k/68881\n\t" | ||
221 | "fmovemx %0,%%fp0-%%fp1\n\t" | ||
222 | "fmoveml %1,%%fpcr/%%fpsr/%%fpiar\n\t" | ||
223 | ".chip 68k" | ||
224 | : /* no outputs */ | ||
225 | : "m" (*sc->sc_fpregs), "m" (*sc->sc_fpcntl)); | ||
226 | } | ||
227 | __asm__ volatile (".chip 68k/68881\n\t" | ||
228 | "frestore %0\n\t" | ||
229 | ".chip 68k" : : "m" (*sc->sc_fpstate)); | ||
230 | err = 0; | ||
231 | |||
232 | out: | ||
233 | return err; | ||
234 | } | ||
235 | |||
236 | #define FPCONTEXT_SIZE 216 | ||
237 | #define uc_fpstate uc_filler[0] | ||
238 | #define uc_formatvec uc_filler[FPCONTEXT_SIZE/4] | ||
239 | #define uc_extra uc_filler[FPCONTEXT_SIZE/4+1] | ||
240 | |||
241 | static inline int rt_restore_fpu_state(struct ucontext __user *uc) | ||
242 | { | ||
243 | unsigned char fpstate[FPCONTEXT_SIZE]; | ||
244 | int context_size = CPU_IS_060 ? 8 : 0; | ||
245 | fpregset_t fpregs; | ||
246 | int err = 1; | ||
247 | |||
248 | if (FPU_IS_EMU) { | ||
249 | /* restore fpu control register */ | ||
250 | if (__copy_from_user(current->thread.fpcntl, | ||
251 | uc->uc_mcontext.fpregs.f_fpcntl, 12)) | ||
252 | goto out; | ||
253 | /* restore all other fpu register */ | ||
254 | if (__copy_from_user(current->thread.fp, | ||
255 | uc->uc_mcontext.fpregs.f_fpregs, 96)) | ||
256 | goto out; | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | if (__get_user(*(long *)fpstate, (long __user *)&uc->uc_fpstate)) | ||
261 | goto out; | ||
262 | if (CPU_IS_060 ? fpstate[2] : fpstate[0]) { | ||
263 | if (!CPU_IS_060) | ||
264 | context_size = fpstate[1]; | ||
265 | /* Verify the frame format. */ | ||
266 | if (!CPU_IS_060 && (fpstate[0] != fpu_version)) | ||
267 | goto out; | ||
268 | if (CPU_IS_020_OR_030) { | ||
269 | if (m68k_fputype & FPU_68881 && | ||
270 | !(context_size == 0x18 || context_size == 0xb4)) | ||
271 | goto out; | ||
272 | if (m68k_fputype & FPU_68882 && | ||
273 | !(context_size == 0x38 || context_size == 0xd4)) | ||
274 | goto out; | ||
275 | } else if (CPU_IS_040) { | ||
276 | if (!(context_size == 0x00 || | ||
277 | context_size == 0x28 || | ||
278 | context_size == 0x60)) | ||
279 | goto out; | ||
280 | } else if (CPU_IS_060) { | ||
281 | if (!(fpstate[3] == 0x00 || | ||
282 | fpstate[3] == 0x60 || | ||
283 | fpstate[3] == 0xe0)) | ||
284 | goto out; | ||
285 | } else | ||
286 | goto out; | ||
287 | if (__copy_from_user(&fpregs, &uc->uc_mcontext.fpregs, | ||
288 | sizeof(fpregs))) | ||
289 | goto out; | ||
290 | __asm__ volatile (".chip 68k/68881\n\t" | ||
291 | "fmovemx %0,%%fp0-%%fp7\n\t" | ||
292 | "fmoveml %1,%%fpcr/%%fpsr/%%fpiar\n\t" | ||
293 | ".chip 68k" | ||
294 | : /* no outputs */ | ||
295 | : "m" (*fpregs.f_fpregs), | ||
296 | "m" (*fpregs.f_fpcntl)); | ||
297 | } | ||
298 | if (context_size && | ||
299 | __copy_from_user(fpstate + 4, (long __user *)&uc->uc_fpstate + 1, | ||
300 | context_size)) | ||
301 | goto out; | ||
302 | __asm__ volatile (".chip 68k/68881\n\t" | ||
303 | "frestore %0\n\t" | ||
304 | ".chip 68k" : : "m" (*fpstate)); | ||
305 | err = 0; | ||
306 | |||
307 | out: | ||
308 | return err; | ||
309 | } | ||
310 | |||
311 | static int mangle_kernel_stack(struct pt_regs *regs, int formatvec, | ||
312 | void __user *fp) | ||
313 | { | ||
314 | int fsize = frame_extra_sizes[formatvec >> 12]; | ||
315 | if (fsize < 0) { | ||
316 | /* | ||
317 | * user process trying to return with weird frame format | ||
318 | */ | ||
319 | #ifdef DEBUG | ||
320 | printk("user process returning with weird frame format\n"); | ||
321 | #endif | ||
322 | return 1; | ||
323 | } | ||
324 | if (!fsize) { | ||
325 | regs->format = formatvec >> 12; | ||
326 | regs->vector = formatvec & 0xfff; | ||
327 | } else { | ||
328 | struct switch_stack *sw = (struct switch_stack *)regs - 1; | ||
329 | unsigned long buf[fsize / 2]; /* yes, twice as much */ | ||
330 | |||
331 | /* that'll make sure that expansion won't crap over data */ | ||
332 | if (copy_from_user(buf + fsize / 4, fp, fsize)) | ||
333 | return 1; | ||
334 | |||
335 | /* point of no return */ | ||
336 | regs->format = formatvec >> 12; | ||
337 | regs->vector = formatvec & 0xfff; | ||
338 | #define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack)) | ||
339 | __asm__ __volatile__ | ||
340 | (" movel %0,%/a0\n\t" | ||
341 | " subl %1,%/a0\n\t" /* make room on stack */ | ||
342 | " movel %/a0,%/sp\n\t" /* set stack pointer */ | ||
343 | /* move switch_stack and pt_regs */ | ||
344 | "1: movel %0@+,%/a0@+\n\t" | ||
345 | " dbra %2,1b\n\t" | ||
346 | " lea %/sp@(%c3),%/a0\n\t" /* add offset of fmt */ | ||
347 | " lsrl #2,%1\n\t" | ||
348 | " subql #1,%1\n\t" | ||
349 | /* copy to the gap we'd made */ | ||
350 | "2: movel %4@+,%/a0@+\n\t" | ||
351 | " dbra %1,2b\n\t" | ||
352 | " bral ret_from_signal\n" | ||
353 | : /* no outputs, it doesn't ever return */ | ||
354 | : "a" (sw), "d" (fsize), "d" (frame_offset/4-1), | ||
355 | "n" (frame_offset), "a" (buf + fsize/4) | ||
356 | : "a0"); | ||
357 | #undef frame_offset | ||
358 | } | ||
359 | return 0; | ||
360 | } | ||
361 | |||
362 | static inline int | ||
363 | restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __user *fp) | ||
364 | { | ||
365 | int formatvec; | ||
366 | struct sigcontext context; | ||
367 | int err; | ||
368 | |||
369 | /* Always make any pending restarted system calls return -EINTR */ | ||
370 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
371 | |||
372 | /* get previous context */ | ||
373 | if (copy_from_user(&context, usc, sizeof(context))) | ||
374 | goto badframe; | ||
375 | |||
376 | /* restore passed registers */ | ||
377 | regs->d0 = context.sc_d0; | ||
378 | regs->d1 = context.sc_d1; | ||
379 | regs->a0 = context.sc_a0; | ||
380 | regs->a1 = context.sc_a1; | ||
381 | regs->sr = (regs->sr & 0xff00) | (context.sc_sr & 0xff); | ||
382 | regs->pc = context.sc_pc; | ||
383 | regs->orig_d0 = -1; /* disable syscall checks */ | ||
384 | wrusp(context.sc_usp); | ||
385 | formatvec = context.sc_formatvec; | ||
386 | |||
387 | err = restore_fpu_state(&context); | ||
388 | |||
389 | if (err || mangle_kernel_stack(regs, formatvec, fp)) | ||
390 | goto badframe; | ||
391 | |||
392 | return 0; | ||
393 | |||
394 | badframe: | ||
395 | return 1; | ||
396 | } | ||
397 | |||
398 | static inline int | ||
399 | rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw, | ||
400 | struct ucontext __user *uc) | ||
401 | { | ||
402 | int temp; | ||
403 | greg_t __user *gregs = uc->uc_mcontext.gregs; | ||
404 | unsigned long usp; | ||
405 | int err; | ||
406 | |||
407 | /* Always make any pending restarted system calls return -EINTR */ | ||
408 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
409 | |||
410 | err = __get_user(temp, &uc->uc_mcontext.version); | ||
411 | if (temp != MCONTEXT_VERSION) | ||
412 | goto badframe; | ||
413 | /* restore passed registers */ | ||
414 | err |= __get_user(regs->d0, &gregs[0]); | ||
415 | err |= __get_user(regs->d1, &gregs[1]); | ||
416 | err |= __get_user(regs->d2, &gregs[2]); | ||
417 | err |= __get_user(regs->d3, &gregs[3]); | ||
418 | err |= __get_user(regs->d4, &gregs[4]); | ||
419 | err |= __get_user(regs->d5, &gregs[5]); | ||
420 | err |= __get_user(sw->d6, &gregs[6]); | ||
421 | err |= __get_user(sw->d7, &gregs[7]); | ||
422 | err |= __get_user(regs->a0, &gregs[8]); | ||
423 | err |= __get_user(regs->a1, &gregs[9]); | ||
424 | err |= __get_user(regs->a2, &gregs[10]); | ||
425 | err |= __get_user(sw->a3, &gregs[11]); | ||
426 | err |= __get_user(sw->a4, &gregs[12]); | ||
427 | err |= __get_user(sw->a5, &gregs[13]); | ||
428 | err |= __get_user(sw->a6, &gregs[14]); | ||
429 | err |= __get_user(usp, &gregs[15]); | ||
430 | wrusp(usp); | ||
431 | err |= __get_user(regs->pc, &gregs[16]); | ||
432 | err |= __get_user(temp, &gregs[17]); | ||
433 | regs->sr = (regs->sr & 0xff00) | (temp & 0xff); | ||
434 | regs->orig_d0 = -1; /* disable syscall checks */ | ||
435 | err |= __get_user(temp, &uc->uc_formatvec); | ||
436 | |||
437 | err |= rt_restore_fpu_state(uc); | ||
438 | |||
439 | if (err || do_sigaltstack(&uc->uc_stack, NULL, usp) == -EFAULT) | ||
440 | goto badframe; | ||
441 | |||
442 | if (mangle_kernel_stack(regs, temp, &uc->uc_extra)) | ||
443 | goto badframe; | ||
444 | |||
445 | return 0; | ||
446 | |||
447 | badframe: | ||
448 | return 1; | ||
449 | } | ||
450 | |||
451 | asmlinkage int do_sigreturn(unsigned long __unused) | ||
452 | { | ||
453 | struct switch_stack *sw = (struct switch_stack *) &__unused; | ||
454 | struct pt_regs *regs = (struct pt_regs *) (sw + 1); | ||
455 | unsigned long usp = rdusp(); | ||
456 | struct sigframe __user *frame = (struct sigframe __user *)(usp - 4); | ||
457 | sigset_t set; | ||
458 | |||
459 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
460 | goto badframe; | ||
461 | if (__get_user(set.sig[0], &frame->sc.sc_mask) || | ||
462 | (_NSIG_WORDS > 1 && | ||
463 | __copy_from_user(&set.sig[1], &frame->extramask, | ||
464 | sizeof(frame->extramask)))) | ||
465 | goto badframe; | ||
466 | |||
467 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
468 | current->blocked = set; | ||
469 | recalc_sigpending(); | ||
470 | |||
471 | if (restore_sigcontext(regs, &frame->sc, frame + 1)) | ||
472 | goto badframe; | ||
473 | return regs->d0; | ||
474 | |||
475 | badframe: | ||
476 | force_sig(SIGSEGV, current); | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | asmlinkage int do_rt_sigreturn(unsigned long __unused) | ||
481 | { | ||
482 | struct switch_stack *sw = (struct switch_stack *) &__unused; | ||
483 | struct pt_regs *regs = (struct pt_regs *) (sw + 1); | ||
484 | unsigned long usp = rdusp(); | ||
485 | struct rt_sigframe __user *frame = (struct rt_sigframe __user *)(usp - 4); | ||
486 | sigset_t set; | ||
487 | |||
488 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
489 | goto badframe; | ||
490 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | ||
491 | goto badframe; | ||
492 | |||
493 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
494 | current->blocked = set; | ||
495 | recalc_sigpending(); | ||
496 | |||
497 | if (rt_restore_ucontext(regs, sw, &frame->uc)) | ||
498 | goto badframe; | ||
499 | return regs->d0; | ||
500 | |||
501 | badframe: | ||
502 | force_sig(SIGSEGV, current); | ||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | /* | ||
507 | * Set up a signal frame. | ||
508 | */ | ||
509 | |||
510 | static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs) | ||
511 | { | ||
512 | if (FPU_IS_EMU) { | ||
513 | /* save registers */ | ||
514 | memcpy(sc->sc_fpcntl, current->thread.fpcntl, 12); | ||
515 | memcpy(sc->sc_fpregs, current->thread.fp, 24); | ||
516 | return; | ||
517 | } | ||
518 | |||
519 | __asm__ volatile (".chip 68k/68881\n\t" | ||
520 | "fsave %0\n\t" | ||
521 | ".chip 68k" | ||
522 | : : "m" (*sc->sc_fpstate) : "memory"); | ||
523 | |||
524 | if (CPU_IS_060 ? sc->sc_fpstate[2] : sc->sc_fpstate[0]) { | ||
525 | fpu_version = sc->sc_fpstate[0]; | ||
526 | if (CPU_IS_020_OR_030 && | ||
527 | regs->vector >= (VEC_FPBRUC * 4) && | ||
528 | regs->vector <= (VEC_FPNAN * 4)) { | ||
529 | /* Clear pending exception in 68882 idle frame */ | ||
530 | if (*(unsigned short *) sc->sc_fpstate == 0x1f38) | ||
531 | sc->sc_fpstate[0x38] |= 1 << 3; | ||
532 | } | ||
533 | __asm__ volatile (".chip 68k/68881\n\t" | ||
534 | "fmovemx %%fp0-%%fp1,%0\n\t" | ||
535 | "fmoveml %%fpcr/%%fpsr/%%fpiar,%1\n\t" | ||
536 | ".chip 68k" | ||
537 | : "=m" (*sc->sc_fpregs), | ||
538 | "=m" (*sc->sc_fpcntl) | ||
539 | : /* no inputs */ | ||
540 | : "memory"); | ||
541 | } | ||
542 | } | ||
543 | |||
544 | static inline int rt_save_fpu_state(struct ucontext __user *uc, struct pt_regs *regs) | ||
545 | { | ||
546 | unsigned char fpstate[FPCONTEXT_SIZE]; | ||
547 | int context_size = CPU_IS_060 ? 8 : 0; | ||
548 | int err = 0; | ||
549 | |||
550 | if (FPU_IS_EMU) { | ||
551 | /* save fpu control register */ | ||
552 | err |= copy_to_user(uc->uc_mcontext.fpregs.f_fpcntl, | ||
553 | current->thread.fpcntl, 12); | ||
554 | /* save all other fpu register */ | ||
555 | err |= copy_to_user(uc->uc_mcontext.fpregs.f_fpregs, | ||
556 | current->thread.fp, 96); | ||
557 | return err; | ||
558 | } | ||
559 | |||
560 | __asm__ volatile (".chip 68k/68881\n\t" | ||
561 | "fsave %0\n\t" | ||
562 | ".chip 68k" | ||
563 | : : "m" (*fpstate) : "memory"); | ||
564 | |||
565 | err |= __put_user(*(long *)fpstate, (long __user *)&uc->uc_fpstate); | ||
566 | if (CPU_IS_060 ? fpstate[2] : fpstate[0]) { | ||
567 | fpregset_t fpregs; | ||
568 | if (!CPU_IS_060) | ||
569 | context_size = fpstate[1]; | ||
570 | fpu_version = fpstate[0]; | ||
571 | if (CPU_IS_020_OR_030 && | ||
572 | regs->vector >= (VEC_FPBRUC * 4) && | ||
573 | regs->vector <= (VEC_FPNAN * 4)) { | ||
574 | /* Clear pending exception in 68882 idle frame */ | ||
575 | if (*(unsigned short *) fpstate == 0x1f38) | ||
576 | fpstate[0x38] |= 1 << 3; | ||
577 | } | ||
578 | __asm__ volatile (".chip 68k/68881\n\t" | ||
579 | "fmovemx %%fp0-%%fp7,%0\n\t" | ||
580 | "fmoveml %%fpcr/%%fpsr/%%fpiar,%1\n\t" | ||
581 | ".chip 68k" | ||
582 | : "=m" (*fpregs.f_fpregs), | ||
583 | "=m" (*fpregs.f_fpcntl) | ||
584 | : /* no inputs */ | ||
585 | : "memory"); | ||
586 | err |= copy_to_user(&uc->uc_mcontext.fpregs, &fpregs, | ||
587 | sizeof(fpregs)); | ||
588 | } | ||
589 | if (context_size) | ||
590 | err |= copy_to_user((long __user *)&uc->uc_fpstate + 1, fpstate + 4, | ||
591 | context_size); | ||
592 | return err; | ||
593 | } | ||
594 | |||
595 | static void setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, | ||
596 | unsigned long mask) | ||
597 | { | ||
598 | sc->sc_mask = mask; | ||
599 | sc->sc_usp = rdusp(); | ||
600 | sc->sc_d0 = regs->d0; | ||
601 | sc->sc_d1 = regs->d1; | ||
602 | sc->sc_a0 = regs->a0; | ||
603 | sc->sc_a1 = regs->a1; | ||
604 | sc->sc_sr = regs->sr; | ||
605 | sc->sc_pc = regs->pc; | ||
606 | sc->sc_formatvec = regs->format << 12 | regs->vector; | ||
607 | save_fpu_state(sc, regs); | ||
608 | } | ||
609 | |||
610 | static inline int rt_setup_ucontext(struct ucontext __user *uc, struct pt_regs *regs) | ||
611 | { | ||
612 | struct switch_stack *sw = (struct switch_stack *)regs - 1; | ||
613 | greg_t __user *gregs = uc->uc_mcontext.gregs; | ||
614 | int err = 0; | ||
615 | |||
616 | err |= __put_user(MCONTEXT_VERSION, &uc->uc_mcontext.version); | ||
617 | err |= __put_user(regs->d0, &gregs[0]); | ||
618 | err |= __put_user(regs->d1, &gregs[1]); | ||
619 | err |= __put_user(regs->d2, &gregs[2]); | ||
620 | err |= __put_user(regs->d3, &gregs[3]); | ||
621 | err |= __put_user(regs->d4, &gregs[4]); | ||
622 | err |= __put_user(regs->d5, &gregs[5]); | ||
623 | err |= __put_user(sw->d6, &gregs[6]); | ||
624 | err |= __put_user(sw->d7, &gregs[7]); | ||
625 | err |= __put_user(regs->a0, &gregs[8]); | ||
626 | err |= __put_user(regs->a1, &gregs[9]); | ||
627 | err |= __put_user(regs->a2, &gregs[10]); | ||
628 | err |= __put_user(sw->a3, &gregs[11]); | ||
629 | err |= __put_user(sw->a4, &gregs[12]); | ||
630 | err |= __put_user(sw->a5, &gregs[13]); | ||
631 | err |= __put_user(sw->a6, &gregs[14]); | ||
632 | err |= __put_user(rdusp(), &gregs[15]); | ||
633 | err |= __put_user(regs->pc, &gregs[16]); | ||
634 | err |= __put_user(regs->sr, &gregs[17]); | ||
635 | err |= __put_user((regs->format << 12) | regs->vector, &uc->uc_formatvec); | ||
636 | err |= rt_save_fpu_state(uc, regs); | ||
637 | return err; | ||
638 | } | ||
639 | |||
640 | static inline void push_cache (unsigned long vaddr) | ||
641 | { | ||
642 | /* | ||
643 | * Using the old cache_push_v() was really a big waste. | ||
644 | * | ||
645 | * What we are trying to do is to flush 8 bytes to ram. | ||
646 | * Flushing 2 cache lines of 16 bytes is much cheaper than | ||
647 | * flushing 1 or 2 pages, as previously done in | ||
648 | * cache_push_v(). | ||
649 | * Jes | ||
650 | */ | ||
651 | if (CPU_IS_040) { | ||
652 | unsigned long temp; | ||
653 | |||
654 | __asm__ __volatile__ (".chip 68040\n\t" | ||
655 | "nop\n\t" | ||
656 | "ptestr (%1)\n\t" | ||
657 | "movec %%mmusr,%0\n\t" | ||
658 | ".chip 68k" | ||
659 | : "=r" (temp) | ||
660 | : "a" (vaddr)); | ||
661 | |||
662 | temp &= PAGE_MASK; | ||
663 | temp |= vaddr & ~PAGE_MASK; | ||
664 | |||
665 | __asm__ __volatile__ (".chip 68040\n\t" | ||
666 | "nop\n\t" | ||
667 | "cpushl %%bc,(%0)\n\t" | ||
668 | ".chip 68k" | ||
669 | : : "a" (temp)); | ||
670 | } | ||
671 | else if (CPU_IS_060) { | ||
672 | unsigned long temp; | ||
673 | __asm__ __volatile__ (".chip 68060\n\t" | ||
674 | "plpar (%0)\n\t" | ||
675 | ".chip 68k" | ||
676 | : "=a" (temp) | ||
677 | : "0" (vaddr)); | ||
678 | __asm__ __volatile__ (".chip 68060\n\t" | ||
679 | "cpushl %%bc,(%0)\n\t" | ||
680 | ".chip 68k" | ||
681 | : : "a" (temp)); | ||
682 | } | ||
683 | else { | ||
684 | /* | ||
685 | * 68030/68020 have no writeback cache; | ||
686 | * still need to clear icache. | ||
687 | * Note that vaddr is guaranteed to be long word aligned. | ||
688 | */ | ||
689 | unsigned long temp; | ||
690 | asm volatile ("movec %%cacr,%0" : "=r" (temp)); | ||
691 | temp += 4; | ||
692 | asm volatile ("movec %0,%%caar\n\t" | ||
693 | "movec %1,%%cacr" | ||
694 | : : "r" (vaddr), "r" (temp)); | ||
695 | asm volatile ("movec %0,%%caar\n\t" | ||
696 | "movec %1,%%cacr" | ||
697 | : : "r" (vaddr + 4), "r" (temp)); | ||
698 | } | ||
699 | } | ||
700 | |||
701 | static inline void __user * | ||
702 | get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) | ||
703 | { | ||
704 | unsigned long usp; | ||
705 | |||
706 | /* Default to using normal stack. */ | ||
707 | usp = rdusp(); | ||
708 | |||
709 | /* This is the X/Open sanctioned signal stack switching. */ | ||
710 | if (ka->sa.sa_flags & SA_ONSTACK) { | ||
711 | if (!sas_ss_flags(usp)) | ||
712 | usp = current->sas_ss_sp + current->sas_ss_size; | ||
713 | } | ||
714 | return (void __user *)((usp - frame_size) & -8UL); | ||
715 | } | ||
716 | |||
717 | static int setup_frame (int sig, struct k_sigaction *ka, | ||
718 | sigset_t *set, struct pt_regs *regs) | ||
719 | { | ||
720 | struct sigframe __user *frame; | ||
721 | int fsize = frame_extra_sizes[regs->format]; | ||
722 | struct sigcontext context; | ||
723 | int err = 0; | ||
724 | |||
725 | if (fsize < 0) { | ||
726 | #ifdef DEBUG | ||
727 | printk ("setup_frame: Unknown frame format %#x\n", | ||
728 | regs->format); | ||
729 | #endif | ||
730 | goto give_sigsegv; | ||
731 | } | ||
732 | |||
733 | frame = get_sigframe(ka, regs, sizeof(*frame) + fsize); | ||
734 | |||
735 | if (fsize) | ||
736 | err |= copy_to_user (frame + 1, regs + 1, fsize); | ||
737 | |||
738 | err |= __put_user((current_thread_info()->exec_domain | ||
739 | && current_thread_info()->exec_domain->signal_invmap | ||
740 | && sig < 32 | ||
741 | ? current_thread_info()->exec_domain->signal_invmap[sig] | ||
742 | : sig), | ||
743 | &frame->sig); | ||
744 | |||
745 | err |= __put_user(regs->vector, &frame->code); | ||
746 | err |= __put_user(&frame->sc, &frame->psc); | ||
747 | |||
748 | if (_NSIG_WORDS > 1) | ||
749 | err |= copy_to_user(frame->extramask, &set->sig[1], | ||
750 | sizeof(frame->extramask)); | ||
751 | |||
752 | setup_sigcontext(&context, regs, set->sig[0]); | ||
753 | err |= copy_to_user (&frame->sc, &context, sizeof(context)); | ||
754 | |||
755 | /* Set up to return from userspace. */ | ||
756 | err |= __put_user(frame->retcode, &frame->pretcode); | ||
757 | /* moveq #,d0; trap #0 */ | ||
758 | err |= __put_user(0x70004e40 + (__NR_sigreturn << 16), | ||
759 | (long __user *)(frame->retcode)); | ||
760 | |||
761 | if (err) | ||
762 | goto give_sigsegv; | ||
763 | |||
764 | push_cache ((unsigned long) &frame->retcode); | ||
765 | |||
766 | /* | ||
767 | * Set up registers for signal handler. All the state we are about | ||
768 | * to destroy is successfully copied to sigframe. | ||
769 | */ | ||
770 | wrusp ((unsigned long) frame); | ||
771 | regs->pc = (unsigned long) ka->sa.sa_handler; | ||
772 | |||
773 | /* | ||
774 | * This is subtle; if we build more than one sigframe, all but the | ||
775 | * first one will see frame format 0 and have fsize == 0, so we won't | ||
776 | * screw stkadj. | ||
777 | */ | ||
778 | if (fsize) | ||
779 | regs->stkadj = fsize; | ||
780 | |||
781 | /* Prepare to skip over the extra stuff in the exception frame. */ | ||
782 | if (regs->stkadj) { | ||
783 | struct pt_regs *tregs = | ||
784 | (struct pt_regs *)((ulong)regs + regs->stkadj); | ||
785 | #ifdef DEBUG | ||
786 | printk("Performing stackadjust=%04x\n", regs->stkadj); | ||
787 | #endif | ||
788 | /* This must be copied with decreasing addresses to | ||
789 | handle overlaps. */ | ||
790 | tregs->vector = 0; | ||
791 | tregs->format = 0; | ||
792 | tregs->pc = regs->pc; | ||
793 | tregs->sr = regs->sr; | ||
794 | } | ||
795 | return 0; | ||
796 | |||
797 | give_sigsegv: | ||
798 | force_sigsegv(sig, current); | ||
799 | return err; | ||
800 | } | ||
801 | |||
802 | static int setup_rt_frame (int sig, struct k_sigaction *ka, siginfo_t *info, | ||
803 | sigset_t *set, struct pt_regs *regs) | ||
804 | { | ||
805 | struct rt_sigframe __user *frame; | ||
806 | int fsize = frame_extra_sizes[regs->format]; | ||
807 | int err = 0; | ||
808 | |||
809 | if (fsize < 0) { | ||
810 | #ifdef DEBUG | ||
811 | printk ("setup_frame: Unknown frame format %#x\n", | ||
812 | regs->format); | ||
813 | #endif | ||
814 | goto give_sigsegv; | ||
815 | } | ||
816 | |||
817 | frame = get_sigframe(ka, regs, sizeof(*frame)); | ||
818 | |||
819 | if (fsize) | ||
820 | err |= copy_to_user (&frame->uc.uc_extra, regs + 1, fsize); | ||
821 | |||
822 | err |= __put_user((current_thread_info()->exec_domain | ||
823 | && current_thread_info()->exec_domain->signal_invmap | ||
824 | && sig < 32 | ||
825 | ? current_thread_info()->exec_domain->signal_invmap[sig] | ||
826 | : sig), | ||
827 | &frame->sig); | ||
828 | err |= __put_user(&frame->info, &frame->pinfo); | ||
829 | err |= __put_user(&frame->uc, &frame->puc); | ||
830 | err |= copy_siginfo_to_user(&frame->info, info); | ||
831 | |||
832 | /* Create the ucontext. */ | ||
833 | err |= __put_user(0, &frame->uc.uc_flags); | ||
834 | err |= __put_user(NULL, &frame->uc.uc_link); | ||
835 | err |= __put_user((void __user *)current->sas_ss_sp, | ||
836 | &frame->uc.uc_stack.ss_sp); | ||
837 | err |= __put_user(sas_ss_flags(rdusp()), | ||
838 | &frame->uc.uc_stack.ss_flags); | ||
839 | err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); | ||
840 | err |= rt_setup_ucontext(&frame->uc, regs); | ||
841 | err |= copy_to_user (&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
842 | |||
843 | /* Set up to return from userspace. */ | ||
844 | err |= __put_user(frame->retcode, &frame->pretcode); | ||
845 | #ifdef __mcoldfire__ | ||
846 | /* movel #__NR_rt_sigreturn,d0; trap #0 */ | ||
847 | err |= __put_user(0x203c0000, (long __user *)(frame->retcode + 0)); | ||
848 | err |= __put_user(0x00004e40 + (__NR_rt_sigreturn << 16), | ||
849 | (long __user *)(frame->retcode + 4)); | ||
850 | #else | 3 | #else |
851 | /* moveq #,d0; notb d0; trap #0 */ | 4 | #include "signal_no.c" |
852 | err |= __put_user(0x70004600 + ((__NR_rt_sigreturn ^ 0xff) << 16), | ||
853 | (long __user *)(frame->retcode + 0)); | ||
854 | err |= __put_user(0x4e40, (short __user *)(frame->retcode + 4)); | ||
855 | #endif | ||
856 | |||
857 | if (err) | ||
858 | goto give_sigsegv; | ||
859 | |||
860 | push_cache ((unsigned long) &frame->retcode); | ||
861 | |||
862 | /* | ||
863 | * Set up registers for signal handler. All the state we are about | ||
864 | * to destroy is successfully copied to sigframe. | ||
865 | */ | ||
866 | wrusp ((unsigned long) frame); | ||
867 | regs->pc = (unsigned long) ka->sa.sa_handler; | ||
868 | |||
869 | /* | ||
870 | * This is subtle; if we build more than one sigframe, all but the | ||
871 | * first one will see frame format 0 and have fsize == 0, so we won't | ||
872 | * screw stkadj. | ||
873 | */ | ||
874 | if (fsize) | ||
875 | regs->stkadj = fsize; | ||
876 | |||
877 | /* Prepare to skip over the extra stuff in the exception frame. */ | ||
878 | if (regs->stkadj) { | ||
879 | struct pt_regs *tregs = | ||
880 | (struct pt_regs *)((ulong)regs + regs->stkadj); | ||
881 | #ifdef DEBUG | ||
882 | printk("Performing stackadjust=%04x\n", regs->stkadj); | ||
883 | #endif | 5 | #endif |
884 | /* This must be copied with decreasing addresses to | ||
885 | handle overlaps. */ | ||
886 | tregs->vector = 0; | ||
887 | tregs->format = 0; | ||
888 | tregs->pc = regs->pc; | ||
889 | tregs->sr = regs->sr; | ||
890 | } | ||
891 | return 0; | ||
892 | |||
893 | give_sigsegv: | ||
894 | force_sigsegv(sig, current); | ||
895 | return err; | ||
896 | } | ||
897 | |||
898 | static inline void | ||
899 | handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler) | ||
900 | { | ||
901 | switch (regs->d0) { | ||
902 | case -ERESTARTNOHAND: | ||
903 | if (!has_handler) | ||
904 | goto do_restart; | ||
905 | regs->d0 = -EINTR; | ||
906 | break; | ||
907 | |||
908 | case -ERESTART_RESTARTBLOCK: | ||
909 | if (!has_handler) { | ||
910 | regs->d0 = __NR_restart_syscall; | ||
911 | regs->pc -= 2; | ||
912 | break; | ||
913 | } | ||
914 | regs->d0 = -EINTR; | ||
915 | break; | ||
916 | |||
917 | case -ERESTARTSYS: | ||
918 | if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) { | ||
919 | regs->d0 = -EINTR; | ||
920 | break; | ||
921 | } | ||
922 | /* fallthrough */ | ||
923 | case -ERESTARTNOINTR: | ||
924 | do_restart: | ||
925 | regs->d0 = regs->orig_d0; | ||
926 | regs->pc -= 2; | ||
927 | break; | ||
928 | } | ||
929 | } | ||
930 | |||
931 | void ptrace_signal_deliver(struct pt_regs *regs, void *cookie) | ||
932 | { | ||
933 | if (regs->orig_d0 < 0) | ||
934 | return; | ||
935 | switch (regs->d0) { | ||
936 | case -ERESTARTNOHAND: | ||
937 | case -ERESTARTSYS: | ||
938 | case -ERESTARTNOINTR: | ||
939 | regs->d0 = regs->orig_d0; | ||
940 | regs->orig_d0 = -1; | ||
941 | regs->pc -= 2; | ||
942 | break; | ||
943 | } | ||
944 | } | ||
945 | |||
946 | /* | ||
947 | * OK, we're invoking a handler | ||
948 | */ | ||
949 | static void | ||
950 | handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, | ||
951 | sigset_t *oldset, struct pt_regs *regs) | ||
952 | { | ||
953 | int err; | ||
954 | /* are we from a system call? */ | ||
955 | if (regs->orig_d0 >= 0) | ||
956 | /* If so, check system call restarting.. */ | ||
957 | handle_restart(regs, ka, 1); | ||
958 | |||
959 | /* set up the stack frame */ | ||
960 | if (ka->sa.sa_flags & SA_SIGINFO) | ||
961 | err = setup_rt_frame(sig, ka, info, oldset, regs); | ||
962 | else | ||
963 | err = setup_frame(sig, ka, oldset, regs); | ||
964 | |||
965 | if (err) | ||
966 | return; | ||
967 | |||
968 | sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); | ||
969 | if (!(ka->sa.sa_flags & SA_NODEFER)) | ||
970 | sigaddset(¤t->blocked,sig); | ||
971 | recalc_sigpending(); | ||
972 | |||
973 | if (test_thread_flag(TIF_DELAYED_TRACE)) { | ||
974 | regs->sr &= ~0x8000; | ||
975 | send_sig(SIGTRAP, current, 1); | ||
976 | } | ||
977 | |||
978 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
979 | } | ||
980 | |||
981 | /* | ||
982 | * Note that 'init' is a special process: it doesn't get signals it doesn't | ||
983 | * want to handle. Thus you cannot kill init even with a SIGKILL even by | ||
984 | * mistake. | ||
985 | */ | ||
986 | asmlinkage void do_signal(struct pt_regs *regs) | ||
987 | { | ||
988 | siginfo_t info; | ||
989 | struct k_sigaction ka; | ||
990 | int signr; | ||
991 | sigset_t *oldset; | ||
992 | |||
993 | current->thread.esp0 = (unsigned long) regs; | ||
994 | |||
995 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
996 | oldset = ¤t->saved_sigmask; | ||
997 | else | ||
998 | oldset = ¤t->blocked; | ||
999 | |||
1000 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | ||
1001 | if (signr > 0) { | ||
1002 | /* Whee! Actually deliver the signal. */ | ||
1003 | handle_signal(signr, &ka, &info, oldset, regs); | ||
1004 | return; | ||
1005 | } | ||
1006 | |||
1007 | /* Did we come from a system call? */ | ||
1008 | if (regs->orig_d0 >= 0) | ||
1009 | /* Restart the system call - no handlers present */ | ||
1010 | handle_restart(regs, NULL, 0); | ||
1011 | |||
1012 | /* If there's no signal to deliver, we just restore the saved mask. */ | ||
1013 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) { | ||
1014 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
1015 | sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); | ||
1016 | } | ||
1017 | } | ||
diff --git a/arch/m68k/kernel/signal_mm.c b/arch/m68k/kernel/signal_mm.c new file mode 100644 index 000000000000..a0afc239304e --- /dev/null +++ b/arch/m68k/kernel/signal_mm.c | |||
@@ -0,0 +1,1017 @@ | |||
1 | /* | ||
2 | * linux/arch/m68k/kernel/signal.c | ||
3 | * | ||
4 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file COPYING in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * Linux/m68k support by Hamish Macdonald | ||
13 | * | ||
14 | * 68060 fixes by Jesper Skov | ||
15 | * | ||
16 | * 1997-12-01 Modified for POSIX.1b signals by Andreas Schwab | ||
17 | * | ||
18 | * mathemu support by Roman Zippel | ||
19 | * (Note: fpstate in the signal context is completely ignored for the emulator | ||
20 | * and the internal floating point format is put on stack) | ||
21 | */ | ||
22 | |||
23 | /* | ||
24 | * ++roman (07/09/96): implemented signal stacks (specially for tosemu on | ||
25 | * Atari :-) Current limitation: Only one sigstack can be active at one time. | ||
26 | * If a second signal with SA_ONSTACK set arrives while working on a sigstack, | ||
27 | * SA_ONSTACK is ignored. This behaviour avoids lots of trouble with nested | ||
28 | * signal handlers! | ||
29 | */ | ||
30 | |||
31 | #include <linux/sched.h> | ||
32 | #include <linux/mm.h> | ||
33 | #include <linux/kernel.h> | ||
34 | #include <linux/signal.h> | ||
35 | #include <linux/syscalls.h> | ||
36 | #include <linux/errno.h> | ||
37 | #include <linux/wait.h> | ||
38 | #include <linux/ptrace.h> | ||
39 | #include <linux/unistd.h> | ||
40 | #include <linux/stddef.h> | ||
41 | #include <linux/highuid.h> | ||
42 | #include <linux/personality.h> | ||
43 | #include <linux/tty.h> | ||
44 | #include <linux/binfmts.h> | ||
45 | #include <linux/module.h> | ||
46 | |||
47 | #include <asm/setup.h> | ||
48 | #include <asm/uaccess.h> | ||
49 | #include <asm/pgtable.h> | ||
50 | #include <asm/traps.h> | ||
51 | #include <asm/ucontext.h> | ||
52 | |||
53 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | ||
54 | |||
55 | static const int frame_extra_sizes[16] = { | ||
56 | [1] = -1, /* sizeof(((struct frame *)0)->un.fmt1), */ | ||
57 | [2] = sizeof(((struct frame *)0)->un.fmt2), | ||
58 | [3] = sizeof(((struct frame *)0)->un.fmt3), | ||
59 | [4] = sizeof(((struct frame *)0)->un.fmt4), | ||
60 | [5] = -1, /* sizeof(((struct frame *)0)->un.fmt5), */ | ||
61 | [6] = -1, /* sizeof(((struct frame *)0)->un.fmt6), */ | ||
62 | [7] = sizeof(((struct frame *)0)->un.fmt7), | ||
63 | [8] = -1, /* sizeof(((struct frame *)0)->un.fmt8), */ | ||
64 | [9] = sizeof(((struct frame *)0)->un.fmt9), | ||
65 | [10] = sizeof(((struct frame *)0)->un.fmta), | ||
66 | [11] = sizeof(((struct frame *)0)->un.fmtb), | ||
67 | [12] = -1, /* sizeof(((struct frame *)0)->un.fmtc), */ | ||
68 | [13] = -1, /* sizeof(((struct frame *)0)->un.fmtd), */ | ||
69 | [14] = -1, /* sizeof(((struct frame *)0)->un.fmte), */ | ||
70 | [15] = -1, /* sizeof(((struct frame *)0)->un.fmtf), */ | ||
71 | }; | ||
72 | |||
73 | int handle_kernel_fault(struct pt_regs *regs) | ||
74 | { | ||
75 | const struct exception_table_entry *fixup; | ||
76 | struct pt_regs *tregs; | ||
77 | |||
78 | /* Are we prepared to handle this kernel fault? */ | ||
79 | fixup = search_exception_tables(regs->pc); | ||
80 | if (!fixup) | ||
81 | return 0; | ||
82 | |||
83 | /* Create a new four word stack frame, discarding the old one. */ | ||
84 | regs->stkadj = frame_extra_sizes[regs->format]; | ||
85 | tregs = (struct pt_regs *)((long)regs + regs->stkadj); | ||
86 | tregs->vector = regs->vector; | ||
87 | tregs->format = 0; | ||
88 | tregs->pc = fixup->fixup; | ||
89 | tregs->sr = regs->sr; | ||
90 | |||
91 | return 1; | ||
92 | } | ||
93 | |||
94 | /* | ||
95 | * Atomically swap in the new signal mask, and wait for a signal. | ||
96 | */ | ||
97 | asmlinkage int | ||
98 | sys_sigsuspend(int unused0, int unused1, old_sigset_t mask) | ||
99 | { | ||
100 | mask &= _BLOCKABLE; | ||
101 | spin_lock_irq(¤t->sighand->siglock); | ||
102 | current->saved_sigmask = current->blocked; | ||
103 | siginitset(¤t->blocked, mask); | ||
104 | recalc_sigpending(); | ||
105 | spin_unlock_irq(¤t->sighand->siglock); | ||
106 | |||
107 | current->state = TASK_INTERRUPTIBLE; | ||
108 | schedule(); | ||
109 | set_restore_sigmask(); | ||
110 | |||
111 | return -ERESTARTNOHAND; | ||
112 | } | ||
113 | |||
114 | asmlinkage int | ||
115 | sys_sigaction(int sig, const struct old_sigaction __user *act, | ||
116 | struct old_sigaction __user *oact) | ||
117 | { | ||
118 | struct k_sigaction new_ka, old_ka; | ||
119 | int ret; | ||
120 | |||
121 | if (act) { | ||
122 | old_sigset_t mask; | ||
123 | if (!access_ok(VERIFY_READ, act, sizeof(*act)) || | ||
124 | __get_user(new_ka.sa.sa_handler, &act->sa_handler) || | ||
125 | __get_user(new_ka.sa.sa_restorer, &act->sa_restorer) || | ||
126 | __get_user(new_ka.sa.sa_flags, &act->sa_flags) || | ||
127 | __get_user(mask, &act->sa_mask)) | ||
128 | return -EFAULT; | ||
129 | siginitset(&new_ka.sa.sa_mask, mask); | ||
130 | } | ||
131 | |||
132 | ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); | ||
133 | |||
134 | if (!ret && oact) { | ||
135 | if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || | ||
136 | __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || | ||
137 | __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer) || | ||
138 | __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || | ||
139 | __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) | ||
140 | return -EFAULT; | ||
141 | } | ||
142 | |||
143 | return ret; | ||
144 | } | ||
145 | |||
146 | asmlinkage int | ||
147 | sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss) | ||
148 | { | ||
149 | return do_sigaltstack(uss, uoss, rdusp()); | ||
150 | } | ||
151 | |||
152 | |||
153 | /* | ||
154 | * Do a signal return; undo the signal stack. | ||
155 | * | ||
156 | * Keep the return code on the stack quadword aligned! | ||
157 | * That makes the cache flush below easier. | ||
158 | */ | ||
159 | |||
160 | struct sigframe | ||
161 | { | ||
162 | char __user *pretcode; | ||
163 | int sig; | ||
164 | int code; | ||
165 | struct sigcontext __user *psc; | ||
166 | char retcode[8]; | ||
167 | unsigned long extramask[_NSIG_WORDS-1]; | ||
168 | struct sigcontext sc; | ||
169 | }; | ||
170 | |||
171 | struct rt_sigframe | ||
172 | { | ||
173 | char __user *pretcode; | ||
174 | int sig; | ||
175 | struct siginfo __user *pinfo; | ||
176 | void __user *puc; | ||
177 | char retcode[8]; | ||
178 | struct siginfo info; | ||
179 | struct ucontext uc; | ||
180 | }; | ||
181 | |||
182 | |||
183 | static unsigned char fpu_version; /* version number of fpu, set by setup_frame */ | ||
184 | |||
185 | static inline int restore_fpu_state(struct sigcontext *sc) | ||
186 | { | ||
187 | int err = 1; | ||
188 | |||
189 | if (FPU_IS_EMU) { | ||
190 | /* restore registers */ | ||
191 | memcpy(current->thread.fpcntl, sc->sc_fpcntl, 12); | ||
192 | memcpy(current->thread.fp, sc->sc_fpregs, 24); | ||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | if (CPU_IS_060 ? sc->sc_fpstate[2] : sc->sc_fpstate[0]) { | ||
197 | /* Verify the frame format. */ | ||
198 | if (!CPU_IS_060 && (sc->sc_fpstate[0] != fpu_version)) | ||
199 | goto out; | ||
200 | if (CPU_IS_020_OR_030) { | ||
201 | if (m68k_fputype & FPU_68881 && | ||
202 | !(sc->sc_fpstate[1] == 0x18 || sc->sc_fpstate[1] == 0xb4)) | ||
203 | goto out; | ||
204 | if (m68k_fputype & FPU_68882 && | ||
205 | !(sc->sc_fpstate[1] == 0x38 || sc->sc_fpstate[1] == 0xd4)) | ||
206 | goto out; | ||
207 | } else if (CPU_IS_040) { | ||
208 | if (!(sc->sc_fpstate[1] == 0x00 || | ||
209 | sc->sc_fpstate[1] == 0x28 || | ||
210 | sc->sc_fpstate[1] == 0x60)) | ||
211 | goto out; | ||
212 | } else if (CPU_IS_060) { | ||
213 | if (!(sc->sc_fpstate[3] == 0x00 || | ||
214 | sc->sc_fpstate[3] == 0x60 || | ||
215 | sc->sc_fpstate[3] == 0xe0)) | ||
216 | goto out; | ||
217 | } else | ||
218 | goto out; | ||
219 | |||
220 | __asm__ volatile (".chip 68k/68881\n\t" | ||
221 | "fmovemx %0,%%fp0-%%fp1\n\t" | ||
222 | "fmoveml %1,%%fpcr/%%fpsr/%%fpiar\n\t" | ||
223 | ".chip 68k" | ||
224 | : /* no outputs */ | ||
225 | : "m" (*sc->sc_fpregs), "m" (*sc->sc_fpcntl)); | ||
226 | } | ||
227 | __asm__ volatile (".chip 68k/68881\n\t" | ||
228 | "frestore %0\n\t" | ||
229 | ".chip 68k" : : "m" (*sc->sc_fpstate)); | ||
230 | err = 0; | ||
231 | |||
232 | out: | ||
233 | return err; | ||
234 | } | ||
235 | |||
236 | #define FPCONTEXT_SIZE 216 | ||
237 | #define uc_fpstate uc_filler[0] | ||
238 | #define uc_formatvec uc_filler[FPCONTEXT_SIZE/4] | ||
239 | #define uc_extra uc_filler[FPCONTEXT_SIZE/4+1] | ||
240 | |||
241 | static inline int rt_restore_fpu_state(struct ucontext __user *uc) | ||
242 | { | ||
243 | unsigned char fpstate[FPCONTEXT_SIZE]; | ||
244 | int context_size = CPU_IS_060 ? 8 : 0; | ||
245 | fpregset_t fpregs; | ||
246 | int err = 1; | ||
247 | |||
248 | if (FPU_IS_EMU) { | ||
249 | /* restore fpu control register */ | ||
250 | if (__copy_from_user(current->thread.fpcntl, | ||
251 | uc->uc_mcontext.fpregs.f_fpcntl, 12)) | ||
252 | goto out; | ||
253 | /* restore all other fpu register */ | ||
254 | if (__copy_from_user(current->thread.fp, | ||
255 | uc->uc_mcontext.fpregs.f_fpregs, 96)) | ||
256 | goto out; | ||
257 | return 0; | ||
258 | } | ||
259 | |||
260 | if (__get_user(*(long *)fpstate, (long __user *)&uc->uc_fpstate)) | ||
261 | goto out; | ||
262 | if (CPU_IS_060 ? fpstate[2] : fpstate[0]) { | ||
263 | if (!CPU_IS_060) | ||
264 | context_size = fpstate[1]; | ||
265 | /* Verify the frame format. */ | ||
266 | if (!CPU_IS_060 && (fpstate[0] != fpu_version)) | ||
267 | goto out; | ||
268 | if (CPU_IS_020_OR_030) { | ||
269 | if (m68k_fputype & FPU_68881 && | ||
270 | !(context_size == 0x18 || context_size == 0xb4)) | ||
271 | goto out; | ||
272 | if (m68k_fputype & FPU_68882 && | ||
273 | !(context_size == 0x38 || context_size == 0xd4)) | ||
274 | goto out; | ||
275 | } else if (CPU_IS_040) { | ||
276 | if (!(context_size == 0x00 || | ||
277 | context_size == 0x28 || | ||
278 | context_size == 0x60)) | ||
279 | goto out; | ||
280 | } else if (CPU_IS_060) { | ||
281 | if (!(fpstate[3] == 0x00 || | ||
282 | fpstate[3] == 0x60 || | ||
283 | fpstate[3] == 0xe0)) | ||
284 | goto out; | ||
285 | } else | ||
286 | goto out; | ||
287 | if (__copy_from_user(&fpregs, &uc->uc_mcontext.fpregs, | ||
288 | sizeof(fpregs))) | ||
289 | goto out; | ||
290 | __asm__ volatile (".chip 68k/68881\n\t" | ||
291 | "fmovemx %0,%%fp0-%%fp7\n\t" | ||
292 | "fmoveml %1,%%fpcr/%%fpsr/%%fpiar\n\t" | ||
293 | ".chip 68k" | ||
294 | : /* no outputs */ | ||
295 | : "m" (*fpregs.f_fpregs), | ||
296 | "m" (*fpregs.f_fpcntl)); | ||
297 | } | ||
298 | if (context_size && | ||
299 | __copy_from_user(fpstate + 4, (long __user *)&uc->uc_fpstate + 1, | ||
300 | context_size)) | ||
301 | goto out; | ||
302 | __asm__ volatile (".chip 68k/68881\n\t" | ||
303 | "frestore %0\n\t" | ||
304 | ".chip 68k" : : "m" (*fpstate)); | ||
305 | err = 0; | ||
306 | |||
307 | out: | ||
308 | return err; | ||
309 | } | ||
310 | |||
311 | static int mangle_kernel_stack(struct pt_regs *regs, int formatvec, | ||
312 | void __user *fp) | ||
313 | { | ||
314 | int fsize = frame_extra_sizes[formatvec >> 12]; | ||
315 | if (fsize < 0) { | ||
316 | /* | ||
317 | * user process trying to return with weird frame format | ||
318 | */ | ||
319 | #ifdef DEBUG | ||
320 | printk("user process returning with weird frame format\n"); | ||
321 | #endif | ||
322 | return 1; | ||
323 | } | ||
324 | if (!fsize) { | ||
325 | regs->format = formatvec >> 12; | ||
326 | regs->vector = formatvec & 0xfff; | ||
327 | } else { | ||
328 | struct switch_stack *sw = (struct switch_stack *)regs - 1; | ||
329 | unsigned long buf[fsize / 2]; /* yes, twice as much */ | ||
330 | |||
331 | /* that'll make sure that expansion won't crap over data */ | ||
332 | if (copy_from_user(buf + fsize / 4, fp, fsize)) | ||
333 | return 1; | ||
334 | |||
335 | /* point of no return */ | ||
336 | regs->format = formatvec >> 12; | ||
337 | regs->vector = formatvec & 0xfff; | ||
338 | #define frame_offset (sizeof(struct pt_regs)+sizeof(struct switch_stack)) | ||
339 | __asm__ __volatile__ | ||
340 | (" movel %0,%/a0\n\t" | ||
341 | " subl %1,%/a0\n\t" /* make room on stack */ | ||
342 | " movel %/a0,%/sp\n\t" /* set stack pointer */ | ||
343 | /* move switch_stack and pt_regs */ | ||
344 | "1: movel %0@+,%/a0@+\n\t" | ||
345 | " dbra %2,1b\n\t" | ||
346 | " lea %/sp@(%c3),%/a0\n\t" /* add offset of fmt */ | ||
347 | " lsrl #2,%1\n\t" | ||
348 | " subql #1,%1\n\t" | ||
349 | /* copy to the gap we'd made */ | ||
350 | "2: movel %4@+,%/a0@+\n\t" | ||
351 | " dbra %1,2b\n\t" | ||
352 | " bral ret_from_signal\n" | ||
353 | : /* no outputs, it doesn't ever return */ | ||
354 | : "a" (sw), "d" (fsize), "d" (frame_offset/4-1), | ||
355 | "n" (frame_offset), "a" (buf + fsize/4) | ||
356 | : "a0"); | ||
357 | #undef frame_offset | ||
358 | } | ||
359 | return 0; | ||
360 | } | ||
361 | |||
362 | static inline int | ||
363 | restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __user *fp) | ||
364 | { | ||
365 | int formatvec; | ||
366 | struct sigcontext context; | ||
367 | int err; | ||
368 | |||
369 | /* Always make any pending restarted system calls return -EINTR */ | ||
370 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
371 | |||
372 | /* get previous context */ | ||
373 | if (copy_from_user(&context, usc, sizeof(context))) | ||
374 | goto badframe; | ||
375 | |||
376 | /* restore passed registers */ | ||
377 | regs->d0 = context.sc_d0; | ||
378 | regs->d1 = context.sc_d1; | ||
379 | regs->a0 = context.sc_a0; | ||
380 | regs->a1 = context.sc_a1; | ||
381 | regs->sr = (regs->sr & 0xff00) | (context.sc_sr & 0xff); | ||
382 | regs->pc = context.sc_pc; | ||
383 | regs->orig_d0 = -1; /* disable syscall checks */ | ||
384 | wrusp(context.sc_usp); | ||
385 | formatvec = context.sc_formatvec; | ||
386 | |||
387 | err = restore_fpu_state(&context); | ||
388 | |||
389 | if (err || mangle_kernel_stack(regs, formatvec, fp)) | ||
390 | goto badframe; | ||
391 | |||
392 | return 0; | ||
393 | |||
394 | badframe: | ||
395 | return 1; | ||
396 | } | ||
397 | |||
398 | static inline int | ||
399 | rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw, | ||
400 | struct ucontext __user *uc) | ||
401 | { | ||
402 | int temp; | ||
403 | greg_t __user *gregs = uc->uc_mcontext.gregs; | ||
404 | unsigned long usp; | ||
405 | int err; | ||
406 | |||
407 | /* Always make any pending restarted system calls return -EINTR */ | ||
408 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
409 | |||
410 | err = __get_user(temp, &uc->uc_mcontext.version); | ||
411 | if (temp != MCONTEXT_VERSION) | ||
412 | goto badframe; | ||
413 | /* restore passed registers */ | ||
414 | err |= __get_user(regs->d0, &gregs[0]); | ||
415 | err |= __get_user(regs->d1, &gregs[1]); | ||
416 | err |= __get_user(regs->d2, &gregs[2]); | ||
417 | err |= __get_user(regs->d3, &gregs[3]); | ||
418 | err |= __get_user(regs->d4, &gregs[4]); | ||
419 | err |= __get_user(regs->d5, &gregs[5]); | ||
420 | err |= __get_user(sw->d6, &gregs[6]); | ||
421 | err |= __get_user(sw->d7, &gregs[7]); | ||
422 | err |= __get_user(regs->a0, &gregs[8]); | ||
423 | err |= __get_user(regs->a1, &gregs[9]); | ||
424 | err |= __get_user(regs->a2, &gregs[10]); | ||
425 | err |= __get_user(sw->a3, &gregs[11]); | ||
426 | err |= __get_user(sw->a4, &gregs[12]); | ||
427 | err |= __get_user(sw->a5, &gregs[13]); | ||
428 | err |= __get_user(sw->a6, &gregs[14]); | ||
429 | err |= __get_user(usp, &gregs[15]); | ||
430 | wrusp(usp); | ||
431 | err |= __get_user(regs->pc, &gregs[16]); | ||
432 | err |= __get_user(temp, &gregs[17]); | ||
433 | regs->sr = (regs->sr & 0xff00) | (temp & 0xff); | ||
434 | regs->orig_d0 = -1; /* disable syscall checks */ | ||
435 | err |= __get_user(temp, &uc->uc_formatvec); | ||
436 | |||
437 | err |= rt_restore_fpu_state(uc); | ||
438 | |||
439 | if (err || do_sigaltstack(&uc->uc_stack, NULL, usp) == -EFAULT) | ||
440 | goto badframe; | ||
441 | |||
442 | if (mangle_kernel_stack(regs, temp, &uc->uc_extra)) | ||
443 | goto badframe; | ||
444 | |||
445 | return 0; | ||
446 | |||
447 | badframe: | ||
448 | return 1; | ||
449 | } | ||
450 | |||
451 | asmlinkage int do_sigreturn(unsigned long __unused) | ||
452 | { | ||
453 | struct switch_stack *sw = (struct switch_stack *) &__unused; | ||
454 | struct pt_regs *regs = (struct pt_regs *) (sw + 1); | ||
455 | unsigned long usp = rdusp(); | ||
456 | struct sigframe __user *frame = (struct sigframe __user *)(usp - 4); | ||
457 | sigset_t set; | ||
458 | |||
459 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
460 | goto badframe; | ||
461 | if (__get_user(set.sig[0], &frame->sc.sc_mask) || | ||
462 | (_NSIG_WORDS > 1 && | ||
463 | __copy_from_user(&set.sig[1], &frame->extramask, | ||
464 | sizeof(frame->extramask)))) | ||
465 | goto badframe; | ||
466 | |||
467 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
468 | current->blocked = set; | ||
469 | recalc_sigpending(); | ||
470 | |||
471 | if (restore_sigcontext(regs, &frame->sc, frame + 1)) | ||
472 | goto badframe; | ||
473 | return regs->d0; | ||
474 | |||
475 | badframe: | ||
476 | force_sig(SIGSEGV, current); | ||
477 | return 0; | ||
478 | } | ||
479 | |||
480 | asmlinkage int do_rt_sigreturn(unsigned long __unused) | ||
481 | { | ||
482 | struct switch_stack *sw = (struct switch_stack *) &__unused; | ||
483 | struct pt_regs *regs = (struct pt_regs *) (sw + 1); | ||
484 | unsigned long usp = rdusp(); | ||
485 | struct rt_sigframe __user *frame = (struct rt_sigframe __user *)(usp - 4); | ||
486 | sigset_t set; | ||
487 | |||
488 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
489 | goto badframe; | ||
490 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | ||
491 | goto badframe; | ||
492 | |||
493 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
494 | current->blocked = set; | ||
495 | recalc_sigpending(); | ||
496 | |||
497 | if (rt_restore_ucontext(regs, sw, &frame->uc)) | ||
498 | goto badframe; | ||
499 | return regs->d0; | ||
500 | |||
501 | badframe: | ||
502 | force_sig(SIGSEGV, current); | ||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | /* | ||
507 | * Set up a signal frame. | ||
508 | */ | ||
509 | |||
510 | static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs) | ||
511 | { | ||
512 | if (FPU_IS_EMU) { | ||
513 | /* save registers */ | ||
514 | memcpy(sc->sc_fpcntl, current->thread.fpcntl, 12); | ||
515 | memcpy(sc->sc_fpregs, current->thread.fp, 24); | ||
516 | return; | ||
517 | } | ||
518 | |||
519 | __asm__ volatile (".chip 68k/68881\n\t" | ||
520 | "fsave %0\n\t" | ||
521 | ".chip 68k" | ||
522 | : : "m" (*sc->sc_fpstate) : "memory"); | ||
523 | |||
524 | if (CPU_IS_060 ? sc->sc_fpstate[2] : sc->sc_fpstate[0]) { | ||
525 | fpu_version = sc->sc_fpstate[0]; | ||
526 | if (CPU_IS_020_OR_030 && | ||
527 | regs->vector >= (VEC_FPBRUC * 4) && | ||
528 | regs->vector <= (VEC_FPNAN * 4)) { | ||
529 | /* Clear pending exception in 68882 idle frame */ | ||
530 | if (*(unsigned short *) sc->sc_fpstate == 0x1f38) | ||
531 | sc->sc_fpstate[0x38] |= 1 << 3; | ||
532 | } | ||
533 | __asm__ volatile (".chip 68k/68881\n\t" | ||
534 | "fmovemx %%fp0-%%fp1,%0\n\t" | ||
535 | "fmoveml %%fpcr/%%fpsr/%%fpiar,%1\n\t" | ||
536 | ".chip 68k" | ||
537 | : "=m" (*sc->sc_fpregs), | ||
538 | "=m" (*sc->sc_fpcntl) | ||
539 | : /* no inputs */ | ||
540 | : "memory"); | ||
541 | } | ||
542 | } | ||
543 | |||
544 | static inline int rt_save_fpu_state(struct ucontext __user *uc, struct pt_regs *regs) | ||
545 | { | ||
546 | unsigned char fpstate[FPCONTEXT_SIZE]; | ||
547 | int context_size = CPU_IS_060 ? 8 : 0; | ||
548 | int err = 0; | ||
549 | |||
550 | if (FPU_IS_EMU) { | ||
551 | /* save fpu control register */ | ||
552 | err |= copy_to_user(uc->uc_mcontext.fpregs.f_fpcntl, | ||
553 | current->thread.fpcntl, 12); | ||
554 | /* save all other fpu register */ | ||
555 | err |= copy_to_user(uc->uc_mcontext.fpregs.f_fpregs, | ||
556 | current->thread.fp, 96); | ||
557 | return err; | ||
558 | } | ||
559 | |||
560 | __asm__ volatile (".chip 68k/68881\n\t" | ||
561 | "fsave %0\n\t" | ||
562 | ".chip 68k" | ||
563 | : : "m" (*fpstate) : "memory"); | ||
564 | |||
565 | err |= __put_user(*(long *)fpstate, (long __user *)&uc->uc_fpstate); | ||
566 | if (CPU_IS_060 ? fpstate[2] : fpstate[0]) { | ||
567 | fpregset_t fpregs; | ||
568 | if (!CPU_IS_060) | ||
569 | context_size = fpstate[1]; | ||
570 | fpu_version = fpstate[0]; | ||
571 | if (CPU_IS_020_OR_030 && | ||
572 | regs->vector >= (VEC_FPBRUC * 4) && | ||
573 | regs->vector <= (VEC_FPNAN * 4)) { | ||
574 | /* Clear pending exception in 68882 idle frame */ | ||
575 | if (*(unsigned short *) fpstate == 0x1f38) | ||
576 | fpstate[0x38] |= 1 << 3; | ||
577 | } | ||
578 | __asm__ volatile (".chip 68k/68881\n\t" | ||
579 | "fmovemx %%fp0-%%fp7,%0\n\t" | ||
580 | "fmoveml %%fpcr/%%fpsr/%%fpiar,%1\n\t" | ||
581 | ".chip 68k" | ||
582 | : "=m" (*fpregs.f_fpregs), | ||
583 | "=m" (*fpregs.f_fpcntl) | ||
584 | : /* no inputs */ | ||
585 | : "memory"); | ||
586 | err |= copy_to_user(&uc->uc_mcontext.fpregs, &fpregs, | ||
587 | sizeof(fpregs)); | ||
588 | } | ||
589 | if (context_size) | ||
590 | err |= copy_to_user((long __user *)&uc->uc_fpstate + 1, fpstate + 4, | ||
591 | context_size); | ||
592 | return err; | ||
593 | } | ||
594 | |||
595 | static void setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, | ||
596 | unsigned long mask) | ||
597 | { | ||
598 | sc->sc_mask = mask; | ||
599 | sc->sc_usp = rdusp(); | ||
600 | sc->sc_d0 = regs->d0; | ||
601 | sc->sc_d1 = regs->d1; | ||
602 | sc->sc_a0 = regs->a0; | ||
603 | sc->sc_a1 = regs->a1; | ||
604 | sc->sc_sr = regs->sr; | ||
605 | sc->sc_pc = regs->pc; | ||
606 | sc->sc_formatvec = regs->format << 12 | regs->vector; | ||
607 | save_fpu_state(sc, regs); | ||
608 | } | ||
609 | |||
610 | static inline int rt_setup_ucontext(struct ucontext __user *uc, struct pt_regs *regs) | ||
611 | { | ||
612 | struct switch_stack *sw = (struct switch_stack *)regs - 1; | ||
613 | greg_t __user *gregs = uc->uc_mcontext.gregs; | ||
614 | int err = 0; | ||
615 | |||
616 | err |= __put_user(MCONTEXT_VERSION, &uc->uc_mcontext.version); | ||
617 | err |= __put_user(regs->d0, &gregs[0]); | ||
618 | err |= __put_user(regs->d1, &gregs[1]); | ||
619 | err |= __put_user(regs->d2, &gregs[2]); | ||
620 | err |= __put_user(regs->d3, &gregs[3]); | ||
621 | err |= __put_user(regs->d4, &gregs[4]); | ||
622 | err |= __put_user(regs->d5, &gregs[5]); | ||
623 | err |= __put_user(sw->d6, &gregs[6]); | ||
624 | err |= __put_user(sw->d7, &gregs[7]); | ||
625 | err |= __put_user(regs->a0, &gregs[8]); | ||
626 | err |= __put_user(regs->a1, &gregs[9]); | ||
627 | err |= __put_user(regs->a2, &gregs[10]); | ||
628 | err |= __put_user(sw->a3, &gregs[11]); | ||
629 | err |= __put_user(sw->a4, &gregs[12]); | ||
630 | err |= __put_user(sw->a5, &gregs[13]); | ||
631 | err |= __put_user(sw->a6, &gregs[14]); | ||
632 | err |= __put_user(rdusp(), &gregs[15]); | ||
633 | err |= __put_user(regs->pc, &gregs[16]); | ||
634 | err |= __put_user(regs->sr, &gregs[17]); | ||
635 | err |= __put_user((regs->format << 12) | regs->vector, &uc->uc_formatvec); | ||
636 | err |= rt_save_fpu_state(uc, regs); | ||
637 | return err; | ||
638 | } | ||
639 | |||
640 | static inline void push_cache (unsigned long vaddr) | ||
641 | { | ||
642 | /* | ||
643 | * Using the old cache_push_v() was really a big waste. | ||
644 | * | ||
645 | * What we are trying to do is to flush 8 bytes to ram. | ||
646 | * Flushing 2 cache lines of 16 bytes is much cheaper than | ||
647 | * flushing 1 or 2 pages, as previously done in | ||
648 | * cache_push_v(). | ||
649 | * Jes | ||
650 | */ | ||
651 | if (CPU_IS_040) { | ||
652 | unsigned long temp; | ||
653 | |||
654 | __asm__ __volatile__ (".chip 68040\n\t" | ||
655 | "nop\n\t" | ||
656 | "ptestr (%1)\n\t" | ||
657 | "movec %%mmusr,%0\n\t" | ||
658 | ".chip 68k" | ||
659 | : "=r" (temp) | ||
660 | : "a" (vaddr)); | ||
661 | |||
662 | temp &= PAGE_MASK; | ||
663 | temp |= vaddr & ~PAGE_MASK; | ||
664 | |||
665 | __asm__ __volatile__ (".chip 68040\n\t" | ||
666 | "nop\n\t" | ||
667 | "cpushl %%bc,(%0)\n\t" | ||
668 | ".chip 68k" | ||
669 | : : "a" (temp)); | ||
670 | } | ||
671 | else if (CPU_IS_060) { | ||
672 | unsigned long temp; | ||
673 | __asm__ __volatile__ (".chip 68060\n\t" | ||
674 | "plpar (%0)\n\t" | ||
675 | ".chip 68k" | ||
676 | : "=a" (temp) | ||
677 | : "0" (vaddr)); | ||
678 | __asm__ __volatile__ (".chip 68060\n\t" | ||
679 | "cpushl %%bc,(%0)\n\t" | ||
680 | ".chip 68k" | ||
681 | : : "a" (temp)); | ||
682 | } | ||
683 | else { | ||
684 | /* | ||
685 | * 68030/68020 have no writeback cache; | ||
686 | * still need to clear icache. | ||
687 | * Note that vaddr is guaranteed to be long word aligned. | ||
688 | */ | ||
689 | unsigned long temp; | ||
690 | asm volatile ("movec %%cacr,%0" : "=r" (temp)); | ||
691 | temp += 4; | ||
692 | asm volatile ("movec %0,%%caar\n\t" | ||
693 | "movec %1,%%cacr" | ||
694 | : : "r" (vaddr), "r" (temp)); | ||
695 | asm volatile ("movec %0,%%caar\n\t" | ||
696 | "movec %1,%%cacr" | ||
697 | : : "r" (vaddr + 4), "r" (temp)); | ||
698 | } | ||
699 | } | ||
700 | |||
701 | static inline void __user * | ||
702 | get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) | ||
703 | { | ||
704 | unsigned long usp; | ||
705 | |||
706 | /* Default to using normal stack. */ | ||
707 | usp = rdusp(); | ||
708 | |||
709 | /* This is the X/Open sanctioned signal stack switching. */ | ||
710 | if (ka->sa.sa_flags & SA_ONSTACK) { | ||
711 | if (!sas_ss_flags(usp)) | ||
712 | usp = current->sas_ss_sp + current->sas_ss_size; | ||
713 | } | ||
714 | return (void __user *)((usp - frame_size) & -8UL); | ||
715 | } | ||
716 | |||
717 | static int setup_frame (int sig, struct k_sigaction *ka, | ||
718 | sigset_t *set, struct pt_regs *regs) | ||
719 | { | ||
720 | struct sigframe __user *frame; | ||
721 | int fsize = frame_extra_sizes[regs->format]; | ||
722 | struct sigcontext context; | ||
723 | int err = 0; | ||
724 | |||
725 | if (fsize < 0) { | ||
726 | #ifdef DEBUG | ||
727 | printk ("setup_frame: Unknown frame format %#x\n", | ||
728 | regs->format); | ||
729 | #endif | ||
730 | goto give_sigsegv; | ||
731 | } | ||
732 | |||
733 | frame = get_sigframe(ka, regs, sizeof(*frame) + fsize); | ||
734 | |||
735 | if (fsize) | ||
736 | err |= copy_to_user (frame + 1, regs + 1, fsize); | ||
737 | |||
738 | err |= __put_user((current_thread_info()->exec_domain | ||
739 | && current_thread_info()->exec_domain->signal_invmap | ||
740 | && sig < 32 | ||
741 | ? current_thread_info()->exec_domain->signal_invmap[sig] | ||
742 | : sig), | ||
743 | &frame->sig); | ||
744 | |||
745 | err |= __put_user(regs->vector, &frame->code); | ||
746 | err |= __put_user(&frame->sc, &frame->psc); | ||
747 | |||
748 | if (_NSIG_WORDS > 1) | ||
749 | err |= copy_to_user(frame->extramask, &set->sig[1], | ||
750 | sizeof(frame->extramask)); | ||
751 | |||
752 | setup_sigcontext(&context, regs, set->sig[0]); | ||
753 | err |= copy_to_user (&frame->sc, &context, sizeof(context)); | ||
754 | |||
755 | /* Set up to return from userspace. */ | ||
756 | err |= __put_user(frame->retcode, &frame->pretcode); | ||
757 | /* moveq #,d0; trap #0 */ | ||
758 | err |= __put_user(0x70004e40 + (__NR_sigreturn << 16), | ||
759 | (long __user *)(frame->retcode)); | ||
760 | |||
761 | if (err) | ||
762 | goto give_sigsegv; | ||
763 | |||
764 | push_cache ((unsigned long) &frame->retcode); | ||
765 | |||
766 | /* | ||
767 | * Set up registers for signal handler. All the state we are about | ||
768 | * to destroy is successfully copied to sigframe. | ||
769 | */ | ||
770 | wrusp ((unsigned long) frame); | ||
771 | regs->pc = (unsigned long) ka->sa.sa_handler; | ||
772 | |||
773 | /* | ||
774 | * This is subtle; if we build more than one sigframe, all but the | ||
775 | * first one will see frame format 0 and have fsize == 0, so we won't | ||
776 | * screw stkadj. | ||
777 | */ | ||
778 | if (fsize) | ||
779 | regs->stkadj = fsize; | ||
780 | |||
781 | /* Prepare to skip over the extra stuff in the exception frame. */ | ||
782 | if (regs->stkadj) { | ||
783 | struct pt_regs *tregs = | ||
784 | (struct pt_regs *)((ulong)regs + regs->stkadj); | ||
785 | #ifdef DEBUG | ||
786 | printk("Performing stackadjust=%04x\n", regs->stkadj); | ||
787 | #endif | ||
788 | /* This must be copied with decreasing addresses to | ||
789 | handle overlaps. */ | ||
790 | tregs->vector = 0; | ||
791 | tregs->format = 0; | ||
792 | tregs->pc = regs->pc; | ||
793 | tregs->sr = regs->sr; | ||
794 | } | ||
795 | return 0; | ||
796 | |||
797 | give_sigsegv: | ||
798 | force_sigsegv(sig, current); | ||
799 | return err; | ||
800 | } | ||
801 | |||
802 | static int setup_rt_frame (int sig, struct k_sigaction *ka, siginfo_t *info, | ||
803 | sigset_t *set, struct pt_regs *regs) | ||
804 | { | ||
805 | struct rt_sigframe __user *frame; | ||
806 | int fsize = frame_extra_sizes[regs->format]; | ||
807 | int err = 0; | ||
808 | |||
809 | if (fsize < 0) { | ||
810 | #ifdef DEBUG | ||
811 | printk ("setup_frame: Unknown frame format %#x\n", | ||
812 | regs->format); | ||
813 | #endif | ||
814 | goto give_sigsegv; | ||
815 | } | ||
816 | |||
817 | frame = get_sigframe(ka, regs, sizeof(*frame)); | ||
818 | |||
819 | if (fsize) | ||
820 | err |= copy_to_user (&frame->uc.uc_extra, regs + 1, fsize); | ||
821 | |||
822 | err |= __put_user((current_thread_info()->exec_domain | ||
823 | && current_thread_info()->exec_domain->signal_invmap | ||
824 | && sig < 32 | ||
825 | ? current_thread_info()->exec_domain->signal_invmap[sig] | ||
826 | : sig), | ||
827 | &frame->sig); | ||
828 | err |= __put_user(&frame->info, &frame->pinfo); | ||
829 | err |= __put_user(&frame->uc, &frame->puc); | ||
830 | err |= copy_siginfo_to_user(&frame->info, info); | ||
831 | |||
832 | /* Create the ucontext. */ | ||
833 | err |= __put_user(0, &frame->uc.uc_flags); | ||
834 | err |= __put_user(NULL, &frame->uc.uc_link); | ||
835 | err |= __put_user((void __user *)current->sas_ss_sp, | ||
836 | &frame->uc.uc_stack.ss_sp); | ||
837 | err |= __put_user(sas_ss_flags(rdusp()), | ||
838 | &frame->uc.uc_stack.ss_flags); | ||
839 | err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); | ||
840 | err |= rt_setup_ucontext(&frame->uc, regs); | ||
841 | err |= copy_to_user (&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
842 | |||
843 | /* Set up to return from userspace. */ | ||
844 | err |= __put_user(frame->retcode, &frame->pretcode); | ||
845 | #ifdef __mcoldfire__ | ||
846 | /* movel #__NR_rt_sigreturn,d0; trap #0 */ | ||
847 | err |= __put_user(0x203c0000, (long __user *)(frame->retcode + 0)); | ||
848 | err |= __put_user(0x00004e40 + (__NR_rt_sigreturn << 16), | ||
849 | (long __user *)(frame->retcode + 4)); | ||
850 | #else | ||
851 | /* moveq #,d0; notb d0; trap #0 */ | ||
852 | err |= __put_user(0x70004600 + ((__NR_rt_sigreturn ^ 0xff) << 16), | ||
853 | (long __user *)(frame->retcode + 0)); | ||
854 | err |= __put_user(0x4e40, (short __user *)(frame->retcode + 4)); | ||
855 | #endif | ||
856 | |||
857 | if (err) | ||
858 | goto give_sigsegv; | ||
859 | |||
860 | push_cache ((unsigned long) &frame->retcode); | ||
861 | |||
862 | /* | ||
863 | * Set up registers for signal handler. All the state we are about | ||
864 | * to destroy is successfully copied to sigframe. | ||
865 | */ | ||
866 | wrusp ((unsigned long) frame); | ||
867 | regs->pc = (unsigned long) ka->sa.sa_handler; | ||
868 | |||
869 | /* | ||
870 | * This is subtle; if we build more than one sigframe, all but the | ||
871 | * first one will see frame format 0 and have fsize == 0, so we won't | ||
872 | * screw stkadj. | ||
873 | */ | ||
874 | if (fsize) | ||
875 | regs->stkadj = fsize; | ||
876 | |||
877 | /* Prepare to skip over the extra stuff in the exception frame. */ | ||
878 | if (regs->stkadj) { | ||
879 | struct pt_regs *tregs = | ||
880 | (struct pt_regs *)((ulong)regs + regs->stkadj); | ||
881 | #ifdef DEBUG | ||
882 | printk("Performing stackadjust=%04x\n", regs->stkadj); | ||
883 | #endif | ||
884 | /* This must be copied with decreasing addresses to | ||
885 | handle overlaps. */ | ||
886 | tregs->vector = 0; | ||
887 | tregs->format = 0; | ||
888 | tregs->pc = regs->pc; | ||
889 | tregs->sr = regs->sr; | ||
890 | } | ||
891 | return 0; | ||
892 | |||
893 | give_sigsegv: | ||
894 | force_sigsegv(sig, current); | ||
895 | return err; | ||
896 | } | ||
897 | |||
898 | static inline void | ||
899 | handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler) | ||
900 | { | ||
901 | switch (regs->d0) { | ||
902 | case -ERESTARTNOHAND: | ||
903 | if (!has_handler) | ||
904 | goto do_restart; | ||
905 | regs->d0 = -EINTR; | ||
906 | break; | ||
907 | |||
908 | case -ERESTART_RESTARTBLOCK: | ||
909 | if (!has_handler) { | ||
910 | regs->d0 = __NR_restart_syscall; | ||
911 | regs->pc -= 2; | ||
912 | break; | ||
913 | } | ||
914 | regs->d0 = -EINTR; | ||
915 | break; | ||
916 | |||
917 | case -ERESTARTSYS: | ||
918 | if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) { | ||
919 | regs->d0 = -EINTR; | ||
920 | break; | ||
921 | } | ||
922 | /* fallthrough */ | ||
923 | case -ERESTARTNOINTR: | ||
924 | do_restart: | ||
925 | regs->d0 = regs->orig_d0; | ||
926 | regs->pc -= 2; | ||
927 | break; | ||
928 | } | ||
929 | } | ||
930 | |||
931 | void ptrace_signal_deliver(struct pt_regs *regs, void *cookie) | ||
932 | { | ||
933 | if (regs->orig_d0 < 0) | ||
934 | return; | ||
935 | switch (regs->d0) { | ||
936 | case -ERESTARTNOHAND: | ||
937 | case -ERESTARTSYS: | ||
938 | case -ERESTARTNOINTR: | ||
939 | regs->d0 = regs->orig_d0; | ||
940 | regs->orig_d0 = -1; | ||
941 | regs->pc -= 2; | ||
942 | break; | ||
943 | } | ||
944 | } | ||
945 | |||
946 | /* | ||
947 | * OK, we're invoking a handler | ||
948 | */ | ||
949 | static void | ||
950 | handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, | ||
951 | sigset_t *oldset, struct pt_regs *regs) | ||
952 | { | ||
953 | int err; | ||
954 | /* are we from a system call? */ | ||
955 | if (regs->orig_d0 >= 0) | ||
956 | /* If so, check system call restarting.. */ | ||
957 | handle_restart(regs, ka, 1); | ||
958 | |||
959 | /* set up the stack frame */ | ||
960 | if (ka->sa.sa_flags & SA_SIGINFO) | ||
961 | err = setup_rt_frame(sig, ka, info, oldset, regs); | ||
962 | else | ||
963 | err = setup_frame(sig, ka, oldset, regs); | ||
964 | |||
965 | if (err) | ||
966 | return; | ||
967 | |||
968 | sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); | ||
969 | if (!(ka->sa.sa_flags & SA_NODEFER)) | ||
970 | sigaddset(¤t->blocked,sig); | ||
971 | recalc_sigpending(); | ||
972 | |||
973 | if (test_thread_flag(TIF_DELAYED_TRACE)) { | ||
974 | regs->sr &= ~0x8000; | ||
975 | send_sig(SIGTRAP, current, 1); | ||
976 | } | ||
977 | |||
978 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
979 | } | ||
980 | |||
981 | /* | ||
982 | * Note that 'init' is a special process: it doesn't get signals it doesn't | ||
983 | * want to handle. Thus you cannot kill init even with a SIGKILL even by | ||
984 | * mistake. | ||
985 | */ | ||
986 | asmlinkage void do_signal(struct pt_regs *regs) | ||
987 | { | ||
988 | siginfo_t info; | ||
989 | struct k_sigaction ka; | ||
990 | int signr; | ||
991 | sigset_t *oldset; | ||
992 | |||
993 | current->thread.esp0 = (unsigned long) regs; | ||
994 | |||
995 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
996 | oldset = ¤t->saved_sigmask; | ||
997 | else | ||
998 | oldset = ¤t->blocked; | ||
999 | |||
1000 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | ||
1001 | if (signr > 0) { | ||
1002 | /* Whee! Actually deliver the signal. */ | ||
1003 | handle_signal(signr, &ka, &info, oldset, regs); | ||
1004 | return; | ||
1005 | } | ||
1006 | |||
1007 | /* Did we come from a system call? */ | ||
1008 | if (regs->orig_d0 >= 0) | ||
1009 | /* Restart the system call - no handlers present */ | ||
1010 | handle_restart(regs, NULL, 0); | ||
1011 | |||
1012 | /* If there's no signal to deliver, we just restore the saved mask. */ | ||
1013 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) { | ||
1014 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
1015 | sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); | ||
1016 | } | ||
1017 | } | ||
diff --git a/arch/m68k/kernel/signal_no.c b/arch/m68k/kernel/signal_no.c new file mode 100644 index 000000000000..36a81bb6835a --- /dev/null +++ b/arch/m68k/kernel/signal_no.c | |||
@@ -0,0 +1,765 @@ | |||
1 | /* | ||
2 | * linux/arch/m68knommu/kernel/signal.c | ||
3 | * | ||
4 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General Public | ||
7 | * License. See the file COPYING in the main directory of this archive | ||
8 | * for more details. | ||
9 | */ | ||
10 | |||
11 | /* | ||
12 | * Linux/m68k support by Hamish Macdonald | ||
13 | * | ||
14 | * 68060 fixes by Jesper Skov | ||
15 | * | ||
16 | * 1997-12-01 Modified for POSIX.1b signals by Andreas Schwab | ||
17 | * | ||
18 | * mathemu support by Roman Zippel | ||
19 | * (Note: fpstate in the signal context is completely ignored for the emulator | ||
20 | * and the internal floating point format is put on stack) | ||
21 | */ | ||
22 | |||
23 | /* | ||
24 | * ++roman (07/09/96): implemented signal stacks (specially for tosemu on | ||
25 | * Atari :-) Current limitation: Only one sigstack can be active at one time. | ||
26 | * If a second signal with SA_ONSTACK set arrives while working on a sigstack, | ||
27 | * SA_ONSTACK is ignored. This behaviour avoids lots of trouble with nested | ||
28 | * signal handlers! | ||
29 | */ | ||
30 | |||
31 | #include <linux/sched.h> | ||
32 | #include <linux/mm.h> | ||
33 | #include <linux/kernel.h> | ||
34 | #include <linux/signal.h> | ||
35 | #include <linux/syscalls.h> | ||
36 | #include <linux/errno.h> | ||
37 | #include <linux/wait.h> | ||
38 | #include <linux/ptrace.h> | ||
39 | #include <linux/unistd.h> | ||
40 | #include <linux/stddef.h> | ||
41 | #include <linux/highuid.h> | ||
42 | #include <linux/tty.h> | ||
43 | #include <linux/personality.h> | ||
44 | #include <linux/binfmts.h> | ||
45 | |||
46 | #include <asm/setup.h> | ||
47 | #include <asm/uaccess.h> | ||
48 | #include <asm/pgtable.h> | ||
49 | #include <asm/traps.h> | ||
50 | #include <asm/ucontext.h> | ||
51 | |||
52 | #define _BLOCKABLE (~(sigmask(SIGKILL) | sigmask(SIGSTOP))) | ||
53 | |||
54 | void ret_from_user_signal(void); | ||
55 | void ret_from_user_rt_signal(void); | ||
56 | |||
57 | /* | ||
58 | * Atomically swap in the new signal mask, and wait for a signal. | ||
59 | */ | ||
60 | asmlinkage int | ||
61 | sys_sigsuspend(int unused0, int unused1, old_sigset_t mask) | ||
62 | { | ||
63 | mask &= _BLOCKABLE; | ||
64 | spin_lock_irq(¤t->sighand->siglock); | ||
65 | current->saved_sigmask = current->blocked; | ||
66 | siginitset(¤t->blocked, mask); | ||
67 | recalc_sigpending(); | ||
68 | spin_unlock_irq(¤t->sighand->siglock); | ||
69 | |||
70 | current->state = TASK_INTERRUPTIBLE; | ||
71 | schedule(); | ||
72 | set_restore_sigmask(); | ||
73 | |||
74 | return -ERESTARTNOHAND; | ||
75 | } | ||
76 | |||
77 | asmlinkage int | ||
78 | sys_sigaction(int sig, const struct old_sigaction __user *act, | ||
79 | struct old_sigaction __user *oact) | ||
80 | { | ||
81 | struct k_sigaction new_ka, old_ka; | ||
82 | int ret; | ||
83 | |||
84 | if (act) { | ||
85 | old_sigset_t mask; | ||
86 | if (!access_ok(VERIFY_READ, act, sizeof(*act)) || | ||
87 | __get_user(new_ka.sa.sa_handler, &act->sa_handler) || | ||
88 | __get_user(new_ka.sa.sa_restorer, &act->sa_restorer) || | ||
89 | __get_user(new_ka.sa.sa_flags, &act->sa_flags) || | ||
90 | __get_user(mask, &act->sa_mask)) | ||
91 | return -EFAULT; | ||
92 | siginitset(&new_ka.sa.sa_mask, mask); | ||
93 | } | ||
94 | |||
95 | ret = do_sigaction(sig, act ? &new_ka : NULL, oact ? &old_ka : NULL); | ||
96 | |||
97 | if (!ret && oact) { | ||
98 | if (!access_ok(VERIFY_WRITE, oact, sizeof(*oact)) || | ||
99 | __put_user(old_ka.sa.sa_handler, &oact->sa_handler) || | ||
100 | __put_user(old_ka.sa.sa_restorer, &oact->sa_restorer) || | ||
101 | __put_user(old_ka.sa.sa_flags, &oact->sa_flags) || | ||
102 | __put_user(old_ka.sa.sa_mask.sig[0], &oact->sa_mask)) | ||
103 | return -EFAULT; | ||
104 | } | ||
105 | |||
106 | return ret; | ||
107 | } | ||
108 | |||
109 | asmlinkage int | ||
110 | sys_sigaltstack(const stack_t __user *uss, stack_t __user *uoss) | ||
111 | { | ||
112 | return do_sigaltstack(uss, uoss, rdusp()); | ||
113 | } | ||
114 | |||
115 | |||
116 | /* | ||
117 | * Do a signal return; undo the signal stack. | ||
118 | * | ||
119 | * Keep the return code on the stack quadword aligned! | ||
120 | * That makes the cache flush below easier. | ||
121 | */ | ||
122 | |||
123 | struct sigframe | ||
124 | { | ||
125 | char __user *pretcode; | ||
126 | int sig; | ||
127 | int code; | ||
128 | struct sigcontext __user *psc; | ||
129 | char retcode[8]; | ||
130 | unsigned long extramask[_NSIG_WORDS-1]; | ||
131 | struct sigcontext sc; | ||
132 | }; | ||
133 | |||
134 | struct rt_sigframe | ||
135 | { | ||
136 | char __user *pretcode; | ||
137 | int sig; | ||
138 | struct siginfo __user *pinfo; | ||
139 | void __user *puc; | ||
140 | char retcode[8]; | ||
141 | struct siginfo info; | ||
142 | struct ucontext uc; | ||
143 | }; | ||
144 | |||
145 | #ifdef CONFIG_FPU | ||
146 | |||
147 | static unsigned char fpu_version = 0; /* version number of fpu, set by setup_frame */ | ||
148 | |||
149 | static inline int restore_fpu_state(struct sigcontext *sc) | ||
150 | { | ||
151 | int err = 1; | ||
152 | |||
153 | if (FPU_IS_EMU) { | ||
154 | /* restore registers */ | ||
155 | memcpy(current->thread.fpcntl, sc->sc_fpcntl, 12); | ||
156 | memcpy(current->thread.fp, sc->sc_fpregs, 24); | ||
157 | return 0; | ||
158 | } | ||
159 | |||
160 | if (sc->sc_fpstate[0]) { | ||
161 | /* Verify the frame format. */ | ||
162 | if (sc->sc_fpstate[0] != fpu_version) | ||
163 | goto out; | ||
164 | |||
165 | __asm__ volatile (".chip 68k/68881\n\t" | ||
166 | "fmovemx %0,%%fp0-%%fp1\n\t" | ||
167 | "fmoveml %1,%%fpcr/%%fpsr/%%fpiar\n\t" | ||
168 | ".chip 68k" | ||
169 | : /* no outputs */ | ||
170 | : "m" (*sc->sc_fpregs), "m" (*sc->sc_fpcntl)); | ||
171 | } | ||
172 | __asm__ volatile (".chip 68k/68881\n\t" | ||
173 | "frestore %0\n\t" | ||
174 | ".chip 68k" : : "m" (*sc->sc_fpstate)); | ||
175 | err = 0; | ||
176 | |||
177 | out: | ||
178 | return err; | ||
179 | } | ||
180 | |||
181 | #define FPCONTEXT_SIZE 216 | ||
182 | #define uc_fpstate uc_filler[0] | ||
183 | #define uc_formatvec uc_filler[FPCONTEXT_SIZE/4] | ||
184 | #define uc_extra uc_filler[FPCONTEXT_SIZE/4+1] | ||
185 | |||
186 | static inline int rt_restore_fpu_state(struct ucontext __user *uc) | ||
187 | { | ||
188 | unsigned char fpstate[FPCONTEXT_SIZE]; | ||
189 | int context_size = 0; | ||
190 | fpregset_t fpregs; | ||
191 | int err = 1; | ||
192 | |||
193 | if (FPU_IS_EMU) { | ||
194 | /* restore fpu control register */ | ||
195 | if (__copy_from_user(current->thread.fpcntl, | ||
196 | uc->uc_mcontext.fpregs.f_fpcntl, 12)) | ||
197 | goto out; | ||
198 | /* restore all other fpu register */ | ||
199 | if (__copy_from_user(current->thread.fp, | ||
200 | uc->uc_mcontext.fpregs.f_fpregs, 96)) | ||
201 | goto out; | ||
202 | return 0; | ||
203 | } | ||
204 | |||
205 | if (__get_user(*(long *)fpstate, (long __user *)&uc->uc_fpstate)) | ||
206 | goto out; | ||
207 | if (fpstate[0]) { | ||
208 | context_size = fpstate[1]; | ||
209 | |||
210 | /* Verify the frame format. */ | ||
211 | if (fpstate[0] != fpu_version) | ||
212 | goto out; | ||
213 | if (__copy_from_user(&fpregs, &uc->uc_mcontext.fpregs, | ||
214 | sizeof(fpregs))) | ||
215 | goto out; | ||
216 | __asm__ volatile (".chip 68k/68881\n\t" | ||
217 | "fmovemx %0,%%fp0-%%fp7\n\t" | ||
218 | "fmoveml %1,%%fpcr/%%fpsr/%%fpiar\n\t" | ||
219 | ".chip 68k" | ||
220 | : /* no outputs */ | ||
221 | : "m" (*fpregs.f_fpregs), | ||
222 | "m" (*fpregs.f_fpcntl)); | ||
223 | } | ||
224 | if (context_size && | ||
225 | __copy_from_user(fpstate + 4, (long __user *)&uc->uc_fpstate + 1, | ||
226 | context_size)) | ||
227 | goto out; | ||
228 | __asm__ volatile (".chip 68k/68881\n\t" | ||
229 | "frestore %0\n\t" | ||
230 | ".chip 68k" : : "m" (*fpstate)); | ||
231 | err = 0; | ||
232 | |||
233 | out: | ||
234 | return err; | ||
235 | } | ||
236 | |||
237 | #endif | ||
238 | |||
239 | static inline int | ||
240 | restore_sigcontext(struct pt_regs *regs, struct sigcontext __user *usc, void __user *fp, | ||
241 | int *pd0) | ||
242 | { | ||
243 | int formatvec; | ||
244 | struct sigcontext context; | ||
245 | int err = 0; | ||
246 | |||
247 | /* Always make any pending restarted system calls return -EINTR */ | ||
248 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
249 | |||
250 | /* get previous context */ | ||
251 | if (copy_from_user(&context, usc, sizeof(context))) | ||
252 | goto badframe; | ||
253 | |||
254 | /* restore passed registers */ | ||
255 | regs->d1 = context.sc_d1; | ||
256 | regs->a0 = context.sc_a0; | ||
257 | regs->a1 = context.sc_a1; | ||
258 | ((struct switch_stack *)regs - 1)->a5 = context.sc_a5; | ||
259 | regs->sr = (regs->sr & 0xff00) | (context.sc_sr & 0xff); | ||
260 | regs->pc = context.sc_pc; | ||
261 | regs->orig_d0 = -1; /* disable syscall checks */ | ||
262 | wrusp(context.sc_usp); | ||
263 | formatvec = context.sc_formatvec; | ||
264 | regs->format = formatvec >> 12; | ||
265 | regs->vector = formatvec & 0xfff; | ||
266 | |||
267 | #ifdef CONFIG_FPU | ||
268 | err = restore_fpu_state(&context); | ||
269 | #endif | ||
270 | |||
271 | *pd0 = context.sc_d0; | ||
272 | return err; | ||
273 | |||
274 | badframe: | ||
275 | return 1; | ||
276 | } | ||
277 | |||
278 | static inline int | ||
279 | rt_restore_ucontext(struct pt_regs *regs, struct switch_stack *sw, | ||
280 | struct ucontext __user *uc, int *pd0) | ||
281 | { | ||
282 | int temp; | ||
283 | greg_t __user *gregs = uc->uc_mcontext.gregs; | ||
284 | unsigned long usp; | ||
285 | int err; | ||
286 | |||
287 | /* Always make any pending restarted system calls return -EINTR */ | ||
288 | current_thread_info()->restart_block.fn = do_no_restart_syscall; | ||
289 | |||
290 | err = __get_user(temp, &uc->uc_mcontext.version); | ||
291 | if (temp != MCONTEXT_VERSION) | ||
292 | goto badframe; | ||
293 | /* restore passed registers */ | ||
294 | err |= __get_user(regs->d0, &gregs[0]); | ||
295 | err |= __get_user(regs->d1, &gregs[1]); | ||
296 | err |= __get_user(regs->d2, &gregs[2]); | ||
297 | err |= __get_user(regs->d3, &gregs[3]); | ||
298 | err |= __get_user(regs->d4, &gregs[4]); | ||
299 | err |= __get_user(regs->d5, &gregs[5]); | ||
300 | err |= __get_user(sw->d6, &gregs[6]); | ||
301 | err |= __get_user(sw->d7, &gregs[7]); | ||
302 | err |= __get_user(regs->a0, &gregs[8]); | ||
303 | err |= __get_user(regs->a1, &gregs[9]); | ||
304 | err |= __get_user(regs->a2, &gregs[10]); | ||
305 | err |= __get_user(sw->a3, &gregs[11]); | ||
306 | err |= __get_user(sw->a4, &gregs[12]); | ||
307 | err |= __get_user(sw->a5, &gregs[13]); | ||
308 | err |= __get_user(sw->a6, &gregs[14]); | ||
309 | err |= __get_user(usp, &gregs[15]); | ||
310 | wrusp(usp); | ||
311 | err |= __get_user(regs->pc, &gregs[16]); | ||
312 | err |= __get_user(temp, &gregs[17]); | ||
313 | regs->sr = (regs->sr & 0xff00) | (temp & 0xff); | ||
314 | regs->orig_d0 = -1; /* disable syscall checks */ | ||
315 | regs->format = temp >> 12; | ||
316 | regs->vector = temp & 0xfff; | ||
317 | |||
318 | if (do_sigaltstack(&uc->uc_stack, NULL, usp) == -EFAULT) | ||
319 | goto badframe; | ||
320 | |||
321 | *pd0 = regs->d0; | ||
322 | return err; | ||
323 | |||
324 | badframe: | ||
325 | return 1; | ||
326 | } | ||
327 | |||
328 | asmlinkage int do_sigreturn(unsigned long __unused) | ||
329 | { | ||
330 | struct switch_stack *sw = (struct switch_stack *) &__unused; | ||
331 | struct pt_regs *regs = (struct pt_regs *) (sw + 1); | ||
332 | unsigned long usp = rdusp(); | ||
333 | struct sigframe __user *frame = (struct sigframe __user *)(usp - 4); | ||
334 | sigset_t set; | ||
335 | int d0; | ||
336 | |||
337 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
338 | goto badframe; | ||
339 | if (__get_user(set.sig[0], &frame->sc.sc_mask) || | ||
340 | (_NSIG_WORDS > 1 && | ||
341 | __copy_from_user(&set.sig[1], &frame->extramask, | ||
342 | sizeof(frame->extramask)))) | ||
343 | goto badframe; | ||
344 | |||
345 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
346 | spin_lock_irq(¤t->sighand->siglock); | ||
347 | current->blocked = set; | ||
348 | recalc_sigpending(); | ||
349 | spin_unlock_irq(¤t->sighand->siglock); | ||
350 | |||
351 | if (restore_sigcontext(regs, &frame->sc, frame + 1, &d0)) | ||
352 | goto badframe; | ||
353 | return d0; | ||
354 | |||
355 | badframe: | ||
356 | force_sig(SIGSEGV, current); | ||
357 | return 0; | ||
358 | } | ||
359 | |||
360 | asmlinkage int do_rt_sigreturn(unsigned long __unused) | ||
361 | { | ||
362 | struct switch_stack *sw = (struct switch_stack *) &__unused; | ||
363 | struct pt_regs *regs = (struct pt_regs *) (sw + 1); | ||
364 | unsigned long usp = rdusp(); | ||
365 | struct rt_sigframe __user *frame = (struct rt_sigframe __user *)(usp - 4); | ||
366 | sigset_t set; | ||
367 | int d0; | ||
368 | |||
369 | if (!access_ok(VERIFY_READ, frame, sizeof(*frame))) | ||
370 | goto badframe; | ||
371 | if (__copy_from_user(&set, &frame->uc.uc_sigmask, sizeof(set))) | ||
372 | goto badframe; | ||
373 | |||
374 | sigdelsetmask(&set, ~_BLOCKABLE); | ||
375 | spin_lock_irq(¤t->sighand->siglock); | ||
376 | current->blocked = set; | ||
377 | recalc_sigpending(); | ||
378 | spin_unlock_irq(¤t->sighand->siglock); | ||
379 | |||
380 | if (rt_restore_ucontext(regs, sw, &frame->uc, &d0)) | ||
381 | goto badframe; | ||
382 | return d0; | ||
383 | |||
384 | badframe: | ||
385 | force_sig(SIGSEGV, current); | ||
386 | return 0; | ||
387 | } | ||
388 | |||
389 | #ifdef CONFIG_FPU | ||
390 | /* | ||
391 | * Set up a signal frame. | ||
392 | */ | ||
393 | |||
394 | static inline void save_fpu_state(struct sigcontext *sc, struct pt_regs *regs) | ||
395 | { | ||
396 | if (FPU_IS_EMU) { | ||
397 | /* save registers */ | ||
398 | memcpy(sc->sc_fpcntl, current->thread.fpcntl, 12); | ||
399 | memcpy(sc->sc_fpregs, current->thread.fp, 24); | ||
400 | return; | ||
401 | } | ||
402 | |||
403 | __asm__ volatile (".chip 68k/68881\n\t" | ||
404 | "fsave %0\n\t" | ||
405 | ".chip 68k" | ||
406 | : : "m" (*sc->sc_fpstate) : "memory"); | ||
407 | |||
408 | if (sc->sc_fpstate[0]) { | ||
409 | fpu_version = sc->sc_fpstate[0]; | ||
410 | __asm__ volatile (".chip 68k/68881\n\t" | ||
411 | "fmovemx %%fp0-%%fp1,%0\n\t" | ||
412 | "fmoveml %%fpcr/%%fpsr/%%fpiar,%1\n\t" | ||
413 | ".chip 68k" | ||
414 | : "=m" (*sc->sc_fpregs), | ||
415 | "=m" (*sc->sc_fpcntl) | ||
416 | : /* no inputs */ | ||
417 | : "memory"); | ||
418 | } | ||
419 | } | ||
420 | |||
421 | static inline int rt_save_fpu_state(struct ucontext __user *uc, struct pt_regs *regs) | ||
422 | { | ||
423 | unsigned char fpstate[FPCONTEXT_SIZE]; | ||
424 | int context_size = 0; | ||
425 | int err = 0; | ||
426 | |||
427 | if (FPU_IS_EMU) { | ||
428 | /* save fpu control register */ | ||
429 | err |= copy_to_user(uc->uc_mcontext.fpregs.f_pcntl, | ||
430 | current->thread.fpcntl, 12); | ||
431 | /* save all other fpu register */ | ||
432 | err |= copy_to_user(uc->uc_mcontext.fpregs.f_fpregs, | ||
433 | current->thread.fp, 96); | ||
434 | return err; | ||
435 | } | ||
436 | |||
437 | __asm__ volatile (".chip 68k/68881\n\t" | ||
438 | "fsave %0\n\t" | ||
439 | ".chip 68k" | ||
440 | : : "m" (*fpstate) : "memory"); | ||
441 | |||
442 | err |= __put_user(*(long *)fpstate, (long __user *)&uc->uc_fpstate); | ||
443 | if (fpstate[0]) { | ||
444 | fpregset_t fpregs; | ||
445 | context_size = fpstate[1]; | ||
446 | fpu_version = fpstate[0]; | ||
447 | __asm__ volatile (".chip 68k/68881\n\t" | ||
448 | "fmovemx %%fp0-%%fp7,%0\n\t" | ||
449 | "fmoveml %%fpcr/%%fpsr/%%fpiar,%1\n\t" | ||
450 | ".chip 68k" | ||
451 | : "=m" (*fpregs.f_fpregs), | ||
452 | "=m" (*fpregs.f_fpcntl) | ||
453 | : /* no inputs */ | ||
454 | : "memory"); | ||
455 | err |= copy_to_user(&uc->uc_mcontext.fpregs, &fpregs, | ||
456 | sizeof(fpregs)); | ||
457 | } | ||
458 | if (context_size) | ||
459 | err |= copy_to_user((long __user *)&uc->uc_fpstate + 1, fpstate + 4, | ||
460 | context_size); | ||
461 | return err; | ||
462 | } | ||
463 | |||
464 | #endif | ||
465 | |||
466 | static void setup_sigcontext(struct sigcontext *sc, struct pt_regs *regs, | ||
467 | unsigned long mask) | ||
468 | { | ||
469 | sc->sc_mask = mask; | ||
470 | sc->sc_usp = rdusp(); | ||
471 | sc->sc_d0 = regs->d0; | ||
472 | sc->sc_d1 = regs->d1; | ||
473 | sc->sc_a0 = regs->a0; | ||
474 | sc->sc_a1 = regs->a1; | ||
475 | sc->sc_a5 = ((struct switch_stack *)regs - 1)->a5; | ||
476 | sc->sc_sr = regs->sr; | ||
477 | sc->sc_pc = regs->pc; | ||
478 | sc->sc_formatvec = regs->format << 12 | regs->vector; | ||
479 | #ifdef CONFIG_FPU | ||
480 | save_fpu_state(sc, regs); | ||
481 | #endif | ||
482 | } | ||
483 | |||
484 | static inline int rt_setup_ucontext(struct ucontext __user *uc, struct pt_regs *regs) | ||
485 | { | ||
486 | struct switch_stack *sw = (struct switch_stack *)regs - 1; | ||
487 | greg_t __user *gregs = uc->uc_mcontext.gregs; | ||
488 | int err = 0; | ||
489 | |||
490 | err |= __put_user(MCONTEXT_VERSION, &uc->uc_mcontext.version); | ||
491 | err |= __put_user(regs->d0, &gregs[0]); | ||
492 | err |= __put_user(regs->d1, &gregs[1]); | ||
493 | err |= __put_user(regs->d2, &gregs[2]); | ||
494 | err |= __put_user(regs->d3, &gregs[3]); | ||
495 | err |= __put_user(regs->d4, &gregs[4]); | ||
496 | err |= __put_user(regs->d5, &gregs[5]); | ||
497 | err |= __put_user(sw->d6, &gregs[6]); | ||
498 | err |= __put_user(sw->d7, &gregs[7]); | ||
499 | err |= __put_user(regs->a0, &gregs[8]); | ||
500 | err |= __put_user(regs->a1, &gregs[9]); | ||
501 | err |= __put_user(regs->a2, &gregs[10]); | ||
502 | err |= __put_user(sw->a3, &gregs[11]); | ||
503 | err |= __put_user(sw->a4, &gregs[12]); | ||
504 | err |= __put_user(sw->a5, &gregs[13]); | ||
505 | err |= __put_user(sw->a6, &gregs[14]); | ||
506 | err |= __put_user(rdusp(), &gregs[15]); | ||
507 | err |= __put_user(regs->pc, &gregs[16]); | ||
508 | err |= __put_user(regs->sr, &gregs[17]); | ||
509 | #ifdef CONFIG_FPU | ||
510 | err |= rt_save_fpu_state(uc, regs); | ||
511 | #endif | ||
512 | return err; | ||
513 | } | ||
514 | |||
515 | static inline void __user * | ||
516 | get_sigframe(struct k_sigaction *ka, struct pt_regs *regs, size_t frame_size) | ||
517 | { | ||
518 | unsigned long usp; | ||
519 | |||
520 | /* Default to using normal stack. */ | ||
521 | usp = rdusp(); | ||
522 | |||
523 | /* This is the X/Open sanctioned signal stack switching. */ | ||
524 | if (ka->sa.sa_flags & SA_ONSTACK) { | ||
525 | if (!sas_ss_flags(usp)) | ||
526 | usp = current->sas_ss_sp + current->sas_ss_size; | ||
527 | } | ||
528 | return (void __user *)((usp - frame_size) & -8UL); | ||
529 | } | ||
530 | |||
531 | static int setup_frame (int sig, struct k_sigaction *ka, | ||
532 | sigset_t *set, struct pt_regs *regs) | ||
533 | { | ||
534 | struct sigframe __user *frame; | ||
535 | struct sigcontext context; | ||
536 | int err = 0; | ||
537 | |||
538 | frame = get_sigframe(ka, regs, sizeof(*frame)); | ||
539 | |||
540 | err |= __put_user((current_thread_info()->exec_domain | ||
541 | && current_thread_info()->exec_domain->signal_invmap | ||
542 | && sig < 32 | ||
543 | ? current_thread_info()->exec_domain->signal_invmap[sig] | ||
544 | : sig), | ||
545 | &frame->sig); | ||
546 | |||
547 | err |= __put_user(regs->vector, &frame->code); | ||
548 | err |= __put_user(&frame->sc, &frame->psc); | ||
549 | |||
550 | if (_NSIG_WORDS > 1) | ||
551 | err |= copy_to_user(frame->extramask, &set->sig[1], | ||
552 | sizeof(frame->extramask)); | ||
553 | |||
554 | setup_sigcontext(&context, regs, set->sig[0]); | ||
555 | err |= copy_to_user (&frame->sc, &context, sizeof(context)); | ||
556 | |||
557 | /* Set up to return from userspace. */ | ||
558 | err |= __put_user((void *) ret_from_user_signal, &frame->pretcode); | ||
559 | |||
560 | if (err) | ||
561 | goto give_sigsegv; | ||
562 | |||
563 | /* Set up registers for signal handler */ | ||
564 | wrusp ((unsigned long) frame); | ||
565 | regs->pc = (unsigned long) ka->sa.sa_handler; | ||
566 | ((struct switch_stack *)regs - 1)->a5 = current->mm->start_data; | ||
567 | regs->format = 0x4; /*set format byte to make stack appear modulo 4 | ||
568 | which it will be when doing the rte */ | ||
569 | |||
570 | adjust_stack: | ||
571 | /* Prepare to skip over the extra stuff in the exception frame. */ | ||
572 | if (regs->stkadj) { | ||
573 | struct pt_regs *tregs = | ||
574 | (struct pt_regs *)((ulong)regs + regs->stkadj); | ||
575 | #if defined(DEBUG) | ||
576 | printk(KERN_DEBUG "Performing stackadjust=%04x\n", regs->stkadj); | ||
577 | #endif | ||
578 | /* This must be copied with decreasing addresses to | ||
579 | handle overlaps. */ | ||
580 | tregs->vector = 0; | ||
581 | tregs->format = 0; | ||
582 | tregs->pc = regs->pc; | ||
583 | tregs->sr = regs->sr; | ||
584 | } | ||
585 | return err; | ||
586 | |||
587 | give_sigsegv: | ||
588 | force_sigsegv(sig, current); | ||
589 | goto adjust_stack; | ||
590 | } | ||
591 | |||
592 | static int setup_rt_frame (int sig, struct k_sigaction *ka, siginfo_t *info, | ||
593 | sigset_t *set, struct pt_regs *regs) | ||
594 | { | ||
595 | struct rt_sigframe __user *frame; | ||
596 | int err = 0; | ||
597 | |||
598 | frame = get_sigframe(ka, regs, sizeof(*frame)); | ||
599 | |||
600 | err |= __put_user((current_thread_info()->exec_domain | ||
601 | && current_thread_info()->exec_domain->signal_invmap | ||
602 | && sig < 32 | ||
603 | ? current_thread_info()->exec_domain->signal_invmap[sig] | ||
604 | : sig), | ||
605 | &frame->sig); | ||
606 | err |= __put_user(&frame->info, &frame->pinfo); | ||
607 | err |= __put_user(&frame->uc, &frame->puc); | ||
608 | err |= copy_siginfo_to_user(&frame->info, info); | ||
609 | |||
610 | /* Create the ucontext. */ | ||
611 | err |= __put_user(0, &frame->uc.uc_flags); | ||
612 | err |= __put_user(NULL, &frame->uc.uc_link); | ||
613 | err |= __put_user((void __user *)current->sas_ss_sp, | ||
614 | &frame->uc.uc_stack.ss_sp); | ||
615 | err |= __put_user(sas_ss_flags(rdusp()), | ||
616 | &frame->uc.uc_stack.ss_flags); | ||
617 | err |= __put_user(current->sas_ss_size, &frame->uc.uc_stack.ss_size); | ||
618 | err |= rt_setup_ucontext(&frame->uc, regs); | ||
619 | err |= copy_to_user (&frame->uc.uc_sigmask, set, sizeof(*set)); | ||
620 | |||
621 | /* Set up to return from userspace. */ | ||
622 | err |= __put_user((void *) ret_from_user_rt_signal, &frame->pretcode); | ||
623 | |||
624 | if (err) | ||
625 | goto give_sigsegv; | ||
626 | |||
627 | /* Set up registers for signal handler */ | ||
628 | wrusp ((unsigned long) frame); | ||
629 | regs->pc = (unsigned long) ka->sa.sa_handler; | ||
630 | ((struct switch_stack *)regs - 1)->a5 = current->mm->start_data; | ||
631 | regs->format = 0x4; /*set format byte to make stack appear modulo 4 | ||
632 | which it will be when doing the rte */ | ||
633 | |||
634 | adjust_stack: | ||
635 | /* Prepare to skip over the extra stuff in the exception frame. */ | ||
636 | if (regs->stkadj) { | ||
637 | struct pt_regs *tregs = | ||
638 | (struct pt_regs *)((ulong)regs + regs->stkadj); | ||
639 | #if defined(DEBUG) | ||
640 | printk(KERN_DEBUG "Performing stackadjust=%04x\n", regs->stkadj); | ||
641 | #endif | ||
642 | /* This must be copied with decreasing addresses to | ||
643 | handle overlaps. */ | ||
644 | tregs->vector = 0; | ||
645 | tregs->format = 0; | ||
646 | tregs->pc = regs->pc; | ||
647 | tregs->sr = regs->sr; | ||
648 | } | ||
649 | return err; | ||
650 | |||
651 | give_sigsegv: | ||
652 | force_sigsegv(sig, current); | ||
653 | goto adjust_stack; | ||
654 | } | ||
655 | |||
656 | static inline void | ||
657 | handle_restart(struct pt_regs *regs, struct k_sigaction *ka, int has_handler) | ||
658 | { | ||
659 | switch (regs->d0) { | ||
660 | case -ERESTARTNOHAND: | ||
661 | if (!has_handler) | ||
662 | goto do_restart; | ||
663 | regs->d0 = -EINTR; | ||
664 | break; | ||
665 | |||
666 | case -ERESTART_RESTARTBLOCK: | ||
667 | if (!has_handler) { | ||
668 | regs->d0 = __NR_restart_syscall; | ||
669 | regs->pc -= 2; | ||
670 | break; | ||
671 | } | ||
672 | regs->d0 = -EINTR; | ||
673 | break; | ||
674 | |||
675 | case -ERESTARTSYS: | ||
676 | if (has_handler && !(ka->sa.sa_flags & SA_RESTART)) { | ||
677 | regs->d0 = -EINTR; | ||
678 | break; | ||
679 | } | ||
680 | /* fallthrough */ | ||
681 | case -ERESTARTNOINTR: | ||
682 | do_restart: | ||
683 | regs->d0 = regs->orig_d0; | ||
684 | regs->pc -= 2; | ||
685 | break; | ||
686 | } | ||
687 | } | ||
688 | |||
689 | /* | ||
690 | * OK, we're invoking a handler | ||
691 | */ | ||
692 | static void | ||
693 | handle_signal(int sig, struct k_sigaction *ka, siginfo_t *info, | ||
694 | sigset_t *oldset, struct pt_regs *regs) | ||
695 | { | ||
696 | int err; | ||
697 | /* are we from a system call? */ | ||
698 | if (regs->orig_d0 >= 0) | ||
699 | /* If so, check system call restarting.. */ | ||
700 | handle_restart(regs, ka, 1); | ||
701 | |||
702 | /* set up the stack frame */ | ||
703 | if (ka->sa.sa_flags & SA_SIGINFO) | ||
704 | err = setup_rt_frame(sig, ka, info, oldset, regs); | ||
705 | else | ||
706 | err = setup_frame(sig, ka, oldset, regs); | ||
707 | |||
708 | if (err) | ||
709 | return; | ||
710 | |||
711 | spin_lock_irq(¤t->sighand->siglock); | ||
712 | sigorsets(¤t->blocked,¤t->blocked,&ka->sa.sa_mask); | ||
713 | if (!(ka->sa.sa_flags & SA_NODEFER)) | ||
714 | sigaddset(¤t->blocked,sig); | ||
715 | recalc_sigpending(); | ||
716 | spin_unlock_irq(¤t->sighand->siglock); | ||
717 | |||
718 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
719 | } | ||
720 | |||
721 | /* | ||
722 | * Note that 'init' is a special process: it doesn't get signals it doesn't | ||
723 | * want to handle. Thus you cannot kill init even with a SIGKILL even by | ||
724 | * mistake. | ||
725 | */ | ||
726 | asmlinkage void do_signal(struct pt_regs *regs) | ||
727 | { | ||
728 | struct k_sigaction ka; | ||
729 | siginfo_t info; | ||
730 | int signr; | ||
731 | sigset_t *oldset; | ||
732 | |||
733 | /* | ||
734 | * We want the common case to go fast, which | ||
735 | * is why we may in certain cases get here from | ||
736 | * kernel mode. Just return without doing anything | ||
737 | * if so. | ||
738 | */ | ||
739 | if (!user_mode(regs)) | ||
740 | return; | ||
741 | |||
742 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | ||
743 | oldset = ¤t->saved_sigmask; | ||
744 | else | ||
745 | oldset = ¤t->blocked; | ||
746 | |||
747 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); | ||
748 | if (signr > 0) { | ||
749 | /* Whee! Actually deliver the signal. */ | ||
750 | handle_signal(signr, &ka, &info, oldset, regs); | ||
751 | return; | ||
752 | } | ||
753 | |||
754 | /* Did we come from a system call? */ | ||
755 | if (regs->orig_d0 >= 0) { | ||
756 | /* Restart the system call - no handlers present */ | ||
757 | handle_restart(regs, NULL, 0); | ||
758 | } | ||
759 | |||
760 | /* If there's no signal to deliver, we just restore the saved mask. */ | ||
761 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) { | ||
762 | clear_thread_flag(TIF_RESTORE_SIGMASK); | ||
763 | sigprocmask(SIG_SETMASK, ¤t->saved_sigmask, NULL); | ||
764 | } | ||
765 | } | ||
diff --git a/arch/m68k/kernel/sys_m68k.c b/arch/m68k/kernel/sys_m68k.c index 3db2e7f902aa..63013df33584 100644 --- a/arch/m68k/kernel/sys_m68k.c +++ b/arch/m68k/kernel/sys_m68k.c | |||
@@ -1,546 +1,5 @@ | |||
1 | /* | 1 | #ifdef CONFIG_MMU |
2 | * linux/arch/m68k/kernel/sys_m68k.c | 2 | #include "sys_m68k_mm.c" |
3 | * | 3 | #else |
4 | * This file contains various random system calls that | 4 | #include "sys_m68k_no.c" |
5 | * have a non-standard calling sequence on the Linux/m68k | 5 | #endif |
6 | * platform. | ||
7 | */ | ||
8 | |||
9 | #include <linux/capability.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/fs.h> | ||
14 | #include <linux/smp.h> | ||
15 | #include <linux/sem.h> | ||
16 | #include <linux/msg.h> | ||
17 | #include <linux/shm.h> | ||
18 | #include <linux/stat.h> | ||
19 | #include <linux/syscalls.h> | ||
20 | #include <linux/mman.h> | ||
21 | #include <linux/file.h> | ||
22 | #include <linux/ipc.h> | ||
23 | |||
24 | #include <asm/setup.h> | ||
25 | #include <asm/uaccess.h> | ||
26 | #include <asm/cachectl.h> | ||
27 | #include <asm/traps.h> | ||
28 | #include <asm/page.h> | ||
29 | #include <asm/unistd.h> | ||
30 | #include <linux/elf.h> | ||
31 | #include <asm/tlb.h> | ||
32 | |||
33 | asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, | ||
34 | unsigned long error_code); | ||
35 | |||
36 | asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, | ||
37 | unsigned long prot, unsigned long flags, | ||
38 | unsigned long fd, unsigned long pgoff) | ||
39 | { | ||
40 | /* | ||
41 | * This is wrong for sun3 - there PAGE_SIZE is 8Kb, | ||
42 | * so we need to shift the argument down by 1; m68k mmap64(3) | ||
43 | * (in libc) expects the last argument of mmap2 in 4Kb units. | ||
44 | */ | ||
45 | return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff); | ||
46 | } | ||
47 | |||
48 | /* Convert virtual (user) address VADDR to physical address PADDR */ | ||
49 | #define virt_to_phys_040(vaddr) \ | ||
50 | ({ \ | ||
51 | unsigned long _mmusr, _paddr; \ | ||
52 | \ | ||
53 | __asm__ __volatile__ (".chip 68040\n\t" \ | ||
54 | "ptestr (%1)\n\t" \ | ||
55 | "movec %%mmusr,%0\n\t" \ | ||
56 | ".chip 68k" \ | ||
57 | : "=r" (_mmusr) \ | ||
58 | : "a" (vaddr)); \ | ||
59 | _paddr = (_mmusr & MMU_R_040) ? (_mmusr & PAGE_MASK) : 0; \ | ||
60 | _paddr; \ | ||
61 | }) | ||
62 | |||
63 | static inline int | ||
64 | cache_flush_040 (unsigned long addr, int scope, int cache, unsigned long len) | ||
65 | { | ||
66 | unsigned long paddr, i; | ||
67 | |||
68 | switch (scope) | ||
69 | { | ||
70 | case FLUSH_SCOPE_ALL: | ||
71 | switch (cache) | ||
72 | { | ||
73 | case FLUSH_CACHE_DATA: | ||
74 | /* This nop is needed for some broken versions of the 68040. */ | ||
75 | __asm__ __volatile__ ("nop\n\t" | ||
76 | ".chip 68040\n\t" | ||
77 | "cpusha %dc\n\t" | ||
78 | ".chip 68k"); | ||
79 | break; | ||
80 | case FLUSH_CACHE_INSN: | ||
81 | __asm__ __volatile__ ("nop\n\t" | ||
82 | ".chip 68040\n\t" | ||
83 | "cpusha %ic\n\t" | ||
84 | ".chip 68k"); | ||
85 | break; | ||
86 | default: | ||
87 | case FLUSH_CACHE_BOTH: | ||
88 | __asm__ __volatile__ ("nop\n\t" | ||
89 | ".chip 68040\n\t" | ||
90 | "cpusha %bc\n\t" | ||
91 | ".chip 68k"); | ||
92 | break; | ||
93 | } | ||
94 | break; | ||
95 | |||
96 | case FLUSH_SCOPE_LINE: | ||
97 | /* Find the physical address of the first mapped page in the | ||
98 | address range. */ | ||
99 | if ((paddr = virt_to_phys_040(addr))) { | ||
100 | paddr += addr & ~(PAGE_MASK | 15); | ||
101 | len = (len + (addr & 15) + 15) >> 4; | ||
102 | } else { | ||
103 | unsigned long tmp = PAGE_SIZE - (addr & ~PAGE_MASK); | ||
104 | |||
105 | if (len <= tmp) | ||
106 | return 0; | ||
107 | addr += tmp; | ||
108 | len -= tmp; | ||
109 | tmp = PAGE_SIZE; | ||
110 | for (;;) | ||
111 | { | ||
112 | if ((paddr = virt_to_phys_040(addr))) | ||
113 | break; | ||
114 | if (len <= tmp) | ||
115 | return 0; | ||
116 | addr += tmp; | ||
117 | len -= tmp; | ||
118 | } | ||
119 | len = (len + 15) >> 4; | ||
120 | } | ||
121 | i = (PAGE_SIZE - (paddr & ~PAGE_MASK)) >> 4; | ||
122 | while (len--) | ||
123 | { | ||
124 | switch (cache) | ||
125 | { | ||
126 | case FLUSH_CACHE_DATA: | ||
127 | __asm__ __volatile__ ("nop\n\t" | ||
128 | ".chip 68040\n\t" | ||
129 | "cpushl %%dc,(%0)\n\t" | ||
130 | ".chip 68k" | ||
131 | : : "a" (paddr)); | ||
132 | break; | ||
133 | case FLUSH_CACHE_INSN: | ||
134 | __asm__ __volatile__ ("nop\n\t" | ||
135 | ".chip 68040\n\t" | ||
136 | "cpushl %%ic,(%0)\n\t" | ||
137 | ".chip 68k" | ||
138 | : : "a" (paddr)); | ||
139 | break; | ||
140 | default: | ||
141 | case FLUSH_CACHE_BOTH: | ||
142 | __asm__ __volatile__ ("nop\n\t" | ||
143 | ".chip 68040\n\t" | ||
144 | "cpushl %%bc,(%0)\n\t" | ||
145 | ".chip 68k" | ||
146 | : : "a" (paddr)); | ||
147 | break; | ||
148 | } | ||
149 | if (!--i && len) | ||
150 | { | ||
151 | /* | ||
152 | * No need to page align here since it is done by | ||
153 | * virt_to_phys_040(). | ||
154 | */ | ||
155 | addr += PAGE_SIZE; | ||
156 | i = PAGE_SIZE / 16; | ||
157 | /* Recompute physical address when crossing a page | ||
158 | boundary. */ | ||
159 | for (;;) | ||
160 | { | ||
161 | if ((paddr = virt_to_phys_040(addr))) | ||
162 | break; | ||
163 | if (len <= i) | ||
164 | return 0; | ||
165 | len -= i; | ||
166 | addr += PAGE_SIZE; | ||
167 | } | ||
168 | } | ||
169 | else | ||
170 | paddr += 16; | ||
171 | } | ||
172 | break; | ||
173 | |||
174 | default: | ||
175 | case FLUSH_SCOPE_PAGE: | ||
176 | len += (addr & ~PAGE_MASK) + (PAGE_SIZE - 1); | ||
177 | for (len >>= PAGE_SHIFT; len--; addr += PAGE_SIZE) | ||
178 | { | ||
179 | if (!(paddr = virt_to_phys_040(addr))) | ||
180 | continue; | ||
181 | switch (cache) | ||
182 | { | ||
183 | case FLUSH_CACHE_DATA: | ||
184 | __asm__ __volatile__ ("nop\n\t" | ||
185 | ".chip 68040\n\t" | ||
186 | "cpushp %%dc,(%0)\n\t" | ||
187 | ".chip 68k" | ||
188 | : : "a" (paddr)); | ||
189 | break; | ||
190 | case FLUSH_CACHE_INSN: | ||
191 | __asm__ __volatile__ ("nop\n\t" | ||
192 | ".chip 68040\n\t" | ||
193 | "cpushp %%ic,(%0)\n\t" | ||
194 | ".chip 68k" | ||
195 | : : "a" (paddr)); | ||
196 | break; | ||
197 | default: | ||
198 | case FLUSH_CACHE_BOTH: | ||
199 | __asm__ __volatile__ ("nop\n\t" | ||
200 | ".chip 68040\n\t" | ||
201 | "cpushp %%bc,(%0)\n\t" | ||
202 | ".chip 68k" | ||
203 | : : "a" (paddr)); | ||
204 | break; | ||
205 | } | ||
206 | } | ||
207 | break; | ||
208 | } | ||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | #define virt_to_phys_060(vaddr) \ | ||
213 | ({ \ | ||
214 | unsigned long paddr; \ | ||
215 | __asm__ __volatile__ (".chip 68060\n\t" \ | ||
216 | "plpar (%0)\n\t" \ | ||
217 | ".chip 68k" \ | ||
218 | : "=a" (paddr) \ | ||
219 | : "0" (vaddr)); \ | ||
220 | (paddr); /* XXX */ \ | ||
221 | }) | ||
222 | |||
223 | static inline int | ||
224 | cache_flush_060 (unsigned long addr, int scope, int cache, unsigned long len) | ||
225 | { | ||
226 | unsigned long paddr, i; | ||
227 | |||
228 | /* | ||
229 | * 68060 manual says: | ||
230 | * cpush %dc : flush DC, remains valid (with our %cacr setup) | ||
231 | * cpush %ic : invalidate IC | ||
232 | * cpush %bc : flush DC + invalidate IC | ||
233 | */ | ||
234 | switch (scope) | ||
235 | { | ||
236 | case FLUSH_SCOPE_ALL: | ||
237 | switch (cache) | ||
238 | { | ||
239 | case FLUSH_CACHE_DATA: | ||
240 | __asm__ __volatile__ (".chip 68060\n\t" | ||
241 | "cpusha %dc\n\t" | ||
242 | ".chip 68k"); | ||
243 | break; | ||
244 | case FLUSH_CACHE_INSN: | ||
245 | __asm__ __volatile__ (".chip 68060\n\t" | ||
246 | "cpusha %ic\n\t" | ||
247 | ".chip 68k"); | ||
248 | break; | ||
249 | default: | ||
250 | case FLUSH_CACHE_BOTH: | ||
251 | __asm__ __volatile__ (".chip 68060\n\t" | ||
252 | "cpusha %bc\n\t" | ||
253 | ".chip 68k"); | ||
254 | break; | ||
255 | } | ||
256 | break; | ||
257 | |||
258 | case FLUSH_SCOPE_LINE: | ||
259 | /* Find the physical address of the first mapped page in the | ||
260 | address range. */ | ||
261 | len += addr & 15; | ||
262 | addr &= -16; | ||
263 | if (!(paddr = virt_to_phys_060(addr))) { | ||
264 | unsigned long tmp = PAGE_SIZE - (addr & ~PAGE_MASK); | ||
265 | |||
266 | if (len <= tmp) | ||
267 | return 0; | ||
268 | addr += tmp; | ||
269 | len -= tmp; | ||
270 | tmp = PAGE_SIZE; | ||
271 | for (;;) | ||
272 | { | ||
273 | if ((paddr = virt_to_phys_060(addr))) | ||
274 | break; | ||
275 | if (len <= tmp) | ||
276 | return 0; | ||
277 | addr += tmp; | ||
278 | len -= tmp; | ||
279 | } | ||
280 | } | ||
281 | len = (len + 15) >> 4; | ||
282 | i = (PAGE_SIZE - (paddr & ~PAGE_MASK)) >> 4; | ||
283 | while (len--) | ||
284 | { | ||
285 | switch (cache) | ||
286 | { | ||
287 | case FLUSH_CACHE_DATA: | ||
288 | __asm__ __volatile__ (".chip 68060\n\t" | ||
289 | "cpushl %%dc,(%0)\n\t" | ||
290 | ".chip 68k" | ||
291 | : : "a" (paddr)); | ||
292 | break; | ||
293 | case FLUSH_CACHE_INSN: | ||
294 | __asm__ __volatile__ (".chip 68060\n\t" | ||
295 | "cpushl %%ic,(%0)\n\t" | ||
296 | ".chip 68k" | ||
297 | : : "a" (paddr)); | ||
298 | break; | ||
299 | default: | ||
300 | case FLUSH_CACHE_BOTH: | ||
301 | __asm__ __volatile__ (".chip 68060\n\t" | ||
302 | "cpushl %%bc,(%0)\n\t" | ||
303 | ".chip 68k" | ||
304 | : : "a" (paddr)); | ||
305 | break; | ||
306 | } | ||
307 | if (!--i && len) | ||
308 | { | ||
309 | |||
310 | /* | ||
311 | * We just want to jump to the first cache line | ||
312 | * in the next page. | ||
313 | */ | ||
314 | addr += PAGE_SIZE; | ||
315 | addr &= PAGE_MASK; | ||
316 | |||
317 | i = PAGE_SIZE / 16; | ||
318 | /* Recompute physical address when crossing a page | ||
319 | boundary. */ | ||
320 | for (;;) | ||
321 | { | ||
322 | if ((paddr = virt_to_phys_060(addr))) | ||
323 | break; | ||
324 | if (len <= i) | ||
325 | return 0; | ||
326 | len -= i; | ||
327 | addr += PAGE_SIZE; | ||
328 | } | ||
329 | } | ||
330 | else | ||
331 | paddr += 16; | ||
332 | } | ||
333 | break; | ||
334 | |||
335 | default: | ||
336 | case FLUSH_SCOPE_PAGE: | ||
337 | len += (addr & ~PAGE_MASK) + (PAGE_SIZE - 1); | ||
338 | addr &= PAGE_MASK; /* Workaround for bug in some | ||
339 | revisions of the 68060 */ | ||
340 | for (len >>= PAGE_SHIFT; len--; addr += PAGE_SIZE) | ||
341 | { | ||
342 | if (!(paddr = virt_to_phys_060(addr))) | ||
343 | continue; | ||
344 | switch (cache) | ||
345 | { | ||
346 | case FLUSH_CACHE_DATA: | ||
347 | __asm__ __volatile__ (".chip 68060\n\t" | ||
348 | "cpushp %%dc,(%0)\n\t" | ||
349 | ".chip 68k" | ||
350 | : : "a" (paddr)); | ||
351 | break; | ||
352 | case FLUSH_CACHE_INSN: | ||
353 | __asm__ __volatile__ (".chip 68060\n\t" | ||
354 | "cpushp %%ic,(%0)\n\t" | ||
355 | ".chip 68k" | ||
356 | : : "a" (paddr)); | ||
357 | break; | ||
358 | default: | ||
359 | case FLUSH_CACHE_BOTH: | ||
360 | __asm__ __volatile__ (".chip 68060\n\t" | ||
361 | "cpushp %%bc,(%0)\n\t" | ||
362 | ".chip 68k" | ||
363 | : : "a" (paddr)); | ||
364 | break; | ||
365 | } | ||
366 | } | ||
367 | break; | ||
368 | } | ||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | /* sys_cacheflush -- flush (part of) the processor cache. */ | ||
373 | asmlinkage int | ||
374 | sys_cacheflush (unsigned long addr, int scope, int cache, unsigned long len) | ||
375 | { | ||
376 | struct vm_area_struct *vma; | ||
377 | int ret = -EINVAL; | ||
378 | |||
379 | if (scope < FLUSH_SCOPE_LINE || scope > FLUSH_SCOPE_ALL || | ||
380 | cache & ~FLUSH_CACHE_BOTH) | ||
381 | goto out; | ||
382 | |||
383 | if (scope == FLUSH_SCOPE_ALL) { | ||
384 | /* Only the superuser may explicitly flush the whole cache. */ | ||
385 | ret = -EPERM; | ||
386 | if (!capable(CAP_SYS_ADMIN)) | ||
387 | goto out; | ||
388 | } else { | ||
389 | /* | ||
390 | * Verify that the specified address region actually belongs | ||
391 | * to this process. | ||
392 | */ | ||
393 | vma = find_vma (current->mm, addr); | ||
394 | ret = -EINVAL; | ||
395 | /* Check for overflow. */ | ||
396 | if (addr + len < addr) | ||
397 | goto out; | ||
398 | if (vma == NULL || addr < vma->vm_start || addr + len > vma->vm_end) | ||
399 | goto out; | ||
400 | } | ||
401 | |||
402 | if (CPU_IS_020_OR_030) { | ||
403 | if (scope == FLUSH_SCOPE_LINE && len < 256) { | ||
404 | unsigned long cacr; | ||
405 | __asm__ ("movec %%cacr, %0" : "=r" (cacr)); | ||
406 | if (cache & FLUSH_CACHE_INSN) | ||
407 | cacr |= 4; | ||
408 | if (cache & FLUSH_CACHE_DATA) | ||
409 | cacr |= 0x400; | ||
410 | len >>= 2; | ||
411 | while (len--) { | ||
412 | __asm__ __volatile__ ("movec %1, %%caar\n\t" | ||
413 | "movec %0, %%cacr" | ||
414 | : /* no outputs */ | ||
415 | : "r" (cacr), "r" (addr)); | ||
416 | addr += 4; | ||
417 | } | ||
418 | } else { | ||
419 | /* Flush the whole cache, even if page granularity requested. */ | ||
420 | unsigned long cacr; | ||
421 | __asm__ ("movec %%cacr, %0" : "=r" (cacr)); | ||
422 | if (cache & FLUSH_CACHE_INSN) | ||
423 | cacr |= 8; | ||
424 | if (cache & FLUSH_CACHE_DATA) | ||
425 | cacr |= 0x800; | ||
426 | __asm__ __volatile__ ("movec %0, %%cacr" : : "r" (cacr)); | ||
427 | } | ||
428 | ret = 0; | ||
429 | goto out; | ||
430 | } else { | ||
431 | /* | ||
432 | * 040 or 060: don't blindly trust 'scope', someone could | ||
433 | * try to flush a few megs of memory. | ||
434 | */ | ||
435 | |||
436 | if (len>=3*PAGE_SIZE && scope<FLUSH_SCOPE_PAGE) | ||
437 | scope=FLUSH_SCOPE_PAGE; | ||
438 | if (len>=10*PAGE_SIZE && scope<FLUSH_SCOPE_ALL) | ||
439 | scope=FLUSH_SCOPE_ALL; | ||
440 | if (CPU_IS_040) { | ||
441 | ret = cache_flush_040 (addr, scope, cache, len); | ||
442 | } else if (CPU_IS_060) { | ||
443 | ret = cache_flush_060 (addr, scope, cache, len); | ||
444 | } | ||
445 | } | ||
446 | out: | ||
447 | return ret; | ||
448 | } | ||
449 | |||
450 | asmlinkage int sys_getpagesize(void) | ||
451 | { | ||
452 | return PAGE_SIZE; | ||
453 | } | ||
454 | |||
455 | /* | ||
456 | * Do a system call from kernel instead of calling sys_execve so we | ||
457 | * end up with proper pt_regs. | ||
458 | */ | ||
459 | int kernel_execve(const char *filename, | ||
460 | const char *const argv[], | ||
461 | const char *const envp[]) | ||
462 | { | ||
463 | register long __res asm ("%d0") = __NR_execve; | ||
464 | register long __a asm ("%d1") = (long)(filename); | ||
465 | register long __b asm ("%d2") = (long)(argv); | ||
466 | register long __c asm ("%d3") = (long)(envp); | ||
467 | asm volatile ("trap #0" : "+d" (__res) | ||
468 | : "d" (__a), "d" (__b), "d" (__c)); | ||
469 | return __res; | ||
470 | } | ||
471 | |||
472 | asmlinkage unsigned long sys_get_thread_area(void) | ||
473 | { | ||
474 | return current_thread_info()->tp_value; | ||
475 | } | ||
476 | |||
477 | asmlinkage int sys_set_thread_area(unsigned long tp) | ||
478 | { | ||
479 | current_thread_info()->tp_value = tp; | ||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | /* This syscall gets its arguments in A0 (mem), D2 (oldval) and | ||
484 | D1 (newval). */ | ||
485 | asmlinkage int | ||
486 | sys_atomic_cmpxchg_32(unsigned long newval, int oldval, int d3, int d4, int d5, | ||
487 | unsigned long __user * mem) | ||
488 | { | ||
489 | /* This was borrowed from ARM's implementation. */ | ||
490 | for (;;) { | ||
491 | struct mm_struct *mm = current->mm; | ||
492 | pgd_t *pgd; | ||
493 | pmd_t *pmd; | ||
494 | pte_t *pte; | ||
495 | spinlock_t *ptl; | ||
496 | unsigned long mem_value; | ||
497 | |||
498 | down_read(&mm->mmap_sem); | ||
499 | pgd = pgd_offset(mm, (unsigned long)mem); | ||
500 | if (!pgd_present(*pgd)) | ||
501 | goto bad_access; | ||
502 | pmd = pmd_offset(pgd, (unsigned long)mem); | ||
503 | if (!pmd_present(*pmd)) | ||
504 | goto bad_access; | ||
505 | pte = pte_offset_map_lock(mm, pmd, (unsigned long)mem, &ptl); | ||
506 | if (!pte_present(*pte) || !pte_dirty(*pte) | ||
507 | || !pte_write(*pte)) { | ||
508 | pte_unmap_unlock(pte, ptl); | ||
509 | goto bad_access; | ||
510 | } | ||
511 | |||
512 | mem_value = *mem; | ||
513 | if (mem_value == oldval) | ||
514 | *mem = newval; | ||
515 | |||
516 | pte_unmap_unlock(pte, ptl); | ||
517 | up_read(&mm->mmap_sem); | ||
518 | return mem_value; | ||
519 | |||
520 | bad_access: | ||
521 | up_read(&mm->mmap_sem); | ||
522 | /* This is not necessarily a bad access, we can get here if | ||
523 | a memory we're trying to write to should be copied-on-write. | ||
524 | Make the kernel do the necessary page stuff, then re-iterate. | ||
525 | Simulate a write access fault to do that. */ | ||
526 | { | ||
527 | /* The first argument of the function corresponds to | ||
528 | D1, which is the first field of struct pt_regs. */ | ||
529 | struct pt_regs *fp = (struct pt_regs *)&newval; | ||
530 | |||
531 | /* '3' is an RMW flag. */ | ||
532 | if (do_page_fault(fp, (unsigned long)mem, 3)) | ||
533 | /* If the do_page_fault() failed, we don't | ||
534 | have anything meaningful to return. | ||
535 | There should be a SIGSEGV pending for | ||
536 | the process. */ | ||
537 | return 0xdeadbeef; | ||
538 | } | ||
539 | } | ||
540 | } | ||
541 | |||
542 | asmlinkage int sys_atomic_barrier(void) | ||
543 | { | ||
544 | /* no code needed for uniprocs */ | ||
545 | return 0; | ||
546 | } | ||
diff --git a/arch/m68k/kernel/sys_m68k_mm.c b/arch/m68k/kernel/sys_m68k_mm.c new file mode 100644 index 000000000000..3db2e7f902aa --- /dev/null +++ b/arch/m68k/kernel/sys_m68k_mm.c | |||
@@ -0,0 +1,546 @@ | |||
1 | /* | ||
2 | * linux/arch/m68k/kernel/sys_m68k.c | ||
3 | * | ||
4 | * This file contains various random system calls that | ||
5 | * have a non-standard calling sequence on the Linux/m68k | ||
6 | * platform. | ||
7 | */ | ||
8 | |||
9 | #include <linux/capability.h> | ||
10 | #include <linux/errno.h> | ||
11 | #include <linux/sched.h> | ||
12 | #include <linux/mm.h> | ||
13 | #include <linux/fs.h> | ||
14 | #include <linux/smp.h> | ||
15 | #include <linux/sem.h> | ||
16 | #include <linux/msg.h> | ||
17 | #include <linux/shm.h> | ||
18 | #include <linux/stat.h> | ||
19 | #include <linux/syscalls.h> | ||
20 | #include <linux/mman.h> | ||
21 | #include <linux/file.h> | ||
22 | #include <linux/ipc.h> | ||
23 | |||
24 | #include <asm/setup.h> | ||
25 | #include <asm/uaccess.h> | ||
26 | #include <asm/cachectl.h> | ||
27 | #include <asm/traps.h> | ||
28 | #include <asm/page.h> | ||
29 | #include <asm/unistd.h> | ||
30 | #include <linux/elf.h> | ||
31 | #include <asm/tlb.h> | ||
32 | |||
33 | asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, | ||
34 | unsigned long error_code); | ||
35 | |||
36 | asmlinkage long sys_mmap2(unsigned long addr, unsigned long len, | ||
37 | unsigned long prot, unsigned long flags, | ||
38 | unsigned long fd, unsigned long pgoff) | ||
39 | { | ||
40 | /* | ||
41 | * This is wrong for sun3 - there PAGE_SIZE is 8Kb, | ||
42 | * so we need to shift the argument down by 1; m68k mmap64(3) | ||
43 | * (in libc) expects the last argument of mmap2 in 4Kb units. | ||
44 | */ | ||
45 | return sys_mmap_pgoff(addr, len, prot, flags, fd, pgoff); | ||
46 | } | ||
47 | |||
48 | /* Convert virtual (user) address VADDR to physical address PADDR */ | ||
49 | #define virt_to_phys_040(vaddr) \ | ||
50 | ({ \ | ||
51 | unsigned long _mmusr, _paddr; \ | ||
52 | \ | ||
53 | __asm__ __volatile__ (".chip 68040\n\t" \ | ||
54 | "ptestr (%1)\n\t" \ | ||
55 | "movec %%mmusr,%0\n\t" \ | ||
56 | ".chip 68k" \ | ||
57 | : "=r" (_mmusr) \ | ||
58 | : "a" (vaddr)); \ | ||
59 | _paddr = (_mmusr & MMU_R_040) ? (_mmusr & PAGE_MASK) : 0; \ | ||
60 | _paddr; \ | ||
61 | }) | ||
62 | |||
63 | static inline int | ||
64 | cache_flush_040 (unsigned long addr, int scope, int cache, unsigned long len) | ||
65 | { | ||
66 | unsigned long paddr, i; | ||
67 | |||
68 | switch (scope) | ||
69 | { | ||
70 | case FLUSH_SCOPE_ALL: | ||
71 | switch (cache) | ||
72 | { | ||
73 | case FLUSH_CACHE_DATA: | ||
74 | /* This nop is needed for some broken versions of the 68040. */ | ||
75 | __asm__ __volatile__ ("nop\n\t" | ||
76 | ".chip 68040\n\t" | ||
77 | "cpusha %dc\n\t" | ||
78 | ".chip 68k"); | ||
79 | break; | ||
80 | case FLUSH_CACHE_INSN: | ||
81 | __asm__ __volatile__ ("nop\n\t" | ||
82 | ".chip 68040\n\t" | ||
83 | "cpusha %ic\n\t" | ||
84 | ".chip 68k"); | ||
85 | break; | ||
86 | default: | ||
87 | case FLUSH_CACHE_BOTH: | ||
88 | __asm__ __volatile__ ("nop\n\t" | ||
89 | ".chip 68040\n\t" | ||
90 | "cpusha %bc\n\t" | ||
91 | ".chip 68k"); | ||
92 | break; | ||
93 | } | ||
94 | break; | ||
95 | |||
96 | case FLUSH_SCOPE_LINE: | ||
97 | /* Find the physical address of the first mapped page in the | ||
98 | address range. */ | ||
99 | if ((paddr = virt_to_phys_040(addr))) { | ||
100 | paddr += addr & ~(PAGE_MASK | 15); | ||
101 | len = (len + (addr & 15) + 15) >> 4; | ||
102 | } else { | ||
103 | unsigned long tmp = PAGE_SIZE - (addr & ~PAGE_MASK); | ||
104 | |||
105 | if (len <= tmp) | ||
106 | return 0; | ||
107 | addr += tmp; | ||
108 | len -= tmp; | ||
109 | tmp = PAGE_SIZE; | ||
110 | for (;;) | ||
111 | { | ||
112 | if ((paddr = virt_to_phys_040(addr))) | ||
113 | break; | ||
114 | if (len <= tmp) | ||
115 | return 0; | ||
116 | addr += tmp; | ||
117 | len -= tmp; | ||
118 | } | ||
119 | len = (len + 15) >> 4; | ||
120 | } | ||
121 | i = (PAGE_SIZE - (paddr & ~PAGE_MASK)) >> 4; | ||
122 | while (len--) | ||
123 | { | ||
124 | switch (cache) | ||
125 | { | ||
126 | case FLUSH_CACHE_DATA: | ||
127 | __asm__ __volatile__ ("nop\n\t" | ||
128 | ".chip 68040\n\t" | ||
129 | "cpushl %%dc,(%0)\n\t" | ||
130 | ".chip 68k" | ||
131 | : : "a" (paddr)); | ||
132 | break; | ||
133 | case FLUSH_CACHE_INSN: | ||
134 | __asm__ __volatile__ ("nop\n\t" | ||
135 | ".chip 68040\n\t" | ||
136 | "cpushl %%ic,(%0)\n\t" | ||
137 | ".chip 68k" | ||
138 | : : "a" (paddr)); | ||
139 | break; | ||
140 | default: | ||
141 | case FLUSH_CACHE_BOTH: | ||
142 | __asm__ __volatile__ ("nop\n\t" | ||
143 | ".chip 68040\n\t" | ||
144 | "cpushl %%bc,(%0)\n\t" | ||
145 | ".chip 68k" | ||
146 | : : "a" (paddr)); | ||
147 | break; | ||
148 | } | ||
149 | if (!--i && len) | ||
150 | { | ||
151 | /* | ||
152 | * No need to page align here since it is done by | ||
153 | * virt_to_phys_040(). | ||
154 | */ | ||
155 | addr += PAGE_SIZE; | ||
156 | i = PAGE_SIZE / 16; | ||
157 | /* Recompute physical address when crossing a page | ||
158 | boundary. */ | ||
159 | for (;;) | ||
160 | { | ||
161 | if ((paddr = virt_to_phys_040(addr))) | ||
162 | break; | ||
163 | if (len <= i) | ||
164 | return 0; | ||
165 | len -= i; | ||
166 | addr += PAGE_SIZE; | ||
167 | } | ||
168 | } | ||
169 | else | ||
170 | paddr += 16; | ||
171 | } | ||
172 | break; | ||
173 | |||
174 | default: | ||
175 | case FLUSH_SCOPE_PAGE: | ||
176 | len += (addr & ~PAGE_MASK) + (PAGE_SIZE - 1); | ||
177 | for (len >>= PAGE_SHIFT; len--; addr += PAGE_SIZE) | ||
178 | { | ||
179 | if (!(paddr = virt_to_phys_040(addr))) | ||
180 | continue; | ||
181 | switch (cache) | ||
182 | { | ||
183 | case FLUSH_CACHE_DATA: | ||
184 | __asm__ __volatile__ ("nop\n\t" | ||
185 | ".chip 68040\n\t" | ||
186 | "cpushp %%dc,(%0)\n\t" | ||
187 | ".chip 68k" | ||
188 | : : "a" (paddr)); | ||
189 | break; | ||
190 | case FLUSH_CACHE_INSN: | ||
191 | __asm__ __volatile__ ("nop\n\t" | ||
192 | ".chip 68040\n\t" | ||
193 | "cpushp %%ic,(%0)\n\t" | ||
194 | ".chip 68k" | ||
195 | : : "a" (paddr)); | ||
196 | break; | ||
197 | default: | ||
198 | case FLUSH_CACHE_BOTH: | ||
199 | __asm__ __volatile__ ("nop\n\t" | ||
200 | ".chip 68040\n\t" | ||
201 | "cpushp %%bc,(%0)\n\t" | ||
202 | ".chip 68k" | ||
203 | : : "a" (paddr)); | ||
204 | break; | ||
205 | } | ||
206 | } | ||
207 | break; | ||
208 | } | ||
209 | return 0; | ||
210 | } | ||
211 | |||
212 | #define virt_to_phys_060(vaddr) \ | ||
213 | ({ \ | ||
214 | unsigned long paddr; \ | ||
215 | __asm__ __volatile__ (".chip 68060\n\t" \ | ||
216 | "plpar (%0)\n\t" \ | ||
217 | ".chip 68k" \ | ||
218 | : "=a" (paddr) \ | ||
219 | : "0" (vaddr)); \ | ||
220 | (paddr); /* XXX */ \ | ||
221 | }) | ||
222 | |||
223 | static inline int | ||
224 | cache_flush_060 (unsigned long addr, int scope, int cache, unsigned long len) | ||
225 | { | ||
226 | unsigned long paddr, i; | ||
227 | |||
228 | /* | ||
229 | * 68060 manual says: | ||
230 | * cpush %dc : flush DC, remains valid (with our %cacr setup) | ||
231 | * cpush %ic : invalidate IC | ||
232 | * cpush %bc : flush DC + invalidate IC | ||
233 | */ | ||
234 | switch (scope) | ||
235 | { | ||
236 | case FLUSH_SCOPE_ALL: | ||
237 | switch (cache) | ||
238 | { | ||
239 | case FLUSH_CACHE_DATA: | ||
240 | __asm__ __volatile__ (".chip 68060\n\t" | ||
241 | "cpusha %dc\n\t" | ||
242 | ".chip 68k"); | ||
243 | break; | ||
244 | case FLUSH_CACHE_INSN: | ||
245 | __asm__ __volatile__ (".chip 68060\n\t" | ||
246 | "cpusha %ic\n\t" | ||
247 | ".chip 68k"); | ||
248 | break; | ||
249 | default: | ||
250 | case FLUSH_CACHE_BOTH: | ||
251 | __asm__ __volatile__ (".chip 68060\n\t" | ||
252 | "cpusha %bc\n\t" | ||
253 | ".chip 68k"); | ||
254 | break; | ||
255 | } | ||
256 | break; | ||
257 | |||
258 | case FLUSH_SCOPE_LINE: | ||
259 | /* Find the physical address of the first mapped page in the | ||
260 | address range. */ | ||
261 | len += addr & 15; | ||
262 | addr &= -16; | ||
263 | if (!(paddr = virt_to_phys_060(addr))) { | ||
264 | unsigned long tmp = PAGE_SIZE - (addr & ~PAGE_MASK); | ||
265 | |||
266 | if (len <= tmp) | ||
267 | return 0; | ||
268 | addr += tmp; | ||
269 | len -= tmp; | ||
270 | tmp = PAGE_SIZE; | ||
271 | for (;;) | ||
272 | { | ||
273 | if ((paddr = virt_to_phys_060(addr))) | ||
274 | break; | ||
275 | if (len <= tmp) | ||
276 | return 0; | ||
277 | addr += tmp; | ||
278 | len -= tmp; | ||
279 | } | ||
280 | } | ||
281 | len = (len + 15) >> 4; | ||
282 | i = (PAGE_SIZE - (paddr & ~PAGE_MASK)) >> 4; | ||
283 | while (len--) | ||
284 | { | ||
285 | switch (cache) | ||
286 | { | ||
287 | case FLUSH_CACHE_DATA: | ||
288 | __asm__ __volatile__ (".chip 68060\n\t" | ||
289 | "cpushl %%dc,(%0)\n\t" | ||
290 | ".chip 68k" | ||
291 | : : "a" (paddr)); | ||
292 | break; | ||
293 | case FLUSH_CACHE_INSN: | ||
294 | __asm__ __volatile__ (".chip 68060\n\t" | ||
295 | "cpushl %%ic,(%0)\n\t" | ||
296 | ".chip 68k" | ||
297 | : : "a" (paddr)); | ||
298 | break; | ||
299 | default: | ||
300 | case FLUSH_CACHE_BOTH: | ||
301 | __asm__ __volatile__ (".chip 68060\n\t" | ||
302 | "cpushl %%bc,(%0)\n\t" | ||
303 | ".chip 68k" | ||
304 | : : "a" (paddr)); | ||
305 | break; | ||
306 | } | ||
307 | if (!--i && len) | ||
308 | { | ||
309 | |||
310 | /* | ||
311 | * We just want to jump to the first cache line | ||
312 | * in the next page. | ||
313 | */ | ||
314 | addr += PAGE_SIZE; | ||
315 | addr &= PAGE_MASK; | ||
316 | |||
317 | i = PAGE_SIZE / 16; | ||
318 | /* Recompute physical address when crossing a page | ||
319 | boundary. */ | ||
320 | for (;;) | ||
321 | { | ||
322 | if ((paddr = virt_to_phys_060(addr))) | ||
323 | break; | ||
324 | if (len <= i) | ||
325 | return 0; | ||
326 | len -= i; | ||
327 | addr += PAGE_SIZE; | ||
328 | } | ||
329 | } | ||
330 | else | ||
331 | paddr += 16; | ||
332 | } | ||
333 | break; | ||
334 | |||
335 | default: | ||
336 | case FLUSH_SCOPE_PAGE: | ||
337 | len += (addr & ~PAGE_MASK) + (PAGE_SIZE - 1); | ||
338 | addr &= PAGE_MASK; /* Workaround for bug in some | ||
339 | revisions of the 68060 */ | ||
340 | for (len >>= PAGE_SHIFT; len--; addr += PAGE_SIZE) | ||
341 | { | ||
342 | if (!(paddr = virt_to_phys_060(addr))) | ||
343 | continue; | ||
344 | switch (cache) | ||
345 | { | ||
346 | case FLUSH_CACHE_DATA: | ||
347 | __asm__ __volatile__ (".chip 68060\n\t" | ||
348 | "cpushp %%dc,(%0)\n\t" | ||
349 | ".chip 68k" | ||
350 | : : "a" (paddr)); | ||
351 | break; | ||
352 | case FLUSH_CACHE_INSN: | ||
353 | __asm__ __volatile__ (".chip 68060\n\t" | ||
354 | "cpushp %%ic,(%0)\n\t" | ||
355 | ".chip 68k" | ||
356 | : : "a" (paddr)); | ||
357 | break; | ||
358 | default: | ||
359 | case FLUSH_CACHE_BOTH: | ||
360 | __asm__ __volatile__ (".chip 68060\n\t" | ||
361 | "cpushp %%bc,(%0)\n\t" | ||
362 | ".chip 68k" | ||
363 | : : "a" (paddr)); | ||
364 | break; | ||
365 | } | ||
366 | } | ||
367 | break; | ||
368 | } | ||
369 | return 0; | ||
370 | } | ||
371 | |||
372 | /* sys_cacheflush -- flush (part of) the processor cache. */ | ||
373 | asmlinkage int | ||
374 | sys_cacheflush (unsigned long addr, int scope, int cache, unsigned long len) | ||
375 | { | ||
376 | struct vm_area_struct *vma; | ||
377 | int ret = -EINVAL; | ||
378 | |||
379 | if (scope < FLUSH_SCOPE_LINE || scope > FLUSH_SCOPE_ALL || | ||
380 | cache & ~FLUSH_CACHE_BOTH) | ||
381 | goto out; | ||
382 | |||
383 | if (scope == FLUSH_SCOPE_ALL) { | ||
384 | /* Only the superuser may explicitly flush the whole cache. */ | ||
385 | ret = -EPERM; | ||
386 | if (!capable(CAP_SYS_ADMIN)) | ||
387 | goto out; | ||
388 | } else { | ||
389 | /* | ||
390 | * Verify that the specified address region actually belongs | ||
391 | * to this process. | ||
392 | */ | ||
393 | vma = find_vma (current->mm, addr); | ||
394 | ret = -EINVAL; | ||
395 | /* Check for overflow. */ | ||
396 | if (addr + len < addr) | ||
397 | goto out; | ||
398 | if (vma == NULL || addr < vma->vm_start || addr + len > vma->vm_end) | ||
399 | goto out; | ||
400 | } | ||
401 | |||
402 | if (CPU_IS_020_OR_030) { | ||
403 | if (scope == FLUSH_SCOPE_LINE && len < 256) { | ||
404 | unsigned long cacr; | ||
405 | __asm__ ("movec %%cacr, %0" : "=r" (cacr)); | ||
406 | if (cache & FLUSH_CACHE_INSN) | ||
407 | cacr |= 4; | ||
408 | if (cache & FLUSH_CACHE_DATA) | ||
409 | cacr |= 0x400; | ||
410 | len >>= 2; | ||
411 | while (len--) { | ||
412 | __asm__ __volatile__ ("movec %1, %%caar\n\t" | ||
413 | "movec %0, %%cacr" | ||
414 | : /* no outputs */ | ||
415 | : "r" (cacr), "r" (addr)); | ||
416 | addr += 4; | ||
417 | } | ||
418 | } else { | ||
419 | /* Flush the whole cache, even if page granularity requested. */ | ||
420 | unsigned long cacr; | ||
421 | __asm__ ("movec %%cacr, %0" : "=r" (cacr)); | ||
422 | if (cache & FLUSH_CACHE_INSN) | ||
423 | cacr |= 8; | ||
424 | if (cache & FLUSH_CACHE_DATA) | ||
425 | cacr |= 0x800; | ||
426 | __asm__ __volatile__ ("movec %0, %%cacr" : : "r" (cacr)); | ||
427 | } | ||
428 | ret = 0; | ||
429 | goto out; | ||
430 | } else { | ||
431 | /* | ||
432 | * 040 or 060: don't blindly trust 'scope', someone could | ||
433 | * try to flush a few megs of memory. | ||
434 | */ | ||
435 | |||
436 | if (len>=3*PAGE_SIZE && scope<FLUSH_SCOPE_PAGE) | ||
437 | scope=FLUSH_SCOPE_PAGE; | ||
438 | if (len>=10*PAGE_SIZE && scope<FLUSH_SCOPE_ALL) | ||
439 | scope=FLUSH_SCOPE_ALL; | ||
440 | if (CPU_IS_040) { | ||
441 | ret = cache_flush_040 (addr, scope, cache, len); | ||
442 | } else if (CPU_IS_060) { | ||
443 | ret = cache_flush_060 (addr, scope, cache, len); | ||
444 | } | ||
445 | } | ||
446 | out: | ||
447 | return ret; | ||
448 | } | ||
449 | |||
450 | asmlinkage int sys_getpagesize(void) | ||
451 | { | ||
452 | return PAGE_SIZE; | ||
453 | } | ||
454 | |||
455 | /* | ||
456 | * Do a system call from kernel instead of calling sys_execve so we | ||
457 | * end up with proper pt_regs. | ||
458 | */ | ||
459 | int kernel_execve(const char *filename, | ||
460 | const char *const argv[], | ||
461 | const char *const envp[]) | ||
462 | { | ||
463 | register long __res asm ("%d0") = __NR_execve; | ||
464 | register long __a asm ("%d1") = (long)(filename); | ||
465 | register long __b asm ("%d2") = (long)(argv); | ||
466 | register long __c asm ("%d3") = (long)(envp); | ||
467 | asm volatile ("trap #0" : "+d" (__res) | ||
468 | : "d" (__a), "d" (__b), "d" (__c)); | ||
469 | return __res; | ||
470 | } | ||
471 | |||
472 | asmlinkage unsigned long sys_get_thread_area(void) | ||
473 | { | ||
474 | return current_thread_info()->tp_value; | ||
475 | } | ||
476 | |||
477 | asmlinkage int sys_set_thread_area(unsigned long tp) | ||
478 | { | ||
479 | current_thread_info()->tp_value = tp; | ||
480 | return 0; | ||
481 | } | ||
482 | |||
483 | /* This syscall gets its arguments in A0 (mem), D2 (oldval) and | ||
484 | D1 (newval). */ | ||
485 | asmlinkage int | ||
486 | sys_atomic_cmpxchg_32(unsigned long newval, int oldval, int d3, int d4, int d5, | ||
487 | unsigned long __user * mem) | ||
488 | { | ||
489 | /* This was borrowed from ARM's implementation. */ | ||
490 | for (;;) { | ||
491 | struct mm_struct *mm = current->mm; | ||
492 | pgd_t *pgd; | ||
493 | pmd_t *pmd; | ||
494 | pte_t *pte; | ||
495 | spinlock_t *ptl; | ||
496 | unsigned long mem_value; | ||
497 | |||
498 | down_read(&mm->mmap_sem); | ||
499 | pgd = pgd_offset(mm, (unsigned long)mem); | ||
500 | if (!pgd_present(*pgd)) | ||
501 | goto bad_access; | ||
502 | pmd = pmd_offset(pgd, (unsigned long)mem); | ||
503 | if (!pmd_present(*pmd)) | ||
504 | goto bad_access; | ||
505 | pte = pte_offset_map_lock(mm, pmd, (unsigned long)mem, &ptl); | ||
506 | if (!pte_present(*pte) || !pte_dirty(*pte) | ||
507 | || !pte_write(*pte)) { | ||
508 | pte_unmap_unlock(pte, ptl); | ||
509 | goto bad_access; | ||
510 | } | ||
511 | |||
512 | mem_value = *mem; | ||
513 | if (mem_value == oldval) | ||
514 | *mem = newval; | ||
515 | |||
516 | pte_unmap_unlock(pte, ptl); | ||
517 | up_read(&mm->mmap_sem); | ||
518 | return mem_value; | ||
519 | |||
520 | bad_access: | ||
521 | up_read(&mm->mmap_sem); | ||
522 | /* This is not necessarily a bad access, we can get here if | ||
523 | a memory we're trying to write to should be copied-on-write. | ||
524 | Make the kernel do the necessary page stuff, then re-iterate. | ||
525 | Simulate a write access fault to do that. */ | ||
526 | { | ||
527 | /* The first argument of the function corresponds to | ||
528 | D1, which is the first field of struct pt_regs. */ | ||
529 | struct pt_regs *fp = (struct pt_regs *)&newval; | ||
530 | |||
531 | /* '3' is an RMW flag. */ | ||
532 | if (do_page_fault(fp, (unsigned long)mem, 3)) | ||
533 | /* If the do_page_fault() failed, we don't | ||
534 | have anything meaningful to return. | ||
535 | There should be a SIGSEGV pending for | ||
536 | the process. */ | ||
537 | return 0xdeadbeef; | ||
538 | } | ||
539 | } | ||
540 | } | ||
541 | |||
542 | asmlinkage int sys_atomic_barrier(void) | ||
543 | { | ||
544 | /* no code needed for uniprocs */ | ||
545 | return 0; | ||
546 | } | ||
diff --git a/arch/m68k/kernel/sys_m68k_no.c b/arch/m68k/kernel/sys_m68k_no.c new file mode 100644 index 000000000000..68488ae47f0a --- /dev/null +++ b/arch/m68k/kernel/sys_m68k_no.c | |||
@@ -0,0 +1,94 @@ | |||
1 | /* | ||
2 | * linux/arch/m68knommu/kernel/sys_m68k.c | ||
3 | * | ||
4 | * This file contains various random system calls that | ||
5 | * have a non-standard calling sequence on the Linux/m68k | ||
6 | * platform. | ||
7 | */ | ||
8 | |||
9 | #include <linux/errno.h> | ||
10 | #include <linux/sched.h> | ||
11 | #include <linux/mm.h> | ||
12 | #include <linux/smp.h> | ||
13 | #include <linux/sem.h> | ||
14 | #include <linux/msg.h> | ||
15 | #include <linux/shm.h> | ||
16 | #include <linux/stat.h> | ||
17 | #include <linux/syscalls.h> | ||
18 | #include <linux/mman.h> | ||
19 | #include <linux/file.h> | ||
20 | #include <linux/ipc.h> | ||
21 | #include <linux/fs.h> | ||
22 | |||
23 | #include <asm/setup.h> | ||
24 | #include <asm/uaccess.h> | ||
25 | #include <asm/cachectl.h> | ||
26 | #include <asm/traps.h> | ||
27 | #include <asm/cacheflush.h> | ||
28 | #include <asm/unistd.h> | ||
29 | |||
30 | /* sys_cacheflush -- flush (part of) the processor cache. */ | ||
31 | asmlinkage int | ||
32 | sys_cacheflush (unsigned long addr, int scope, int cache, unsigned long len) | ||
33 | { | ||
34 | flush_cache_all(); | ||
35 | return(0); | ||
36 | } | ||
37 | |||
38 | asmlinkage int sys_getpagesize(void) | ||
39 | { | ||
40 | return PAGE_SIZE; | ||
41 | } | ||
42 | |||
43 | /* | ||
44 | * Do a system call from kernel instead of calling sys_execve so we | ||
45 | * end up with proper pt_regs. | ||
46 | */ | ||
47 | int kernel_execve(const char *filename, | ||
48 | const char *const argv[], | ||
49 | const char *const envp[]) | ||
50 | { | ||
51 | register long __res asm ("%d0") = __NR_execve; | ||
52 | register long __a asm ("%d1") = (long)(filename); | ||
53 | register long __b asm ("%d2") = (long)(argv); | ||
54 | register long __c asm ("%d3") = (long)(envp); | ||
55 | asm volatile ("trap #0" : "+d" (__res) | ||
56 | : "d" (__a), "d" (__b), "d" (__c)); | ||
57 | return __res; | ||
58 | } | ||
59 | |||
60 | asmlinkage unsigned long sys_get_thread_area(void) | ||
61 | { | ||
62 | return current_thread_info()->tp_value; | ||
63 | } | ||
64 | |||
65 | asmlinkage int sys_set_thread_area(unsigned long tp) | ||
66 | { | ||
67 | current_thread_info()->tp_value = tp; | ||
68 | return 0; | ||
69 | } | ||
70 | |||
71 | /* This syscall gets its arguments in A0 (mem), D2 (oldval) and | ||
72 | D1 (newval). */ | ||
73 | asmlinkage int | ||
74 | sys_atomic_cmpxchg_32(unsigned long newval, int oldval, int d3, int d4, int d5, | ||
75 | unsigned long __user * mem) | ||
76 | { | ||
77 | struct mm_struct *mm = current->mm; | ||
78 | unsigned long mem_value; | ||
79 | |||
80 | down_read(&mm->mmap_sem); | ||
81 | |||
82 | mem_value = *mem; | ||
83 | if (mem_value == oldval) | ||
84 | *mem = newval; | ||
85 | |||
86 | up_read(&mm->mmap_sem); | ||
87 | return mem_value; | ||
88 | } | ||
89 | |||
90 | asmlinkage int sys_atomic_barrier(void) | ||
91 | { | ||
92 | /* no code needed for uniprocs */ | ||
93 | return 0; | ||
94 | } | ||
diff --git a/arch/m68k/kernel/syscalltable.S b/arch/m68k/kernel/syscalltable.S new file mode 100644 index 000000000000..79b1ed198c07 --- /dev/null +++ b/arch/m68k/kernel/syscalltable.S | |||
@@ -0,0 +1,365 @@ | |||
1 | /* | ||
2 | * linux/arch/m68knommu/kernel/syscalltable.S | ||
3 | * | ||
4 | * Copyright (C) 2002, Greg Ungerer (gerg@snapgear.com) | ||
5 | * | ||
6 | * Based on older entry.S files, the following copyrights apply: | ||
7 | * | ||
8 | * Copyright (C) 1998 D. Jeff Dionne <jeff@lineo.ca>, | ||
9 | * Kenneth Albanowski <kjahds@kjahds.com>, | ||
10 | * Copyright (C) 2000 Lineo Inc. (www.lineo.com) | ||
11 | * Copyright (C) 1991, 1992 Linus Torvalds | ||
12 | */ | ||
13 | |||
14 | #include <linux/sys.h> | ||
15 | #include <linux/linkage.h> | ||
16 | #include <asm/unistd.h> | ||
17 | |||
18 | .text | ||
19 | ALIGN | ||
20 | ENTRY(sys_call_table) | ||
21 | .long sys_restart_syscall /* 0 - old "setup()" system call */ | ||
22 | .long sys_exit | ||
23 | .long sys_fork | ||
24 | .long sys_read | ||
25 | .long sys_write | ||
26 | .long sys_open /* 5 */ | ||
27 | .long sys_close | ||
28 | .long sys_waitpid | ||
29 | .long sys_creat | ||
30 | .long sys_link | ||
31 | .long sys_unlink /* 10 */ | ||
32 | .long sys_execve | ||
33 | .long sys_chdir | ||
34 | .long sys_time | ||
35 | .long sys_mknod | ||
36 | .long sys_chmod /* 15 */ | ||
37 | .long sys_chown16 | ||
38 | .long sys_ni_syscall /* old break syscall holder */ | ||
39 | .long sys_stat | ||
40 | .long sys_lseek | ||
41 | .long sys_getpid /* 20 */ | ||
42 | .long sys_mount | ||
43 | .long sys_oldumount | ||
44 | .long sys_setuid16 | ||
45 | .long sys_getuid16 | ||
46 | .long sys_stime /* 25 */ | ||
47 | .long sys_ptrace | ||
48 | .long sys_alarm | ||
49 | .long sys_fstat | ||
50 | .long sys_pause | ||
51 | .long sys_utime /* 30 */ | ||
52 | .long sys_ni_syscall /* old stty syscall holder */ | ||
53 | .long sys_ni_syscall /* old gtty syscall holder */ | ||
54 | .long sys_access | ||
55 | .long sys_nice | ||
56 | .long sys_ni_syscall /* 35 */ /* old ftime syscall holder */ | ||
57 | .long sys_sync | ||
58 | .long sys_kill | ||
59 | .long sys_rename | ||
60 | .long sys_mkdir | ||
61 | .long sys_rmdir /* 40 */ | ||
62 | .long sys_dup | ||
63 | .long sys_pipe | ||
64 | .long sys_times | ||
65 | .long sys_ni_syscall /* old prof syscall holder */ | ||
66 | .long sys_brk /* 45 */ | ||
67 | .long sys_setgid16 | ||
68 | .long sys_getgid16 | ||
69 | .long sys_signal | ||
70 | .long sys_geteuid16 | ||
71 | .long sys_getegid16 /* 50 */ | ||
72 | .long sys_acct | ||
73 | .long sys_umount /* recycled never used phys() */ | ||
74 | .long sys_ni_syscall /* old lock syscall holder */ | ||
75 | .long sys_ioctl | ||
76 | .long sys_fcntl /* 55 */ | ||
77 | .long sys_ni_syscall /* old mpx syscall holder */ | ||
78 | .long sys_setpgid | ||
79 | .long sys_ni_syscall /* old ulimit syscall holder */ | ||
80 | .long sys_ni_syscall | ||
81 | .long sys_umask /* 60 */ | ||
82 | .long sys_chroot | ||
83 | .long sys_ustat | ||
84 | .long sys_dup2 | ||
85 | .long sys_getppid | ||
86 | .long sys_getpgrp /* 65 */ | ||
87 | .long sys_setsid | ||
88 | .long sys_sigaction | ||
89 | .long sys_sgetmask | ||
90 | .long sys_ssetmask | ||
91 | .long sys_setreuid16 /* 70 */ | ||
92 | .long sys_setregid16 | ||
93 | .long sys_sigsuspend | ||
94 | .long sys_sigpending | ||
95 | .long sys_sethostname | ||
96 | .long sys_setrlimit /* 75 */ | ||
97 | .long sys_old_getrlimit | ||
98 | .long sys_getrusage | ||
99 | .long sys_gettimeofday | ||
100 | .long sys_settimeofday | ||
101 | .long sys_getgroups16 /* 80 */ | ||
102 | .long sys_setgroups16 | ||
103 | .long sys_old_select | ||
104 | .long sys_symlink | ||
105 | .long sys_lstat | ||
106 | .long sys_readlink /* 85 */ | ||
107 | .long sys_uselib | ||
108 | .long sys_ni_syscall /* sys_swapon */ | ||
109 | .long sys_reboot | ||
110 | .long sys_old_readdir | ||
111 | .long sys_old_mmap /* 90 */ | ||
112 | .long sys_munmap | ||
113 | .long sys_truncate | ||
114 | .long sys_ftruncate | ||
115 | .long sys_fchmod | ||
116 | .long sys_fchown16 /* 95 */ | ||
117 | .long sys_getpriority | ||
118 | .long sys_setpriority | ||
119 | .long sys_ni_syscall /* old profil syscall holder */ | ||
120 | .long sys_statfs | ||
121 | .long sys_fstatfs /* 100 */ | ||
122 | .long sys_ni_syscall /* ioperm for i386 */ | ||
123 | .long sys_socketcall | ||
124 | .long sys_syslog | ||
125 | .long sys_setitimer | ||
126 | .long sys_getitimer /* 105 */ | ||
127 | .long sys_newstat | ||
128 | .long sys_newlstat | ||
129 | .long sys_newfstat | ||
130 | .long sys_ni_syscall | ||
131 | .long sys_ni_syscall /* iopl for i386 */ /* 110 */ | ||
132 | .long sys_vhangup | ||
133 | .long sys_ni_syscall /* obsolete idle() syscall */ | ||
134 | .long sys_ni_syscall /* vm86old for i386 */ | ||
135 | .long sys_wait4 | ||
136 | .long sys_ni_syscall /* 115 */ /* sys_swapoff */ | ||
137 | .long sys_sysinfo | ||
138 | .long sys_ipc | ||
139 | .long sys_fsync | ||
140 | .long sys_sigreturn | ||
141 | .long sys_clone /* 120 */ | ||
142 | .long sys_setdomainname | ||
143 | .long sys_newuname | ||
144 | .long sys_cacheflush /* modify_ldt for i386 */ | ||
145 | .long sys_adjtimex | ||
146 | .long sys_ni_syscall /* 125 */ /* sys_mprotect */ | ||
147 | .long sys_sigprocmask | ||
148 | .long sys_ni_syscall /* old "creat_module" */ | ||
149 | .long sys_init_module | ||
150 | .long sys_delete_module | ||
151 | .long sys_ni_syscall /* 130: old "get_kernel_syms" */ | ||
152 | .long sys_quotactl | ||
153 | .long sys_getpgid | ||
154 | .long sys_fchdir | ||
155 | .long sys_bdflush | ||
156 | .long sys_sysfs /* 135 */ | ||
157 | .long sys_personality | ||
158 | .long sys_ni_syscall /* for afs_syscall */ | ||
159 | .long sys_setfsuid16 | ||
160 | .long sys_setfsgid16 | ||
161 | .long sys_llseek /* 140 */ | ||
162 | .long sys_getdents | ||
163 | .long sys_select | ||
164 | .long sys_flock | ||
165 | .long sys_ni_syscall /* sys_msync */ | ||
166 | .long sys_readv /* 145 */ | ||
167 | .long sys_writev | ||
168 | .long sys_getsid | ||
169 | .long sys_fdatasync | ||
170 | .long sys_sysctl | ||
171 | .long sys_ni_syscall /* 150 */ /* sys_mlock */ | ||
172 | .long sys_ni_syscall /* sys_munlock */ | ||
173 | .long sys_ni_syscall /* sys_mlockall */ | ||
174 | .long sys_ni_syscall /* sys_munlockall */ | ||
175 | .long sys_sched_setparam | ||
176 | .long sys_sched_getparam /* 155 */ | ||
177 | .long sys_sched_setscheduler | ||
178 | .long sys_sched_getscheduler | ||
179 | .long sys_sched_yield | ||
180 | .long sys_sched_get_priority_max | ||
181 | .long sys_sched_get_priority_min /* 160 */ | ||
182 | .long sys_sched_rr_get_interval | ||
183 | .long sys_nanosleep | ||
184 | .long sys_ni_syscall /* sys_mremap */ | ||
185 | .long sys_setresuid16 | ||
186 | .long sys_getresuid16 /* 165 */ | ||
187 | .long sys_getpagesize /* sys_getpagesize */ | ||
188 | .long sys_ni_syscall /* old "query_module" */ | ||
189 | .long sys_poll | ||
190 | .long sys_ni_syscall /* sys_nfsservctl */ | ||
191 | .long sys_setresgid16 /* 170 */ | ||
192 | .long sys_getresgid16 | ||
193 | .long sys_prctl | ||
194 | .long sys_rt_sigreturn | ||
195 | .long sys_rt_sigaction | ||
196 | .long sys_rt_sigprocmask /* 175 */ | ||
197 | .long sys_rt_sigpending | ||
198 | .long sys_rt_sigtimedwait | ||
199 | .long sys_rt_sigqueueinfo | ||
200 | .long sys_rt_sigsuspend | ||
201 | .long sys_pread64 /* 180 */ | ||
202 | .long sys_pwrite64 | ||
203 | .long sys_lchown16 | ||
204 | .long sys_getcwd | ||
205 | .long sys_capget | ||
206 | .long sys_capset /* 185 */ | ||
207 | .long sys_sigaltstack | ||
208 | .long sys_sendfile | ||
209 | .long sys_ni_syscall /* streams1 */ | ||
210 | .long sys_ni_syscall /* streams2 */ | ||
211 | .long sys_vfork /* 190 */ | ||
212 | .long sys_getrlimit | ||
213 | .long sys_mmap_pgoff | ||
214 | .long sys_truncate64 | ||
215 | .long sys_ftruncate64 | ||
216 | .long sys_stat64 /* 195 */ | ||
217 | .long sys_lstat64 | ||
218 | .long sys_fstat64 | ||
219 | .long sys_chown | ||
220 | .long sys_getuid | ||
221 | .long sys_getgid /* 200 */ | ||
222 | .long sys_geteuid | ||
223 | .long sys_getegid | ||
224 | .long sys_setreuid | ||
225 | .long sys_setregid | ||
226 | .long sys_getgroups /* 205 */ | ||
227 | .long sys_setgroups | ||
228 | .long sys_fchown | ||
229 | .long sys_setresuid | ||
230 | .long sys_getresuid | ||
231 | .long sys_setresgid /* 210 */ | ||
232 | .long sys_getresgid | ||
233 | .long sys_lchown | ||
234 | .long sys_setuid | ||
235 | .long sys_setgid | ||
236 | .long sys_setfsuid /* 215 */ | ||
237 | .long sys_setfsgid | ||
238 | .long sys_pivot_root | ||
239 | .long sys_ni_syscall | ||
240 | .long sys_ni_syscall | ||
241 | .long sys_getdents64 /* 220 */ | ||
242 | .long sys_gettid | ||
243 | .long sys_tkill | ||
244 | .long sys_setxattr | ||
245 | .long sys_lsetxattr | ||
246 | .long sys_fsetxattr /* 225 */ | ||
247 | .long sys_getxattr | ||
248 | .long sys_lgetxattr | ||
249 | .long sys_fgetxattr | ||
250 | .long sys_listxattr | ||
251 | .long sys_llistxattr /* 230 */ | ||
252 | .long sys_flistxattr | ||
253 | .long sys_removexattr | ||
254 | .long sys_lremovexattr | ||
255 | .long sys_fremovexattr | ||
256 | .long sys_futex /* 235 */ | ||
257 | .long sys_sendfile64 | ||
258 | .long sys_ni_syscall /* sys_mincore */ | ||
259 | .long sys_ni_syscall /* sys_madvise */ | ||
260 | .long sys_fcntl64 | ||
261 | .long sys_readahead /* 240 */ | ||
262 | .long sys_io_setup | ||
263 | .long sys_io_destroy | ||
264 | .long sys_io_getevents | ||
265 | .long sys_io_submit | ||
266 | .long sys_io_cancel /* 245 */ | ||
267 | .long sys_fadvise64 | ||
268 | .long sys_exit_group | ||
269 | .long sys_lookup_dcookie | ||
270 | .long sys_epoll_create | ||
271 | .long sys_epoll_ctl /* 250 */ | ||
272 | .long sys_epoll_wait | ||
273 | .long sys_ni_syscall /* sys_remap_file_pages */ | ||
274 | .long sys_set_tid_address | ||
275 | .long sys_timer_create | ||
276 | .long sys_timer_settime /* 255 */ | ||
277 | .long sys_timer_gettime | ||
278 | .long sys_timer_getoverrun | ||
279 | .long sys_timer_delete | ||
280 | .long sys_clock_settime | ||
281 | .long sys_clock_gettime /* 260 */ | ||
282 | .long sys_clock_getres | ||
283 | .long sys_clock_nanosleep | ||
284 | .long sys_statfs64 | ||
285 | .long sys_fstatfs64 | ||
286 | .long sys_tgkill /* 265 */ | ||
287 | .long sys_utimes | ||
288 | .long sys_fadvise64_64 | ||
289 | .long sys_mbind | ||
290 | .long sys_get_mempolicy | ||
291 | .long sys_set_mempolicy /* 270 */ | ||
292 | .long sys_mq_open | ||
293 | .long sys_mq_unlink | ||
294 | .long sys_mq_timedsend | ||
295 | .long sys_mq_timedreceive | ||
296 | .long sys_mq_notify /* 275 */ | ||
297 | .long sys_mq_getsetattr | ||
298 | .long sys_waitid | ||
299 | .long sys_ni_syscall /* for sys_vserver */ | ||
300 | .long sys_add_key | ||
301 | .long sys_request_key /* 280 */ | ||
302 | .long sys_keyctl | ||
303 | .long sys_ioprio_set | ||
304 | .long sys_ioprio_get | ||
305 | .long sys_inotify_init | ||
306 | .long sys_inotify_add_watch /* 285 */ | ||
307 | .long sys_inotify_rm_watch | ||
308 | .long sys_migrate_pages | ||
309 | .long sys_openat | ||
310 | .long sys_mkdirat | ||
311 | .long sys_mknodat /* 290 */ | ||
312 | .long sys_fchownat | ||
313 | .long sys_futimesat | ||
314 | .long sys_fstatat64 | ||
315 | .long sys_unlinkat | ||
316 | .long sys_renameat /* 295 */ | ||
317 | .long sys_linkat | ||
318 | .long sys_symlinkat | ||
319 | .long sys_readlinkat | ||
320 | .long sys_fchmodat | ||
321 | .long sys_faccessat /* 300 */ | ||
322 | .long sys_ni_syscall /* Reserved for pselect6 */ | ||
323 | .long sys_ni_syscall /* Reserved for ppoll */ | ||
324 | .long sys_unshare | ||
325 | .long sys_set_robust_list | ||
326 | .long sys_get_robust_list /* 305 */ | ||
327 | .long sys_splice | ||
328 | .long sys_sync_file_range | ||
329 | .long sys_tee | ||
330 | .long sys_vmsplice | ||
331 | .long sys_move_pages /* 310 */ | ||
332 | .long sys_sched_setaffinity | ||
333 | .long sys_sched_getaffinity | ||
334 | .long sys_kexec_load | ||
335 | .long sys_getcpu | ||
336 | .long sys_epoll_pwait /* 315 */ | ||
337 | .long sys_utimensat | ||
338 | .long sys_signalfd | ||
339 | .long sys_timerfd_create | ||
340 | .long sys_eventfd | ||
341 | .long sys_fallocate /* 320 */ | ||
342 | .long sys_timerfd_settime | ||
343 | .long sys_timerfd_gettime | ||
344 | .long sys_signalfd4 | ||
345 | .long sys_eventfd2 | ||
346 | .long sys_epoll_create1 /* 325 */ | ||
347 | .long sys_dup3 | ||
348 | .long sys_pipe2 | ||
349 | .long sys_inotify_init1 | ||
350 | .long sys_preadv | ||
351 | .long sys_pwritev /* 330 */ | ||
352 | .long sys_rt_tgsigqueueinfo | ||
353 | .long sys_perf_event_open | ||
354 | .long sys_get_thread_area | ||
355 | .long sys_set_thread_area | ||
356 | .long sys_atomic_cmpxchg_32 /* 335 */ | ||
357 | .long sys_atomic_barrier | ||
358 | .long sys_fanotify_init | ||
359 | .long sys_fanotify_mark | ||
360 | .long sys_prlimit64 | ||
361 | |||
362 | .rept NR_syscalls-(.-sys_call_table)/4 | ||
363 | .long sys_ni_syscall | ||
364 | .endr | ||
365 | |||
diff --git a/arch/m68k/kernel/time.c b/arch/m68k/kernel/time.c index 18b34ee5db3b..a5cf40c26de5 100644 --- a/arch/m68k/kernel/time.c +++ b/arch/m68k/kernel/time.c | |||
@@ -1,114 +1,5 @@ | |||
1 | /* | 1 | #ifdef CONFIG_MMU |
2 | * linux/arch/m68k/kernel/time.c | 2 | #include "time_mm.c" |
3 | * | 3 | #else |
4 | * Copyright (C) 1991, 1992, 1995 Linus Torvalds | 4 | #include "time_no.c" |
5 | * | 5 | #endif |
6 | * This file contains the m68k-specific time handling details. | ||
7 | * Most of the stuff is located in the machine specific files. | ||
8 | * | ||
9 | * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 | ||
10 | * "A Kernel Model for Precision Timekeeping" by Dave Mills | ||
11 | */ | ||
12 | |||
13 | #include <linux/errno.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/param.h> | ||
18 | #include <linux/string.h> | ||
19 | #include <linux/mm.h> | ||
20 | #include <linux/rtc.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | |||
23 | #include <asm/machdep.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/irq_regs.h> | ||
26 | |||
27 | #include <linux/time.h> | ||
28 | #include <linux/timex.h> | ||
29 | #include <linux/profile.h> | ||
30 | |||
31 | static inline int set_rtc_mmss(unsigned long nowtime) | ||
32 | { | ||
33 | if (mach_set_clock_mmss) | ||
34 | return mach_set_clock_mmss (nowtime); | ||
35 | return -1; | ||
36 | } | ||
37 | |||
38 | /* | ||
39 | * timer_interrupt() needs to keep up the real-time clock, | ||
40 | * as well as call the "xtime_update()" routine every clocktick | ||
41 | */ | ||
42 | static irqreturn_t timer_interrupt(int irq, void *dummy) | ||
43 | { | ||
44 | xtime_update(1); | ||
45 | update_process_times(user_mode(get_irq_regs())); | ||
46 | profile_tick(CPU_PROFILING); | ||
47 | |||
48 | #ifdef CONFIG_HEARTBEAT | ||
49 | /* use power LED as a heartbeat instead -- much more useful | ||
50 | for debugging -- based on the version for PReP by Cort */ | ||
51 | /* acts like an actual heart beat -- ie thump-thump-pause... */ | ||
52 | if (mach_heartbeat) { | ||
53 | static unsigned cnt = 0, period = 0, dist = 0; | ||
54 | |||
55 | if (cnt == 0 || cnt == dist) | ||
56 | mach_heartbeat( 1 ); | ||
57 | else if (cnt == 7 || cnt == dist+7) | ||
58 | mach_heartbeat( 0 ); | ||
59 | |||
60 | if (++cnt > period) { | ||
61 | cnt = 0; | ||
62 | /* The hyperbolic function below modifies the heartbeat period | ||
63 | * length in dependency of the current (5min) load. It goes | ||
64 | * through the points f(0)=126, f(1)=86, f(5)=51, | ||
65 | * f(inf)->30. */ | ||
66 | period = ((672<<FSHIFT)/(5*avenrun[0]+(7<<FSHIFT))) + 30; | ||
67 | dist = period / 4; | ||
68 | } | ||
69 | } | ||
70 | #endif /* CONFIG_HEARTBEAT */ | ||
71 | return IRQ_HANDLED; | ||
72 | } | ||
73 | |||
74 | void read_persistent_clock(struct timespec *ts) | ||
75 | { | ||
76 | struct rtc_time time; | ||
77 | ts->tv_sec = 0; | ||
78 | ts->tv_nsec = 0; | ||
79 | |||
80 | if (mach_hwclk) { | ||
81 | mach_hwclk(0, &time); | ||
82 | |||
83 | if ((time.tm_year += 1900) < 1970) | ||
84 | time.tm_year += 100; | ||
85 | ts->tv_sec = mktime(time.tm_year, time.tm_mon, time.tm_mday, | ||
86 | time.tm_hour, time.tm_min, time.tm_sec); | ||
87 | } | ||
88 | } | ||
89 | |||
90 | void __init time_init(void) | ||
91 | { | ||
92 | mach_sched_init(timer_interrupt); | ||
93 | } | ||
94 | |||
95 | u32 arch_gettimeoffset(void) | ||
96 | { | ||
97 | return mach_gettimeoffset() * 1000; | ||
98 | } | ||
99 | |||
100 | static int __init rtc_init(void) | ||
101 | { | ||
102 | struct platform_device *pdev; | ||
103 | |||
104 | if (!mach_hwclk) | ||
105 | return -ENODEV; | ||
106 | |||
107 | pdev = platform_device_register_simple("rtc-generic", -1, NULL, 0); | ||
108 | if (IS_ERR(pdev)) | ||
109 | return PTR_ERR(pdev); | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | module_init(rtc_init); | ||
diff --git a/arch/m68k/kernel/time_mm.c b/arch/m68k/kernel/time_mm.c new file mode 100644 index 000000000000..18b34ee5db3b --- /dev/null +++ b/arch/m68k/kernel/time_mm.c | |||
@@ -0,0 +1,114 @@ | |||
1 | /* | ||
2 | * linux/arch/m68k/kernel/time.c | ||
3 | * | ||
4 | * Copyright (C) 1991, 1992, 1995 Linus Torvalds | ||
5 | * | ||
6 | * This file contains the m68k-specific time handling details. | ||
7 | * Most of the stuff is located in the machine specific files. | ||
8 | * | ||
9 | * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 | ||
10 | * "A Kernel Model for Precision Timekeeping" by Dave Mills | ||
11 | */ | ||
12 | |||
13 | #include <linux/errno.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/param.h> | ||
18 | #include <linux/string.h> | ||
19 | #include <linux/mm.h> | ||
20 | #include <linux/rtc.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | |||
23 | #include <asm/machdep.h> | ||
24 | #include <asm/io.h> | ||
25 | #include <asm/irq_regs.h> | ||
26 | |||
27 | #include <linux/time.h> | ||
28 | #include <linux/timex.h> | ||
29 | #include <linux/profile.h> | ||
30 | |||
31 | static inline int set_rtc_mmss(unsigned long nowtime) | ||
32 | { | ||
33 | if (mach_set_clock_mmss) | ||
34 | return mach_set_clock_mmss (nowtime); | ||
35 | return -1; | ||
36 | } | ||
37 | |||
38 | /* | ||
39 | * timer_interrupt() needs to keep up the real-time clock, | ||
40 | * as well as call the "xtime_update()" routine every clocktick | ||
41 | */ | ||
42 | static irqreturn_t timer_interrupt(int irq, void *dummy) | ||
43 | { | ||
44 | xtime_update(1); | ||
45 | update_process_times(user_mode(get_irq_regs())); | ||
46 | profile_tick(CPU_PROFILING); | ||
47 | |||
48 | #ifdef CONFIG_HEARTBEAT | ||
49 | /* use power LED as a heartbeat instead -- much more useful | ||
50 | for debugging -- based on the version for PReP by Cort */ | ||
51 | /* acts like an actual heart beat -- ie thump-thump-pause... */ | ||
52 | if (mach_heartbeat) { | ||
53 | static unsigned cnt = 0, period = 0, dist = 0; | ||
54 | |||
55 | if (cnt == 0 || cnt == dist) | ||
56 | mach_heartbeat( 1 ); | ||
57 | else if (cnt == 7 || cnt == dist+7) | ||
58 | mach_heartbeat( 0 ); | ||
59 | |||
60 | if (++cnt > period) { | ||
61 | cnt = 0; | ||
62 | /* The hyperbolic function below modifies the heartbeat period | ||
63 | * length in dependency of the current (5min) load. It goes | ||
64 | * through the points f(0)=126, f(1)=86, f(5)=51, | ||
65 | * f(inf)->30. */ | ||
66 | period = ((672<<FSHIFT)/(5*avenrun[0]+(7<<FSHIFT))) + 30; | ||
67 | dist = period / 4; | ||
68 | } | ||
69 | } | ||
70 | #endif /* CONFIG_HEARTBEAT */ | ||
71 | return IRQ_HANDLED; | ||
72 | } | ||
73 | |||
74 | void read_persistent_clock(struct timespec *ts) | ||
75 | { | ||
76 | struct rtc_time time; | ||
77 | ts->tv_sec = 0; | ||
78 | ts->tv_nsec = 0; | ||
79 | |||
80 | if (mach_hwclk) { | ||
81 | mach_hwclk(0, &time); | ||
82 | |||
83 | if ((time.tm_year += 1900) < 1970) | ||
84 | time.tm_year += 100; | ||
85 | ts->tv_sec = mktime(time.tm_year, time.tm_mon, time.tm_mday, | ||
86 | time.tm_hour, time.tm_min, time.tm_sec); | ||
87 | } | ||
88 | } | ||
89 | |||
90 | void __init time_init(void) | ||
91 | { | ||
92 | mach_sched_init(timer_interrupt); | ||
93 | } | ||
94 | |||
95 | u32 arch_gettimeoffset(void) | ||
96 | { | ||
97 | return mach_gettimeoffset() * 1000; | ||
98 | } | ||
99 | |||
100 | static int __init rtc_init(void) | ||
101 | { | ||
102 | struct platform_device *pdev; | ||
103 | |||
104 | if (!mach_hwclk) | ||
105 | return -ENODEV; | ||
106 | |||
107 | pdev = platform_device_register_simple("rtc-generic", -1, NULL, 0); | ||
108 | if (IS_ERR(pdev)) | ||
109 | return PTR_ERR(pdev); | ||
110 | |||
111 | return 0; | ||
112 | } | ||
113 | |||
114 | module_init(rtc_init); | ||
diff --git a/arch/m68k/kernel/time_no.c b/arch/m68k/kernel/time_no.c new file mode 100644 index 000000000000..6623909f70e6 --- /dev/null +++ b/arch/m68k/kernel/time_no.c | |||
@@ -0,0 +1,87 @@ | |||
1 | /* | ||
2 | * linux/arch/m68knommu/kernel/time.c | ||
3 | * | ||
4 | * Copyright (C) 1991, 1992, 1995 Linus Torvalds | ||
5 | * | ||
6 | * This file contains the m68k-specific time handling details. | ||
7 | * Most of the stuff is located in the machine specific files. | ||
8 | * | ||
9 | * 1997-09-10 Updated NTP code according to technical memorandum Jan '96 | ||
10 | * "A Kernel Model for Precision Timekeeping" by Dave Mills | ||
11 | */ | ||
12 | |||
13 | #include <linux/errno.h> | ||
14 | #include <linux/module.h> | ||
15 | #include <linux/sched.h> | ||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/param.h> | ||
18 | #include <linux/string.h> | ||
19 | #include <linux/mm.h> | ||
20 | #include <linux/profile.h> | ||
21 | #include <linux/time.h> | ||
22 | #include <linux/timex.h> | ||
23 | |||
24 | #include <asm/machdep.h> | ||
25 | #include <asm/irq_regs.h> | ||
26 | |||
27 | #define TICK_SIZE (tick_nsec / 1000) | ||
28 | |||
29 | static inline int set_rtc_mmss(unsigned long nowtime) | ||
30 | { | ||
31 | if (mach_set_clock_mmss) | ||
32 | return mach_set_clock_mmss (nowtime); | ||
33 | return -1; | ||
34 | } | ||
35 | |||
36 | #ifndef CONFIG_GENERIC_CLOCKEVENTS | ||
37 | /* | ||
38 | * timer_interrupt() needs to keep up the real-time clock, | ||
39 | * as well as call the "xtime_update()" routine every clocktick | ||
40 | */ | ||
41 | irqreturn_t arch_timer_interrupt(int irq, void *dummy) | ||
42 | { | ||
43 | |||
44 | if (current->pid) | ||
45 | profile_tick(CPU_PROFILING); | ||
46 | |||
47 | xtime_update(1); | ||
48 | |||
49 | update_process_times(user_mode(get_irq_regs())); | ||
50 | |||
51 | return(IRQ_HANDLED); | ||
52 | } | ||
53 | #endif | ||
54 | |||
55 | static unsigned long read_rtc_mmss(void) | ||
56 | { | ||
57 | unsigned int year, mon, day, hour, min, sec; | ||
58 | |||
59 | if (mach_gettod) { | ||
60 | mach_gettod(&year, &mon, &day, &hour, &min, &sec); | ||
61 | if ((year += 1900) < 1970) | ||
62 | year += 100; | ||
63 | } else { | ||
64 | year = 1970; | ||
65 | mon = day = 1; | ||
66 | hour = min = sec = 0; | ||
67 | } | ||
68 | |||
69 | |||
70 | return mktime(year, mon, day, hour, min, sec); | ||
71 | } | ||
72 | |||
73 | void read_persistent_clock(struct timespec *ts) | ||
74 | { | ||
75 | ts->tv_sec = read_rtc_mmss(); | ||
76 | ts->tv_nsec = 0; | ||
77 | } | ||
78 | |||
79 | int update_persistent_clock(struct timespec now) | ||
80 | { | ||
81 | return set_rtc_mmss(now.tv_sec); | ||
82 | } | ||
83 | |||
84 | void time_init(void) | ||
85 | { | ||
86 | hw_timer_init(); | ||
87 | } | ||
diff --git a/arch/m68k/kernel/traps.c b/arch/m68k/kernel/traps.c index 4022bbc28878..c98add3f5f0f 100644 --- a/arch/m68k/kernel/traps.c +++ b/arch/m68k/kernel/traps.c | |||
@@ -1,1207 +1,5 @@ | |||
1 | /* | 1 | #ifdef CONFIG_MMU |
2 | * linux/arch/m68k/kernel/traps.c | 2 | #include "traps_mm.c" |
3 | * | ||
4 | * Copyright (C) 1993, 1994 by Hamish Macdonald | ||
5 | * | ||
6 | * 68040 fixes by Michael Rausch | ||
7 | * 68040 fixes by Martin Apel | ||
8 | * 68040 fixes and writeback by Richard Zidlicky | ||
9 | * 68060 fixes by Roman Hodek | ||
10 | * 68060 fixes by Jesper Skov | ||
11 | * | ||
12 | * This file is subject to the terms and conditions of the GNU General Public | ||
13 | * License. See the file COPYING in the main directory of this archive | ||
14 | * for more details. | ||
15 | */ | ||
16 | |||
17 | /* | ||
18 | * Sets up all exception vectors | ||
19 | */ | ||
20 | |||
21 | #include <linux/sched.h> | ||
22 | #include <linux/signal.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/mm.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/user.h> | ||
27 | #include <linux/string.h> | ||
28 | #include <linux/linkage.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/ptrace.h> | ||
31 | #include <linux/kallsyms.h> | ||
32 | |||
33 | #include <asm/setup.h> | ||
34 | #include <asm/fpu.h> | ||
35 | #include <asm/system.h> | ||
36 | #include <asm/uaccess.h> | ||
37 | #include <asm/traps.h> | ||
38 | #include <asm/pgalloc.h> | ||
39 | #include <asm/machdep.h> | ||
40 | #include <asm/siginfo.h> | ||
41 | |||
42 | /* assembler routines */ | ||
43 | asmlinkage void system_call(void); | ||
44 | asmlinkage void buserr(void); | ||
45 | asmlinkage void trap(void); | ||
46 | asmlinkage void nmihandler(void); | ||
47 | #ifdef CONFIG_M68KFPU_EMU | ||
48 | asmlinkage void fpu_emu(void); | ||
49 | #endif | ||
50 | |||
51 | e_vector vectors[256]; | ||
52 | |||
53 | /* nmi handler for the Amiga */ | ||
54 | asm(".text\n" | ||
55 | __ALIGN_STR "\n" | ||
56 | "nmihandler: rte"); | ||
57 | |||
58 | /* | ||
59 | * this must be called very early as the kernel might | ||
60 | * use some instruction that are emulated on the 060 | ||
61 | * and so we're prepared for early probe attempts (e.g. nf_init). | ||
62 | */ | ||
63 | void __init base_trap_init(void) | ||
64 | { | ||
65 | if (MACH_IS_SUN3X) { | ||
66 | extern e_vector *sun3x_prom_vbr; | ||
67 | |||
68 | __asm__ volatile ("movec %%vbr, %0" : "=r" (sun3x_prom_vbr)); | ||
69 | } | ||
70 | |||
71 | /* setup the exception vector table */ | ||
72 | __asm__ volatile ("movec %0,%%vbr" : : "r" ((void*)vectors)); | ||
73 | |||
74 | if (CPU_IS_060) { | ||
75 | /* set up ISP entry points */ | ||
76 | asmlinkage void unimp_vec(void) asm ("_060_isp_unimp"); | ||
77 | |||
78 | vectors[VEC_UNIMPII] = unimp_vec; | ||
79 | } | ||
80 | |||
81 | vectors[VEC_BUSERR] = buserr; | ||
82 | vectors[VEC_ILLEGAL] = trap; | ||
83 | vectors[VEC_SYS] = system_call; | ||
84 | } | ||
85 | |||
86 | void __init trap_init (void) | ||
87 | { | ||
88 | int i; | ||
89 | |||
90 | for (i = VEC_SPUR; i <= VEC_INT7; i++) | ||
91 | vectors[i] = bad_inthandler; | ||
92 | |||
93 | for (i = 0; i < VEC_USER; i++) | ||
94 | if (!vectors[i]) | ||
95 | vectors[i] = trap; | ||
96 | |||
97 | for (i = VEC_USER; i < 256; i++) | ||
98 | vectors[i] = bad_inthandler; | ||
99 | |||
100 | #ifdef CONFIG_M68KFPU_EMU | ||
101 | if (FPU_IS_EMU) | ||
102 | vectors[VEC_LINE11] = fpu_emu; | ||
103 | #endif | ||
104 | |||
105 | if (CPU_IS_040 && !FPU_IS_EMU) { | ||
106 | /* set up FPSP entry points */ | ||
107 | asmlinkage void dz_vec(void) asm ("dz"); | ||
108 | asmlinkage void inex_vec(void) asm ("inex"); | ||
109 | asmlinkage void ovfl_vec(void) asm ("ovfl"); | ||
110 | asmlinkage void unfl_vec(void) asm ("unfl"); | ||
111 | asmlinkage void snan_vec(void) asm ("snan"); | ||
112 | asmlinkage void operr_vec(void) asm ("operr"); | ||
113 | asmlinkage void bsun_vec(void) asm ("bsun"); | ||
114 | asmlinkage void fline_vec(void) asm ("fline"); | ||
115 | asmlinkage void unsupp_vec(void) asm ("unsupp"); | ||
116 | |||
117 | vectors[VEC_FPDIVZ] = dz_vec; | ||
118 | vectors[VEC_FPIR] = inex_vec; | ||
119 | vectors[VEC_FPOVER] = ovfl_vec; | ||
120 | vectors[VEC_FPUNDER] = unfl_vec; | ||
121 | vectors[VEC_FPNAN] = snan_vec; | ||
122 | vectors[VEC_FPOE] = operr_vec; | ||
123 | vectors[VEC_FPBRUC] = bsun_vec; | ||
124 | vectors[VEC_LINE11] = fline_vec; | ||
125 | vectors[VEC_FPUNSUP] = unsupp_vec; | ||
126 | } | ||
127 | |||
128 | if (CPU_IS_060 && !FPU_IS_EMU) { | ||
129 | /* set up IFPSP entry points */ | ||
130 | asmlinkage void snan_vec6(void) asm ("_060_fpsp_snan"); | ||
131 | asmlinkage void operr_vec6(void) asm ("_060_fpsp_operr"); | ||
132 | asmlinkage void ovfl_vec6(void) asm ("_060_fpsp_ovfl"); | ||
133 | asmlinkage void unfl_vec6(void) asm ("_060_fpsp_unfl"); | ||
134 | asmlinkage void dz_vec6(void) asm ("_060_fpsp_dz"); | ||
135 | asmlinkage void inex_vec6(void) asm ("_060_fpsp_inex"); | ||
136 | asmlinkage void fline_vec6(void) asm ("_060_fpsp_fline"); | ||
137 | asmlinkage void unsupp_vec6(void) asm ("_060_fpsp_unsupp"); | ||
138 | asmlinkage void effadd_vec6(void) asm ("_060_fpsp_effadd"); | ||
139 | |||
140 | vectors[VEC_FPNAN] = snan_vec6; | ||
141 | vectors[VEC_FPOE] = operr_vec6; | ||
142 | vectors[VEC_FPOVER] = ovfl_vec6; | ||
143 | vectors[VEC_FPUNDER] = unfl_vec6; | ||
144 | vectors[VEC_FPDIVZ] = dz_vec6; | ||
145 | vectors[VEC_FPIR] = inex_vec6; | ||
146 | vectors[VEC_LINE11] = fline_vec6; | ||
147 | vectors[VEC_FPUNSUP] = unsupp_vec6; | ||
148 | vectors[VEC_UNIMPEA] = effadd_vec6; | ||
149 | } | ||
150 | |||
151 | /* if running on an amiga, make the NMI interrupt do nothing */ | ||
152 | if (MACH_IS_AMIGA) { | ||
153 | vectors[VEC_INT7] = nmihandler; | ||
154 | } | ||
155 | } | ||
156 | |||
157 | |||
158 | static const char *vec_names[] = { | ||
159 | [VEC_RESETSP] = "RESET SP", | ||
160 | [VEC_RESETPC] = "RESET PC", | ||
161 | [VEC_BUSERR] = "BUS ERROR", | ||
162 | [VEC_ADDRERR] = "ADDRESS ERROR", | ||
163 | [VEC_ILLEGAL] = "ILLEGAL INSTRUCTION", | ||
164 | [VEC_ZERODIV] = "ZERO DIVIDE", | ||
165 | [VEC_CHK] = "CHK", | ||
166 | [VEC_TRAP] = "TRAPcc", | ||
167 | [VEC_PRIV] = "PRIVILEGE VIOLATION", | ||
168 | [VEC_TRACE] = "TRACE", | ||
169 | [VEC_LINE10] = "LINE 1010", | ||
170 | [VEC_LINE11] = "LINE 1111", | ||
171 | [VEC_RESV12] = "UNASSIGNED RESERVED 12", | ||
172 | [VEC_COPROC] = "COPROCESSOR PROTOCOL VIOLATION", | ||
173 | [VEC_FORMAT] = "FORMAT ERROR", | ||
174 | [VEC_UNINT] = "UNINITIALIZED INTERRUPT", | ||
175 | [VEC_RESV16] = "UNASSIGNED RESERVED 16", | ||
176 | [VEC_RESV17] = "UNASSIGNED RESERVED 17", | ||
177 | [VEC_RESV18] = "UNASSIGNED RESERVED 18", | ||
178 | [VEC_RESV19] = "UNASSIGNED RESERVED 19", | ||
179 | [VEC_RESV20] = "UNASSIGNED RESERVED 20", | ||
180 | [VEC_RESV21] = "UNASSIGNED RESERVED 21", | ||
181 | [VEC_RESV22] = "UNASSIGNED RESERVED 22", | ||
182 | [VEC_RESV23] = "UNASSIGNED RESERVED 23", | ||
183 | [VEC_SPUR] = "SPURIOUS INTERRUPT", | ||
184 | [VEC_INT1] = "LEVEL 1 INT", | ||
185 | [VEC_INT2] = "LEVEL 2 INT", | ||
186 | [VEC_INT3] = "LEVEL 3 INT", | ||
187 | [VEC_INT4] = "LEVEL 4 INT", | ||
188 | [VEC_INT5] = "LEVEL 5 INT", | ||
189 | [VEC_INT6] = "LEVEL 6 INT", | ||
190 | [VEC_INT7] = "LEVEL 7 INT", | ||
191 | [VEC_SYS] = "SYSCALL", | ||
192 | [VEC_TRAP1] = "TRAP #1", | ||
193 | [VEC_TRAP2] = "TRAP #2", | ||
194 | [VEC_TRAP3] = "TRAP #3", | ||
195 | [VEC_TRAP4] = "TRAP #4", | ||
196 | [VEC_TRAP5] = "TRAP #5", | ||
197 | [VEC_TRAP6] = "TRAP #6", | ||
198 | [VEC_TRAP7] = "TRAP #7", | ||
199 | [VEC_TRAP8] = "TRAP #8", | ||
200 | [VEC_TRAP9] = "TRAP #9", | ||
201 | [VEC_TRAP10] = "TRAP #10", | ||
202 | [VEC_TRAP11] = "TRAP #11", | ||
203 | [VEC_TRAP12] = "TRAP #12", | ||
204 | [VEC_TRAP13] = "TRAP #13", | ||
205 | [VEC_TRAP14] = "TRAP #14", | ||
206 | [VEC_TRAP15] = "TRAP #15", | ||
207 | [VEC_FPBRUC] = "FPCP BSUN", | ||
208 | [VEC_FPIR] = "FPCP INEXACT", | ||
209 | [VEC_FPDIVZ] = "FPCP DIV BY 0", | ||
210 | [VEC_FPUNDER] = "FPCP UNDERFLOW", | ||
211 | [VEC_FPOE] = "FPCP OPERAND ERROR", | ||
212 | [VEC_FPOVER] = "FPCP OVERFLOW", | ||
213 | [VEC_FPNAN] = "FPCP SNAN", | ||
214 | [VEC_FPUNSUP] = "FPCP UNSUPPORTED OPERATION", | ||
215 | [VEC_MMUCFG] = "MMU CONFIGURATION ERROR", | ||
216 | [VEC_MMUILL] = "MMU ILLEGAL OPERATION ERROR", | ||
217 | [VEC_MMUACC] = "MMU ACCESS LEVEL VIOLATION ERROR", | ||
218 | [VEC_RESV59] = "UNASSIGNED RESERVED 59", | ||
219 | [VEC_UNIMPEA] = "UNASSIGNED RESERVED 60", | ||
220 | [VEC_UNIMPII] = "UNASSIGNED RESERVED 61", | ||
221 | [VEC_RESV62] = "UNASSIGNED RESERVED 62", | ||
222 | [VEC_RESV63] = "UNASSIGNED RESERVED 63", | ||
223 | }; | ||
224 | |||
225 | static const char *space_names[] = { | ||
226 | [0] = "Space 0", | ||
227 | [USER_DATA] = "User Data", | ||
228 | [USER_PROGRAM] = "User Program", | ||
229 | #ifndef CONFIG_SUN3 | ||
230 | [3] = "Space 3", | ||
231 | #else | 3 | #else |
232 | [FC_CONTROL] = "Control", | 4 | #include "traps_no.c" |
233 | #endif | ||
234 | [4] = "Space 4", | ||
235 | [SUPER_DATA] = "Super Data", | ||
236 | [SUPER_PROGRAM] = "Super Program", | ||
237 | [CPU_SPACE] = "CPU" | ||
238 | }; | ||
239 | |||
240 | void die_if_kernel(char *,struct pt_regs *,int); | ||
241 | asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, | ||
242 | unsigned long error_code); | ||
243 | int send_fault_sig(struct pt_regs *regs); | ||
244 | |||
245 | asmlinkage void trap_c(struct frame *fp); | ||
246 | |||
247 | #if defined (CONFIG_M68060) | ||
248 | static inline void access_error060 (struct frame *fp) | ||
249 | { | ||
250 | unsigned long fslw = fp->un.fmt4.pc; /* is really FSLW for access error */ | ||
251 | |||
252 | #ifdef DEBUG | ||
253 | printk("fslw=%#lx, fa=%#lx\n", fslw, fp->un.fmt4.effaddr); | ||
254 | #endif | ||
255 | |||
256 | if (fslw & MMU060_BPE) { | ||
257 | /* branch prediction error -> clear branch cache */ | ||
258 | __asm__ __volatile__ ("movec %/cacr,%/d0\n\t" | ||
259 | "orl #0x00400000,%/d0\n\t" | ||
260 | "movec %/d0,%/cacr" | ||
261 | : : : "d0" ); | ||
262 | /* return if there's no other error */ | ||
263 | if (!(fslw & MMU060_ERR_BITS) && !(fslw & MMU060_SEE)) | ||
264 | return; | ||
265 | } | ||
266 | |||
267 | if (fslw & (MMU060_DESC_ERR | MMU060_WP | MMU060_SP)) { | ||
268 | unsigned long errorcode; | ||
269 | unsigned long addr = fp->un.fmt4.effaddr; | ||
270 | |||
271 | if (fslw & MMU060_MA) | ||
272 | addr = (addr + PAGE_SIZE - 1) & PAGE_MASK; | ||
273 | |||
274 | errorcode = 1; | ||
275 | if (fslw & MMU060_DESC_ERR) { | ||
276 | __flush_tlb040_one(addr); | ||
277 | errorcode = 0; | ||
278 | } | ||
279 | if (fslw & MMU060_W) | ||
280 | errorcode |= 2; | ||
281 | #ifdef DEBUG | ||
282 | printk("errorcode = %d\n", errorcode ); | ||
283 | #endif | ||
284 | do_page_fault(&fp->ptregs, addr, errorcode); | ||
285 | } else if (fslw & (MMU060_SEE)){ | ||
286 | /* Software Emulation Error. | ||
287 | * fault during mem_read/mem_write in ifpsp060/os.S | ||
288 | */ | ||
289 | send_fault_sig(&fp->ptregs); | ||
290 | } else if (!(fslw & (MMU060_RE|MMU060_WE)) || | ||
291 | send_fault_sig(&fp->ptregs) > 0) { | ||
292 | printk("pc=%#lx, fa=%#lx\n", fp->ptregs.pc, fp->un.fmt4.effaddr); | ||
293 | printk( "68060 access error, fslw=%lx\n", fslw ); | ||
294 | trap_c( fp ); | ||
295 | } | ||
296 | } | ||
297 | #endif /* CONFIG_M68060 */ | ||
298 | |||
299 | #if defined (CONFIG_M68040) | ||
300 | static inline unsigned long probe040(int iswrite, unsigned long addr, int wbs) | ||
301 | { | ||
302 | unsigned long mmusr; | ||
303 | mm_segment_t old_fs = get_fs(); | ||
304 | |||
305 | set_fs(MAKE_MM_SEG(wbs)); | ||
306 | |||
307 | if (iswrite) | ||
308 | asm volatile (".chip 68040; ptestw (%0); .chip 68k" : : "a" (addr)); | ||
309 | else | ||
310 | asm volatile (".chip 68040; ptestr (%0); .chip 68k" : : "a" (addr)); | ||
311 | |||
312 | asm volatile (".chip 68040; movec %%mmusr,%0; .chip 68k" : "=r" (mmusr)); | ||
313 | |||
314 | set_fs(old_fs); | ||
315 | |||
316 | return mmusr; | ||
317 | } | ||
318 | |||
319 | static inline int do_040writeback1(unsigned short wbs, unsigned long wba, | ||
320 | unsigned long wbd) | ||
321 | { | ||
322 | int res = 0; | ||
323 | mm_segment_t old_fs = get_fs(); | ||
324 | |||
325 | /* set_fs can not be moved, otherwise put_user() may oops */ | ||
326 | set_fs(MAKE_MM_SEG(wbs)); | ||
327 | |||
328 | switch (wbs & WBSIZ_040) { | ||
329 | case BA_SIZE_BYTE: | ||
330 | res = put_user(wbd & 0xff, (char __user *)wba); | ||
331 | break; | ||
332 | case BA_SIZE_WORD: | ||
333 | res = put_user(wbd & 0xffff, (short __user *)wba); | ||
334 | break; | ||
335 | case BA_SIZE_LONG: | ||
336 | res = put_user(wbd, (int __user *)wba); | ||
337 | break; | ||
338 | } | ||
339 | |||
340 | /* set_fs can not be moved, otherwise put_user() may oops */ | ||
341 | set_fs(old_fs); | ||
342 | |||
343 | |||
344 | #ifdef DEBUG | ||
345 | printk("do_040writeback1, res=%d\n",res); | ||
346 | #endif | ||
347 | |||
348 | return res; | ||
349 | } | ||
350 | |||
351 | /* after an exception in a writeback the stack frame corresponding | ||
352 | * to that exception is discarded, set a few bits in the old frame | ||
353 | * to simulate what it should look like | ||
354 | */ | ||
355 | static inline void fix_xframe040(struct frame *fp, unsigned long wba, unsigned short wbs) | ||
356 | { | ||
357 | fp->un.fmt7.faddr = wba; | ||
358 | fp->un.fmt7.ssw = wbs & 0xff; | ||
359 | if (wba != current->thread.faddr) | ||
360 | fp->un.fmt7.ssw |= MA_040; | ||
361 | } | ||
362 | |||
363 | static inline void do_040writebacks(struct frame *fp) | ||
364 | { | ||
365 | int res = 0; | ||
366 | #if 0 | ||
367 | if (fp->un.fmt7.wb1s & WBV_040) | ||
368 | printk("access_error040: cannot handle 1st writeback. oops.\n"); | ||
369 | #endif | ||
370 | |||
371 | if ((fp->un.fmt7.wb2s & WBV_040) && | ||
372 | !(fp->un.fmt7.wb2s & WBTT_040)) { | ||
373 | res = do_040writeback1(fp->un.fmt7.wb2s, fp->un.fmt7.wb2a, | ||
374 | fp->un.fmt7.wb2d); | ||
375 | if (res) | ||
376 | fix_xframe040(fp, fp->un.fmt7.wb2a, fp->un.fmt7.wb2s); | ||
377 | else | ||
378 | fp->un.fmt7.wb2s = 0; | ||
379 | } | ||
380 | |||
381 | /* do the 2nd wb only if the first one was successful (except for a kernel wb) */ | ||
382 | if (fp->un.fmt7.wb3s & WBV_040 && (!res || fp->un.fmt7.wb3s & 4)) { | ||
383 | res = do_040writeback1(fp->un.fmt7.wb3s, fp->un.fmt7.wb3a, | ||
384 | fp->un.fmt7.wb3d); | ||
385 | if (res) | ||
386 | { | ||
387 | fix_xframe040(fp, fp->un.fmt7.wb3a, fp->un.fmt7.wb3s); | ||
388 | |||
389 | fp->un.fmt7.wb2s = fp->un.fmt7.wb3s; | ||
390 | fp->un.fmt7.wb3s &= (~WBV_040); | ||
391 | fp->un.fmt7.wb2a = fp->un.fmt7.wb3a; | ||
392 | fp->un.fmt7.wb2d = fp->un.fmt7.wb3d; | ||
393 | } | ||
394 | else | ||
395 | fp->un.fmt7.wb3s = 0; | ||
396 | } | ||
397 | |||
398 | if (res) | ||
399 | send_fault_sig(&fp->ptregs); | ||
400 | } | ||
401 | |||
402 | /* | ||
403 | * called from sigreturn(), must ensure userspace code didn't | ||
404 | * manipulate exception frame to circumvent protection, then complete | ||
405 | * pending writebacks | ||
406 | * we just clear TM2 to turn it into a userspace access | ||
407 | */ | ||
408 | asmlinkage void berr_040cleanup(struct frame *fp) | ||
409 | { | ||
410 | fp->un.fmt7.wb2s &= ~4; | ||
411 | fp->un.fmt7.wb3s &= ~4; | ||
412 | |||
413 | do_040writebacks(fp); | ||
414 | } | ||
415 | |||
416 | static inline void access_error040(struct frame *fp) | ||
417 | { | ||
418 | unsigned short ssw = fp->un.fmt7.ssw; | ||
419 | unsigned long mmusr; | ||
420 | |||
421 | #ifdef DEBUG | ||
422 | printk("ssw=%#x, fa=%#lx\n", ssw, fp->un.fmt7.faddr); | ||
423 | printk("wb1s=%#x, wb2s=%#x, wb3s=%#x\n", fp->un.fmt7.wb1s, | ||
424 | fp->un.fmt7.wb2s, fp->un.fmt7.wb3s); | ||
425 | printk ("wb2a=%lx, wb3a=%lx, wb2d=%lx, wb3d=%lx\n", | ||
426 | fp->un.fmt7.wb2a, fp->un.fmt7.wb3a, | ||
427 | fp->un.fmt7.wb2d, fp->un.fmt7.wb3d); | ||
428 | #endif | ||
429 | |||
430 | if (ssw & ATC_040) { | ||
431 | unsigned long addr = fp->un.fmt7.faddr; | ||
432 | unsigned long errorcode; | ||
433 | |||
434 | /* | ||
435 | * The MMU status has to be determined AFTER the address | ||
436 | * has been corrected if there was a misaligned access (MA). | ||
437 | */ | ||
438 | if (ssw & MA_040) | ||
439 | addr = (addr + 7) & -8; | ||
440 | |||
441 | /* MMU error, get the MMUSR info for this access */ | ||
442 | mmusr = probe040(!(ssw & RW_040), addr, ssw); | ||
443 | #ifdef DEBUG | ||
444 | printk("mmusr = %lx\n", mmusr); | ||
445 | #endif | ||
446 | errorcode = 1; | ||
447 | if (!(mmusr & MMU_R_040)) { | ||
448 | /* clear the invalid atc entry */ | ||
449 | __flush_tlb040_one(addr); | ||
450 | errorcode = 0; | ||
451 | } | ||
452 | |||
453 | /* despite what documentation seems to say, RMW | ||
454 | * accesses have always both the LK and RW bits set */ | ||
455 | if (!(ssw & RW_040) || (ssw & LK_040)) | ||
456 | errorcode |= 2; | ||
457 | |||
458 | if (do_page_fault(&fp->ptregs, addr, errorcode)) { | ||
459 | #ifdef DEBUG | ||
460 | printk("do_page_fault() !=0\n"); | ||
461 | #endif | ||
462 | if (user_mode(&fp->ptregs)){ | ||
463 | /* delay writebacks after signal delivery */ | ||
464 | #ifdef DEBUG | ||
465 | printk(".. was usermode - return\n"); | ||
466 | #endif | ||
467 | return; | ||
468 | } | ||
469 | /* disable writeback into user space from kernel | ||
470 | * (if do_page_fault didn't fix the mapping, | ||
471 | * the writeback won't do good) | ||
472 | */ | ||
473 | disable_wb: | ||
474 | #ifdef DEBUG | ||
475 | printk(".. disabling wb2\n"); | ||
476 | #endif | ||
477 | if (fp->un.fmt7.wb2a == fp->un.fmt7.faddr) | ||
478 | fp->un.fmt7.wb2s &= ~WBV_040; | ||
479 | if (fp->un.fmt7.wb3a == fp->un.fmt7.faddr) | ||
480 | fp->un.fmt7.wb3s &= ~WBV_040; | ||
481 | } | ||
482 | } else { | ||
483 | /* In case of a bus error we either kill the process or expect | ||
484 | * the kernel to catch the fault, which then is also responsible | ||
485 | * for cleaning up the mess. | ||
486 | */ | ||
487 | current->thread.signo = SIGBUS; | ||
488 | current->thread.faddr = fp->un.fmt7.faddr; | ||
489 | if (send_fault_sig(&fp->ptregs) >= 0) | ||
490 | printk("68040 bus error (ssw=%x, faddr=%lx)\n", ssw, | ||
491 | fp->un.fmt7.faddr); | ||
492 | goto disable_wb; | ||
493 | } | ||
494 | |||
495 | do_040writebacks(fp); | ||
496 | } | ||
497 | #endif /* CONFIG_M68040 */ | ||
498 | |||
499 | #if defined(CONFIG_SUN3) | ||
500 | #include <asm/sun3mmu.h> | ||
501 | |||
502 | extern int mmu_emu_handle_fault (unsigned long, int, int); | ||
503 | |||
504 | /* sun3 version of bus_error030 */ | ||
505 | |||
506 | static inline void bus_error030 (struct frame *fp) | ||
507 | { | ||
508 | unsigned char buserr_type = sun3_get_buserr (); | ||
509 | unsigned long addr, errorcode; | ||
510 | unsigned short ssw = fp->un.fmtb.ssw; | ||
511 | extern unsigned long _sun3_map_test_start, _sun3_map_test_end; | ||
512 | |||
513 | #ifdef DEBUG | ||
514 | if (ssw & (FC | FB)) | ||
515 | printk ("Instruction fault at %#010lx\n", | ||
516 | ssw & FC ? | ||
517 | fp->ptregs.format == 0xa ? fp->ptregs.pc + 2 : fp->un.fmtb.baddr - 2 | ||
518 | : | ||
519 | fp->ptregs.format == 0xa ? fp->ptregs.pc + 4 : fp->un.fmtb.baddr); | ||
520 | if (ssw & DF) | ||
521 | printk ("Data %s fault at %#010lx in %s (pc=%#lx)\n", | ||
522 | ssw & RW ? "read" : "write", | ||
523 | fp->un.fmtb.daddr, | ||
524 | space_names[ssw & DFC], fp->ptregs.pc); | ||
525 | #endif | ||
526 | |||
527 | /* | ||
528 | * Check if this page should be demand-mapped. This needs to go before | ||
529 | * the testing for a bad kernel-space access (demand-mapping applies | ||
530 | * to kernel accesses too). | ||
531 | */ | ||
532 | |||
533 | if ((ssw & DF) | ||
534 | && (buserr_type & (SUN3_BUSERR_PROTERR | SUN3_BUSERR_INVALID))) { | ||
535 | if (mmu_emu_handle_fault (fp->un.fmtb.daddr, ssw & RW, 0)) | ||
536 | return; | ||
537 | } | ||
538 | |||
539 | /* Check for kernel-space pagefault (BAD). */ | ||
540 | if (fp->ptregs.sr & PS_S) { | ||
541 | /* kernel fault must be a data fault to user space */ | ||
542 | if (! ((ssw & DF) && ((ssw & DFC) == USER_DATA))) { | ||
543 | // try checking the kernel mappings before surrender | ||
544 | if (mmu_emu_handle_fault (fp->un.fmtb.daddr, ssw & RW, 1)) | ||
545 | return; | ||
546 | /* instruction fault or kernel data fault! */ | ||
547 | if (ssw & (FC | FB)) | ||
548 | printk ("Instruction fault at %#010lx\n", | ||
549 | fp->ptregs.pc); | ||
550 | if (ssw & DF) { | ||
551 | /* was this fault incurred testing bus mappings? */ | ||
552 | if((fp->ptregs.pc >= (unsigned long)&_sun3_map_test_start) && | ||
553 | (fp->ptregs.pc <= (unsigned long)&_sun3_map_test_end)) { | ||
554 | send_fault_sig(&fp->ptregs); | ||
555 | return; | ||
556 | } | ||
557 | |||
558 | printk ("Data %s fault at %#010lx in %s (pc=%#lx)\n", | ||
559 | ssw & RW ? "read" : "write", | ||
560 | fp->un.fmtb.daddr, | ||
561 | space_names[ssw & DFC], fp->ptregs.pc); | ||
562 | } | ||
563 | printk ("BAD KERNEL BUSERR\n"); | ||
564 | |||
565 | die_if_kernel("Oops", &fp->ptregs,0); | ||
566 | force_sig(SIGKILL, current); | ||
567 | return; | ||
568 | } | ||
569 | } else { | ||
570 | /* user fault */ | ||
571 | if (!(ssw & (FC | FB)) && !(ssw & DF)) | ||
572 | /* not an instruction fault or data fault! BAD */ | ||
573 | panic ("USER BUSERR w/o instruction or data fault"); | ||
574 | } | ||
575 | |||
576 | |||
577 | /* First handle the data fault, if any. */ | ||
578 | if (ssw & DF) { | ||
579 | addr = fp->un.fmtb.daddr; | ||
580 | |||
581 | // errorcode bit 0: 0 -> no page 1 -> protection fault | ||
582 | // errorcode bit 1: 0 -> read fault 1 -> write fault | ||
583 | |||
584 | // (buserr_type & SUN3_BUSERR_PROTERR) -> protection fault | ||
585 | // (buserr_type & SUN3_BUSERR_INVALID) -> invalid page fault | ||
586 | |||
587 | if (buserr_type & SUN3_BUSERR_PROTERR) | ||
588 | errorcode = 0x01; | ||
589 | else if (buserr_type & SUN3_BUSERR_INVALID) | ||
590 | errorcode = 0x00; | ||
591 | else { | ||
592 | #ifdef DEBUG | ||
593 | printk ("*** unexpected busfault type=%#04x\n", buserr_type); | ||
594 | printk ("invalid %s access at %#lx from pc %#lx\n", | ||
595 | !(ssw & RW) ? "write" : "read", addr, | ||
596 | fp->ptregs.pc); | ||
597 | #endif | ||
598 | die_if_kernel ("Oops", &fp->ptregs, buserr_type); | ||
599 | force_sig (SIGBUS, current); | ||
600 | return; | ||
601 | } | ||
602 | |||
603 | //todo: wtf is RM bit? --m | ||
604 | if (!(ssw & RW) || ssw & RM) | ||
605 | errorcode |= 0x02; | ||
606 | |||
607 | /* Handle page fault. */ | ||
608 | do_page_fault (&fp->ptregs, addr, errorcode); | ||
609 | |||
610 | /* Retry the data fault now. */ | ||
611 | return; | ||
612 | } | ||
613 | |||
614 | /* Now handle the instruction fault. */ | ||
615 | |||
616 | /* Get the fault address. */ | ||
617 | if (fp->ptregs.format == 0xA) | ||
618 | addr = fp->ptregs.pc + 4; | ||
619 | else | ||
620 | addr = fp->un.fmtb.baddr; | ||
621 | if (ssw & FC) | ||
622 | addr -= 2; | ||
623 | |||
624 | if (buserr_type & SUN3_BUSERR_INVALID) { | ||
625 | if (!mmu_emu_handle_fault (fp->un.fmtb.daddr, 1, 0)) | ||
626 | do_page_fault (&fp->ptregs, addr, 0); | ||
627 | } else { | ||
628 | #ifdef DEBUG | ||
629 | printk ("protection fault on insn access (segv).\n"); | ||
630 | #endif | ||
631 | force_sig (SIGSEGV, current); | ||
632 | } | ||
633 | } | ||
634 | #else | ||
635 | #if defined(CPU_M68020_OR_M68030) | ||
636 | static inline void bus_error030 (struct frame *fp) | ||
637 | { | ||
638 | volatile unsigned short temp; | ||
639 | unsigned short mmusr; | ||
640 | unsigned long addr, errorcode; | ||
641 | unsigned short ssw = fp->un.fmtb.ssw; | ||
642 | #ifdef DEBUG | ||
643 | unsigned long desc; | ||
644 | |||
645 | printk ("pid = %x ", current->pid); | ||
646 | printk ("SSW=%#06x ", ssw); | ||
647 | |||
648 | if (ssw & (FC | FB)) | ||
649 | printk ("Instruction fault at %#010lx\n", | ||
650 | ssw & FC ? | ||
651 | fp->ptregs.format == 0xa ? fp->ptregs.pc + 2 : fp->un.fmtb.baddr - 2 | ||
652 | : | ||
653 | fp->ptregs.format == 0xa ? fp->ptregs.pc + 4 : fp->un.fmtb.baddr); | ||
654 | if (ssw & DF) | ||
655 | printk ("Data %s fault at %#010lx in %s (pc=%#lx)\n", | ||
656 | ssw & RW ? "read" : "write", | ||
657 | fp->un.fmtb.daddr, | ||
658 | space_names[ssw & DFC], fp->ptregs.pc); | ||
659 | #endif | ||
660 | |||
661 | /* ++andreas: If a data fault and an instruction fault happen | ||
662 | at the same time map in both pages. */ | ||
663 | |||
664 | /* First handle the data fault, if any. */ | ||
665 | if (ssw & DF) { | ||
666 | addr = fp->un.fmtb.daddr; | ||
667 | |||
668 | #ifdef DEBUG | ||
669 | asm volatile ("ptestr %3,%2@,#7,%0\n\t" | ||
670 | "pmove %%psr,%1@" | ||
671 | : "=a&" (desc) | ||
672 | : "a" (&temp), "a" (addr), "d" (ssw)); | ||
673 | #else | ||
674 | asm volatile ("ptestr %2,%1@,#7\n\t" | ||
675 | "pmove %%psr,%0@" | ||
676 | : : "a" (&temp), "a" (addr), "d" (ssw)); | ||
677 | #endif | ||
678 | mmusr = temp; | ||
679 | |||
680 | #ifdef DEBUG | ||
681 | printk("mmusr is %#x for addr %#lx in task %p\n", | ||
682 | mmusr, addr, current); | ||
683 | printk("descriptor address is %#lx, contents %#lx\n", | ||
684 | __va(desc), *(unsigned long *)__va(desc)); | ||
685 | #endif | ||
686 | |||
687 | errorcode = (mmusr & MMU_I) ? 0 : 1; | ||
688 | if (!(ssw & RW) || (ssw & RM)) | ||
689 | errorcode |= 2; | ||
690 | |||
691 | if (mmusr & (MMU_I | MMU_WP)) { | ||
692 | if (ssw & 4) { | ||
693 | printk("Data %s fault at %#010lx in %s (pc=%#lx)\n", | ||
694 | ssw & RW ? "read" : "write", | ||
695 | fp->un.fmtb.daddr, | ||
696 | space_names[ssw & DFC], fp->ptregs.pc); | ||
697 | goto buserr; | ||
698 | } | ||
699 | /* Don't try to do anything further if an exception was | ||
700 | handled. */ | ||
701 | if (do_page_fault (&fp->ptregs, addr, errorcode) < 0) | ||
702 | return; | ||
703 | } else if (!(mmusr & MMU_I)) { | ||
704 | /* probably a 020 cas fault */ | ||
705 | if (!(ssw & RM) && send_fault_sig(&fp->ptregs) > 0) | ||
706 | printk("unexpected bus error (%#x,%#x)\n", ssw, mmusr); | ||
707 | } else if (mmusr & (MMU_B|MMU_L|MMU_S)) { | ||
708 | printk("invalid %s access at %#lx from pc %#lx\n", | ||
709 | !(ssw & RW) ? "write" : "read", addr, | ||
710 | fp->ptregs.pc); | ||
711 | die_if_kernel("Oops",&fp->ptregs,mmusr); | ||
712 | force_sig(SIGSEGV, current); | ||
713 | return; | ||
714 | } else { | ||
715 | #if 0 | ||
716 | static volatile long tlong; | ||
717 | #endif | ||
718 | |||
719 | printk("weird %s access at %#lx from pc %#lx (ssw is %#x)\n", | ||
720 | !(ssw & RW) ? "write" : "read", addr, | ||
721 | fp->ptregs.pc, ssw); | ||
722 | asm volatile ("ptestr #1,%1@,#0\n\t" | ||
723 | "pmove %%psr,%0@" | ||
724 | : /* no outputs */ | ||
725 | : "a" (&temp), "a" (addr)); | ||
726 | mmusr = temp; | ||
727 | |||
728 | printk ("level 0 mmusr is %#x\n", mmusr); | ||
729 | #if 0 | ||
730 | asm volatile ("pmove %%tt0,%0@" | ||
731 | : /* no outputs */ | ||
732 | : "a" (&tlong)); | ||
733 | printk("tt0 is %#lx, ", tlong); | ||
734 | asm volatile ("pmove %%tt1,%0@" | ||
735 | : /* no outputs */ | ||
736 | : "a" (&tlong)); | ||
737 | printk("tt1 is %#lx\n", tlong); | ||
738 | #endif | ||
739 | #ifdef DEBUG | ||
740 | printk("Unknown SIGSEGV - 1\n"); | ||
741 | #endif | ||
742 | die_if_kernel("Oops",&fp->ptregs,mmusr); | ||
743 | force_sig(SIGSEGV, current); | ||
744 | return; | ||
745 | } | ||
746 | |||
747 | /* setup an ATC entry for the access about to be retried */ | ||
748 | if (!(ssw & RW) || (ssw & RM)) | ||
749 | asm volatile ("ploadw %1,%0@" : /* no outputs */ | ||
750 | : "a" (addr), "d" (ssw)); | ||
751 | else | ||
752 | asm volatile ("ploadr %1,%0@" : /* no outputs */ | ||
753 | : "a" (addr), "d" (ssw)); | ||
754 | } | ||
755 | |||
756 | /* Now handle the instruction fault. */ | ||
757 | |||
758 | if (!(ssw & (FC|FB))) | ||
759 | return; | ||
760 | |||
761 | if (fp->ptregs.sr & PS_S) { | ||
762 | printk("Instruction fault at %#010lx\n", | ||
763 | fp->ptregs.pc); | ||
764 | buserr: | ||
765 | printk ("BAD KERNEL BUSERR\n"); | ||
766 | die_if_kernel("Oops",&fp->ptregs,0); | ||
767 | force_sig(SIGKILL, current); | ||
768 | return; | ||
769 | } | ||
770 | |||
771 | /* get the fault address */ | ||
772 | if (fp->ptregs.format == 10) | ||
773 | addr = fp->ptregs.pc + 4; | ||
774 | else | ||
775 | addr = fp->un.fmtb.baddr; | ||
776 | if (ssw & FC) | ||
777 | addr -= 2; | ||
778 | |||
779 | if ((ssw & DF) && ((addr ^ fp->un.fmtb.daddr) & PAGE_MASK) == 0) | ||
780 | /* Insn fault on same page as data fault. But we | ||
781 | should still create the ATC entry. */ | ||
782 | goto create_atc_entry; | ||
783 | |||
784 | #ifdef DEBUG | ||
785 | asm volatile ("ptestr #1,%2@,#7,%0\n\t" | ||
786 | "pmove %%psr,%1@" | ||
787 | : "=a&" (desc) | ||
788 | : "a" (&temp), "a" (addr)); | ||
789 | #else | ||
790 | asm volatile ("ptestr #1,%1@,#7\n\t" | ||
791 | "pmove %%psr,%0@" | ||
792 | : : "a" (&temp), "a" (addr)); | ||
793 | #endif | ||
794 | mmusr = temp; | ||
795 | |||
796 | #ifdef DEBUG | ||
797 | printk ("mmusr is %#x for addr %#lx in task %p\n", | ||
798 | mmusr, addr, current); | ||
799 | printk ("descriptor address is %#lx, contents %#lx\n", | ||
800 | __va(desc), *(unsigned long *)__va(desc)); | ||
801 | #endif | ||
802 | |||
803 | if (mmusr & MMU_I) | ||
804 | do_page_fault (&fp->ptregs, addr, 0); | ||
805 | else if (mmusr & (MMU_B|MMU_L|MMU_S)) { | ||
806 | printk ("invalid insn access at %#lx from pc %#lx\n", | ||
807 | addr, fp->ptregs.pc); | ||
808 | #ifdef DEBUG | ||
809 | printk("Unknown SIGSEGV - 2\n"); | ||
810 | #endif | ||
811 | die_if_kernel("Oops",&fp->ptregs,mmusr); | ||
812 | force_sig(SIGSEGV, current); | ||
813 | return; | ||
814 | } | ||
815 | |||
816 | create_atc_entry: | ||
817 | /* setup an ATC entry for the access about to be retried */ | ||
818 | asm volatile ("ploadr #2,%0@" : /* no outputs */ | ||
819 | : "a" (addr)); | ||
820 | } | ||
821 | #endif /* CPU_M68020_OR_M68030 */ | ||
822 | #endif /* !CONFIG_SUN3 */ | ||
823 | |||
824 | asmlinkage void buserr_c(struct frame *fp) | ||
825 | { | ||
826 | /* Only set esp0 if coming from user mode */ | ||
827 | if (user_mode(&fp->ptregs)) | ||
828 | current->thread.esp0 = (unsigned long) fp; | ||
829 | |||
830 | #ifdef DEBUG | ||
831 | printk ("*** Bus Error *** Format is %x\n", fp->ptregs.format); | ||
832 | #endif | ||
833 | |||
834 | switch (fp->ptregs.format) { | ||
835 | #if defined (CONFIG_M68060) | ||
836 | case 4: /* 68060 access error */ | ||
837 | access_error060 (fp); | ||
838 | break; | ||
839 | #endif | ||
840 | #if defined (CONFIG_M68040) | ||
841 | case 0x7: /* 68040 access error */ | ||
842 | access_error040 (fp); | ||
843 | break; | ||
844 | #endif | ||
845 | #if defined (CPU_M68020_OR_M68030) | ||
846 | case 0xa: | ||
847 | case 0xb: | ||
848 | bus_error030 (fp); | ||
849 | break; | ||
850 | #endif | ||
851 | default: | ||
852 | die_if_kernel("bad frame format",&fp->ptregs,0); | ||
853 | #ifdef DEBUG | ||
854 | printk("Unknown SIGSEGV - 4\n"); | ||
855 | #endif | ||
856 | force_sig(SIGSEGV, current); | ||
857 | } | ||
858 | } | ||
859 | |||
860 | |||
861 | static int kstack_depth_to_print = 48; | ||
862 | |||
863 | void show_trace(unsigned long *stack) | ||
864 | { | ||
865 | unsigned long *endstack; | ||
866 | unsigned long addr; | ||
867 | int i; | ||
868 | |||
869 | printk("Call Trace:"); | ||
870 | addr = (unsigned long)stack + THREAD_SIZE - 1; | ||
871 | endstack = (unsigned long *)(addr & -THREAD_SIZE); | ||
872 | i = 0; | ||
873 | while (stack + 1 <= endstack) { | ||
874 | addr = *stack++; | ||
875 | /* | ||
876 | * If the address is either in the text segment of the | ||
877 | * kernel, or in the region which contains vmalloc'ed | ||
878 | * memory, it *may* be the address of a calling | ||
879 | * routine; if so, print it so that someone tracing | ||
880 | * down the cause of the crash will be able to figure | ||
881 | * out the call path that was taken. | ||
882 | */ | ||
883 | if (__kernel_text_address(addr)) { | ||
884 | #ifndef CONFIG_KALLSYMS | ||
885 | if (i % 5 == 0) | ||
886 | printk("\n "); | ||
887 | #endif | ||
888 | printk(" [<%08lx>] %pS\n", addr, (void *)addr); | ||
889 | i++; | ||
890 | } | ||
891 | } | ||
892 | printk("\n"); | ||
893 | } | ||
894 | |||
895 | void show_registers(struct pt_regs *regs) | ||
896 | { | ||
897 | struct frame *fp = (struct frame *)regs; | ||
898 | mm_segment_t old_fs = get_fs(); | ||
899 | u16 c, *cp; | ||
900 | unsigned long addr; | ||
901 | int i; | ||
902 | |||
903 | print_modules(); | ||
904 | printk("PC: [<%08lx>] %pS\n", regs->pc, (void *)regs->pc); | ||
905 | printk("SR: %04x SP: %p a2: %08lx\n", regs->sr, regs, regs->a2); | ||
906 | printk("d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", | ||
907 | regs->d0, regs->d1, regs->d2, regs->d3); | ||
908 | printk("d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n", | ||
909 | regs->d4, regs->d5, regs->a0, regs->a1); | ||
910 | |||
911 | printk("Process %s (pid: %d, task=%p)\n", | ||
912 | current->comm, task_pid_nr(current), current); | ||
913 | addr = (unsigned long)&fp->un; | ||
914 | printk("Frame format=%X ", regs->format); | ||
915 | switch (regs->format) { | ||
916 | case 0x2: | ||
917 | printk("instr addr=%08lx\n", fp->un.fmt2.iaddr); | ||
918 | addr += sizeof(fp->un.fmt2); | ||
919 | break; | ||
920 | case 0x3: | ||
921 | printk("eff addr=%08lx\n", fp->un.fmt3.effaddr); | ||
922 | addr += sizeof(fp->un.fmt3); | ||
923 | break; | ||
924 | case 0x4: | ||
925 | printk((CPU_IS_060 ? "fault addr=%08lx fslw=%08lx\n" | ||
926 | : "eff addr=%08lx pc=%08lx\n"), | ||
927 | fp->un.fmt4.effaddr, fp->un.fmt4.pc); | ||
928 | addr += sizeof(fp->un.fmt4); | ||
929 | break; | ||
930 | case 0x7: | ||
931 | printk("eff addr=%08lx ssw=%04x faddr=%08lx\n", | ||
932 | fp->un.fmt7.effaddr, fp->un.fmt7.ssw, fp->un.fmt7.faddr); | ||
933 | printk("wb 1 stat/addr/data: %04x %08lx %08lx\n", | ||
934 | fp->un.fmt7.wb1s, fp->un.fmt7.wb1a, fp->un.fmt7.wb1dpd0); | ||
935 | printk("wb 2 stat/addr/data: %04x %08lx %08lx\n", | ||
936 | fp->un.fmt7.wb2s, fp->un.fmt7.wb2a, fp->un.fmt7.wb2d); | ||
937 | printk("wb 3 stat/addr/data: %04x %08lx %08lx\n", | ||
938 | fp->un.fmt7.wb3s, fp->un.fmt7.wb3a, fp->un.fmt7.wb3d); | ||
939 | printk("push data: %08lx %08lx %08lx %08lx\n", | ||
940 | fp->un.fmt7.wb1dpd0, fp->un.fmt7.pd1, fp->un.fmt7.pd2, | ||
941 | fp->un.fmt7.pd3); | ||
942 | addr += sizeof(fp->un.fmt7); | ||
943 | break; | ||
944 | case 0x9: | ||
945 | printk("instr addr=%08lx\n", fp->un.fmt9.iaddr); | ||
946 | addr += sizeof(fp->un.fmt9); | ||
947 | break; | ||
948 | case 0xa: | ||
949 | printk("ssw=%04x isc=%04x isb=%04x daddr=%08lx dobuf=%08lx\n", | ||
950 | fp->un.fmta.ssw, fp->un.fmta.isc, fp->un.fmta.isb, | ||
951 | fp->un.fmta.daddr, fp->un.fmta.dobuf); | ||
952 | addr += sizeof(fp->un.fmta); | ||
953 | break; | ||
954 | case 0xb: | ||
955 | printk("ssw=%04x isc=%04x isb=%04x daddr=%08lx dobuf=%08lx\n", | ||
956 | fp->un.fmtb.ssw, fp->un.fmtb.isc, fp->un.fmtb.isb, | ||
957 | fp->un.fmtb.daddr, fp->un.fmtb.dobuf); | ||
958 | printk("baddr=%08lx dibuf=%08lx ver=%x\n", | ||
959 | fp->un.fmtb.baddr, fp->un.fmtb.dibuf, fp->un.fmtb.ver); | ||
960 | addr += sizeof(fp->un.fmtb); | ||
961 | break; | ||
962 | default: | ||
963 | printk("\n"); | ||
964 | } | ||
965 | show_stack(NULL, (unsigned long *)addr); | ||
966 | |||
967 | printk("Code:"); | ||
968 | set_fs(KERNEL_DS); | ||
969 | cp = (u16 *)regs->pc; | ||
970 | for (i = -8; i < 16; i++) { | ||
971 | if (get_user(c, cp + i) && i >= 0) { | ||
972 | printk(" Bad PC value."); | ||
973 | break; | ||
974 | } | ||
975 | printk(i ? " %04x" : " <%04x>", c); | ||
976 | } | ||
977 | set_fs(old_fs); | ||
978 | printk ("\n"); | ||
979 | } | ||
980 | |||
981 | void show_stack(struct task_struct *task, unsigned long *stack) | ||
982 | { | ||
983 | unsigned long *p; | ||
984 | unsigned long *endstack; | ||
985 | int i; | ||
986 | |||
987 | if (!stack) { | ||
988 | if (task) | ||
989 | stack = (unsigned long *)task->thread.esp0; | ||
990 | else | ||
991 | stack = (unsigned long *)&stack; | ||
992 | } | ||
993 | endstack = (unsigned long *)(((unsigned long)stack + THREAD_SIZE - 1) & -THREAD_SIZE); | ||
994 | |||
995 | printk("Stack from %08lx:", (unsigned long)stack); | ||
996 | p = stack; | ||
997 | for (i = 0; i < kstack_depth_to_print; i++) { | ||
998 | if (p + 1 > endstack) | ||
999 | break; | ||
1000 | if (i % 8 == 0) | ||
1001 | printk("\n "); | ||
1002 | printk(" %08lx", *p++); | ||
1003 | } | ||
1004 | printk("\n"); | ||
1005 | show_trace(stack); | ||
1006 | } | ||
1007 | |||
1008 | /* | ||
1009 | * The architecture-independent backtrace generator | ||
1010 | */ | ||
1011 | void dump_stack(void) | ||
1012 | { | ||
1013 | unsigned long stack; | ||
1014 | |||
1015 | show_trace(&stack); | ||
1016 | } | ||
1017 | |||
1018 | EXPORT_SYMBOL(dump_stack); | ||
1019 | |||
1020 | void bad_super_trap (struct frame *fp) | ||
1021 | { | ||
1022 | console_verbose(); | ||
1023 | if (fp->ptregs.vector < 4 * ARRAY_SIZE(vec_names)) | ||
1024 | printk ("*** %s *** FORMAT=%X\n", | ||
1025 | vec_names[(fp->ptregs.vector) >> 2], | ||
1026 | fp->ptregs.format); | ||
1027 | else | ||
1028 | printk ("*** Exception %d *** FORMAT=%X\n", | ||
1029 | (fp->ptregs.vector) >> 2, | ||
1030 | fp->ptregs.format); | ||
1031 | if (fp->ptregs.vector >> 2 == VEC_ADDRERR && CPU_IS_020_OR_030) { | ||
1032 | unsigned short ssw = fp->un.fmtb.ssw; | ||
1033 | |||
1034 | printk ("SSW=%#06x ", ssw); | ||
1035 | |||
1036 | if (ssw & RC) | ||
1037 | printk ("Pipe stage C instruction fault at %#010lx\n", | ||
1038 | (fp->ptregs.format) == 0xA ? | ||
1039 | fp->ptregs.pc + 2 : fp->un.fmtb.baddr - 2); | ||
1040 | if (ssw & RB) | ||
1041 | printk ("Pipe stage B instruction fault at %#010lx\n", | ||
1042 | (fp->ptregs.format) == 0xA ? | ||
1043 | fp->ptregs.pc + 4 : fp->un.fmtb.baddr); | ||
1044 | if (ssw & DF) | ||
1045 | printk ("Data %s fault at %#010lx in %s (pc=%#lx)\n", | ||
1046 | ssw & RW ? "read" : "write", | ||
1047 | fp->un.fmtb.daddr, space_names[ssw & DFC], | ||
1048 | fp->ptregs.pc); | ||
1049 | } | ||
1050 | printk ("Current process id is %d\n", task_pid_nr(current)); | ||
1051 | die_if_kernel("BAD KERNEL TRAP", &fp->ptregs, 0); | ||
1052 | } | ||
1053 | |||
1054 | asmlinkage void trap_c(struct frame *fp) | ||
1055 | { | ||
1056 | int sig; | ||
1057 | siginfo_t info; | ||
1058 | |||
1059 | if (fp->ptregs.sr & PS_S) { | ||
1060 | if (fp->ptregs.vector == VEC_TRACE << 2) { | ||
1061 | /* traced a trapping instruction on a 68020/30, | ||
1062 | * real exception will be executed afterwards. | ||
1063 | */ | ||
1064 | } else if (!handle_kernel_fault(&fp->ptregs)) | ||
1065 | bad_super_trap(fp); | ||
1066 | return; | ||
1067 | } | ||
1068 | |||
1069 | /* send the appropriate signal to the user program */ | ||
1070 | switch ((fp->ptregs.vector) >> 2) { | ||
1071 | case VEC_ADDRERR: | ||
1072 | info.si_code = BUS_ADRALN; | ||
1073 | sig = SIGBUS; | ||
1074 | break; | ||
1075 | case VEC_ILLEGAL: | ||
1076 | case VEC_LINE10: | ||
1077 | case VEC_LINE11: | ||
1078 | info.si_code = ILL_ILLOPC; | ||
1079 | sig = SIGILL; | ||
1080 | break; | ||
1081 | case VEC_PRIV: | ||
1082 | info.si_code = ILL_PRVOPC; | ||
1083 | sig = SIGILL; | ||
1084 | break; | ||
1085 | case VEC_COPROC: | ||
1086 | info.si_code = ILL_COPROC; | ||
1087 | sig = SIGILL; | ||
1088 | break; | ||
1089 | case VEC_TRAP1: | ||
1090 | case VEC_TRAP2: | ||
1091 | case VEC_TRAP3: | ||
1092 | case VEC_TRAP4: | ||
1093 | case VEC_TRAP5: | ||
1094 | case VEC_TRAP6: | ||
1095 | case VEC_TRAP7: | ||
1096 | case VEC_TRAP8: | ||
1097 | case VEC_TRAP9: | ||
1098 | case VEC_TRAP10: | ||
1099 | case VEC_TRAP11: | ||
1100 | case VEC_TRAP12: | ||
1101 | case VEC_TRAP13: | ||
1102 | case VEC_TRAP14: | ||
1103 | info.si_code = ILL_ILLTRP; | ||
1104 | sig = SIGILL; | ||
1105 | break; | ||
1106 | case VEC_FPBRUC: | ||
1107 | case VEC_FPOE: | ||
1108 | case VEC_FPNAN: | ||
1109 | info.si_code = FPE_FLTINV; | ||
1110 | sig = SIGFPE; | ||
1111 | break; | ||
1112 | case VEC_FPIR: | ||
1113 | info.si_code = FPE_FLTRES; | ||
1114 | sig = SIGFPE; | ||
1115 | break; | ||
1116 | case VEC_FPDIVZ: | ||
1117 | info.si_code = FPE_FLTDIV; | ||
1118 | sig = SIGFPE; | ||
1119 | break; | ||
1120 | case VEC_FPUNDER: | ||
1121 | info.si_code = FPE_FLTUND; | ||
1122 | sig = SIGFPE; | ||
1123 | break; | ||
1124 | case VEC_FPOVER: | ||
1125 | info.si_code = FPE_FLTOVF; | ||
1126 | sig = SIGFPE; | ||
1127 | break; | ||
1128 | case VEC_ZERODIV: | ||
1129 | info.si_code = FPE_INTDIV; | ||
1130 | sig = SIGFPE; | ||
1131 | break; | ||
1132 | case VEC_CHK: | ||
1133 | case VEC_TRAP: | ||
1134 | info.si_code = FPE_INTOVF; | ||
1135 | sig = SIGFPE; | ||
1136 | break; | ||
1137 | case VEC_TRACE: /* ptrace single step */ | ||
1138 | info.si_code = TRAP_TRACE; | ||
1139 | sig = SIGTRAP; | ||
1140 | break; | ||
1141 | case VEC_TRAP15: /* breakpoint */ | ||
1142 | info.si_code = TRAP_BRKPT; | ||
1143 | sig = SIGTRAP; | ||
1144 | break; | ||
1145 | default: | ||
1146 | info.si_code = ILL_ILLOPC; | ||
1147 | sig = SIGILL; | ||
1148 | break; | ||
1149 | } | ||
1150 | info.si_signo = sig; | ||
1151 | info.si_errno = 0; | ||
1152 | switch (fp->ptregs.format) { | ||
1153 | default: | ||
1154 | info.si_addr = (void *) fp->ptregs.pc; | ||
1155 | break; | ||
1156 | case 2: | ||
1157 | info.si_addr = (void *) fp->un.fmt2.iaddr; | ||
1158 | break; | ||
1159 | case 7: | ||
1160 | info.si_addr = (void *) fp->un.fmt7.effaddr; | ||
1161 | break; | ||
1162 | case 9: | ||
1163 | info.si_addr = (void *) fp->un.fmt9.iaddr; | ||
1164 | break; | ||
1165 | case 10: | ||
1166 | info.si_addr = (void *) fp->un.fmta.daddr; | ||
1167 | break; | ||
1168 | case 11: | ||
1169 | info.si_addr = (void *) fp->un.fmtb.daddr; | ||
1170 | break; | ||
1171 | } | ||
1172 | force_sig_info (sig, &info, current); | ||
1173 | } | ||
1174 | |||
1175 | void die_if_kernel (char *str, struct pt_regs *fp, int nr) | ||
1176 | { | ||
1177 | if (!(fp->sr & PS_S)) | ||
1178 | return; | ||
1179 | |||
1180 | console_verbose(); | ||
1181 | printk("%s: %08x\n",str,nr); | ||
1182 | show_registers(fp); | ||
1183 | add_taint(TAINT_DIE); | ||
1184 | do_exit(SIGSEGV); | ||
1185 | } | ||
1186 | |||
1187 | /* | ||
1188 | * This function is called if an error occur while accessing | ||
1189 | * user-space from the fpsp040 code. | ||
1190 | */ | ||
1191 | asmlinkage void fpsp040_die(void) | ||
1192 | { | ||
1193 | do_exit(SIGSEGV); | ||
1194 | } | ||
1195 | |||
1196 | #ifdef CONFIG_M68KFPU_EMU | ||
1197 | asmlinkage void fpemu_signal(int signal, int code, void *addr) | ||
1198 | { | ||
1199 | siginfo_t info; | ||
1200 | |||
1201 | info.si_signo = signal; | ||
1202 | info.si_errno = 0; | ||
1203 | info.si_code = code; | ||
1204 | info.si_addr = addr; | ||
1205 | force_sig_info(signal, &info, current); | ||
1206 | } | ||
1207 | #endif | 5 | #endif |
diff --git a/arch/m68k/kernel/traps_mm.c b/arch/m68k/kernel/traps_mm.c new file mode 100644 index 000000000000..4022bbc28878 --- /dev/null +++ b/arch/m68k/kernel/traps_mm.c | |||
@@ -0,0 +1,1207 @@ | |||
1 | /* | ||
2 | * linux/arch/m68k/kernel/traps.c | ||
3 | * | ||
4 | * Copyright (C) 1993, 1994 by Hamish Macdonald | ||
5 | * | ||
6 | * 68040 fixes by Michael Rausch | ||
7 | * 68040 fixes by Martin Apel | ||
8 | * 68040 fixes and writeback by Richard Zidlicky | ||
9 | * 68060 fixes by Roman Hodek | ||
10 | * 68060 fixes by Jesper Skov | ||
11 | * | ||
12 | * This file is subject to the terms and conditions of the GNU General Public | ||
13 | * License. See the file COPYING in the main directory of this archive | ||
14 | * for more details. | ||
15 | */ | ||
16 | |||
17 | /* | ||
18 | * Sets up all exception vectors | ||
19 | */ | ||
20 | |||
21 | #include <linux/sched.h> | ||
22 | #include <linux/signal.h> | ||
23 | #include <linux/kernel.h> | ||
24 | #include <linux/mm.h> | ||
25 | #include <linux/module.h> | ||
26 | #include <linux/user.h> | ||
27 | #include <linux/string.h> | ||
28 | #include <linux/linkage.h> | ||
29 | #include <linux/init.h> | ||
30 | #include <linux/ptrace.h> | ||
31 | #include <linux/kallsyms.h> | ||
32 | |||
33 | #include <asm/setup.h> | ||
34 | #include <asm/fpu.h> | ||
35 | #include <asm/system.h> | ||
36 | #include <asm/uaccess.h> | ||
37 | #include <asm/traps.h> | ||
38 | #include <asm/pgalloc.h> | ||
39 | #include <asm/machdep.h> | ||
40 | #include <asm/siginfo.h> | ||
41 | |||
42 | /* assembler routines */ | ||
43 | asmlinkage void system_call(void); | ||
44 | asmlinkage void buserr(void); | ||
45 | asmlinkage void trap(void); | ||
46 | asmlinkage void nmihandler(void); | ||
47 | #ifdef CONFIG_M68KFPU_EMU | ||
48 | asmlinkage void fpu_emu(void); | ||
49 | #endif | ||
50 | |||
51 | e_vector vectors[256]; | ||
52 | |||
53 | /* nmi handler for the Amiga */ | ||
54 | asm(".text\n" | ||
55 | __ALIGN_STR "\n" | ||
56 | "nmihandler: rte"); | ||
57 | |||
58 | /* | ||
59 | * this must be called very early as the kernel might | ||
60 | * use some instruction that are emulated on the 060 | ||
61 | * and so we're prepared for early probe attempts (e.g. nf_init). | ||
62 | */ | ||
63 | void __init base_trap_init(void) | ||
64 | { | ||
65 | if (MACH_IS_SUN3X) { | ||
66 | extern e_vector *sun3x_prom_vbr; | ||
67 | |||
68 | __asm__ volatile ("movec %%vbr, %0" : "=r" (sun3x_prom_vbr)); | ||
69 | } | ||
70 | |||
71 | /* setup the exception vector table */ | ||
72 | __asm__ volatile ("movec %0,%%vbr" : : "r" ((void*)vectors)); | ||
73 | |||
74 | if (CPU_IS_060) { | ||
75 | /* set up ISP entry points */ | ||
76 | asmlinkage void unimp_vec(void) asm ("_060_isp_unimp"); | ||
77 | |||
78 | vectors[VEC_UNIMPII] = unimp_vec; | ||
79 | } | ||
80 | |||
81 | vectors[VEC_BUSERR] = buserr; | ||
82 | vectors[VEC_ILLEGAL] = trap; | ||
83 | vectors[VEC_SYS] = system_call; | ||
84 | } | ||
85 | |||
86 | void __init trap_init (void) | ||
87 | { | ||
88 | int i; | ||
89 | |||
90 | for (i = VEC_SPUR; i <= VEC_INT7; i++) | ||
91 | vectors[i] = bad_inthandler; | ||
92 | |||
93 | for (i = 0; i < VEC_USER; i++) | ||
94 | if (!vectors[i]) | ||
95 | vectors[i] = trap; | ||
96 | |||
97 | for (i = VEC_USER; i < 256; i++) | ||
98 | vectors[i] = bad_inthandler; | ||
99 | |||
100 | #ifdef CONFIG_M68KFPU_EMU | ||
101 | if (FPU_IS_EMU) | ||
102 | vectors[VEC_LINE11] = fpu_emu; | ||
103 | #endif | ||
104 | |||
105 | if (CPU_IS_040 && !FPU_IS_EMU) { | ||
106 | /* set up FPSP entry points */ | ||
107 | asmlinkage void dz_vec(void) asm ("dz"); | ||
108 | asmlinkage void inex_vec(void) asm ("inex"); | ||
109 | asmlinkage void ovfl_vec(void) asm ("ovfl"); | ||
110 | asmlinkage void unfl_vec(void) asm ("unfl"); | ||
111 | asmlinkage void snan_vec(void) asm ("snan"); | ||
112 | asmlinkage void operr_vec(void) asm ("operr"); | ||
113 | asmlinkage void bsun_vec(void) asm ("bsun"); | ||
114 | asmlinkage void fline_vec(void) asm ("fline"); | ||
115 | asmlinkage void unsupp_vec(void) asm ("unsupp"); | ||
116 | |||
117 | vectors[VEC_FPDIVZ] = dz_vec; | ||
118 | vectors[VEC_FPIR] = inex_vec; | ||
119 | vectors[VEC_FPOVER] = ovfl_vec; | ||
120 | vectors[VEC_FPUNDER] = unfl_vec; | ||
121 | vectors[VEC_FPNAN] = snan_vec; | ||
122 | vectors[VEC_FPOE] = operr_vec; | ||
123 | vectors[VEC_FPBRUC] = bsun_vec; | ||
124 | vectors[VEC_LINE11] = fline_vec; | ||
125 | vectors[VEC_FPUNSUP] = unsupp_vec; | ||
126 | } | ||
127 | |||
128 | if (CPU_IS_060 && !FPU_IS_EMU) { | ||
129 | /* set up IFPSP entry points */ | ||
130 | asmlinkage void snan_vec6(void) asm ("_060_fpsp_snan"); | ||
131 | asmlinkage void operr_vec6(void) asm ("_060_fpsp_operr"); | ||
132 | asmlinkage void ovfl_vec6(void) asm ("_060_fpsp_ovfl"); | ||
133 | asmlinkage void unfl_vec6(void) asm ("_060_fpsp_unfl"); | ||
134 | asmlinkage void dz_vec6(void) asm ("_060_fpsp_dz"); | ||
135 | asmlinkage void inex_vec6(void) asm ("_060_fpsp_inex"); | ||
136 | asmlinkage void fline_vec6(void) asm ("_060_fpsp_fline"); | ||
137 | asmlinkage void unsupp_vec6(void) asm ("_060_fpsp_unsupp"); | ||
138 | asmlinkage void effadd_vec6(void) asm ("_060_fpsp_effadd"); | ||
139 | |||
140 | vectors[VEC_FPNAN] = snan_vec6; | ||
141 | vectors[VEC_FPOE] = operr_vec6; | ||
142 | vectors[VEC_FPOVER] = ovfl_vec6; | ||
143 | vectors[VEC_FPUNDER] = unfl_vec6; | ||
144 | vectors[VEC_FPDIVZ] = dz_vec6; | ||
145 | vectors[VEC_FPIR] = inex_vec6; | ||
146 | vectors[VEC_LINE11] = fline_vec6; | ||
147 | vectors[VEC_FPUNSUP] = unsupp_vec6; | ||
148 | vectors[VEC_UNIMPEA] = effadd_vec6; | ||
149 | } | ||
150 | |||
151 | /* if running on an amiga, make the NMI interrupt do nothing */ | ||
152 | if (MACH_IS_AMIGA) { | ||
153 | vectors[VEC_INT7] = nmihandler; | ||
154 | } | ||
155 | } | ||
156 | |||
157 | |||
158 | static const char *vec_names[] = { | ||
159 | [VEC_RESETSP] = "RESET SP", | ||
160 | [VEC_RESETPC] = "RESET PC", | ||
161 | [VEC_BUSERR] = "BUS ERROR", | ||
162 | [VEC_ADDRERR] = "ADDRESS ERROR", | ||
163 | [VEC_ILLEGAL] = "ILLEGAL INSTRUCTION", | ||
164 | [VEC_ZERODIV] = "ZERO DIVIDE", | ||
165 | [VEC_CHK] = "CHK", | ||
166 | [VEC_TRAP] = "TRAPcc", | ||
167 | [VEC_PRIV] = "PRIVILEGE VIOLATION", | ||
168 | [VEC_TRACE] = "TRACE", | ||
169 | [VEC_LINE10] = "LINE 1010", | ||
170 | [VEC_LINE11] = "LINE 1111", | ||
171 | [VEC_RESV12] = "UNASSIGNED RESERVED 12", | ||
172 | [VEC_COPROC] = "COPROCESSOR PROTOCOL VIOLATION", | ||
173 | [VEC_FORMAT] = "FORMAT ERROR", | ||
174 | [VEC_UNINT] = "UNINITIALIZED INTERRUPT", | ||
175 | [VEC_RESV16] = "UNASSIGNED RESERVED 16", | ||
176 | [VEC_RESV17] = "UNASSIGNED RESERVED 17", | ||
177 | [VEC_RESV18] = "UNASSIGNED RESERVED 18", | ||
178 | [VEC_RESV19] = "UNASSIGNED RESERVED 19", | ||
179 | [VEC_RESV20] = "UNASSIGNED RESERVED 20", | ||
180 | [VEC_RESV21] = "UNASSIGNED RESERVED 21", | ||
181 | [VEC_RESV22] = "UNASSIGNED RESERVED 22", | ||
182 | [VEC_RESV23] = "UNASSIGNED RESERVED 23", | ||
183 | [VEC_SPUR] = "SPURIOUS INTERRUPT", | ||
184 | [VEC_INT1] = "LEVEL 1 INT", | ||
185 | [VEC_INT2] = "LEVEL 2 INT", | ||
186 | [VEC_INT3] = "LEVEL 3 INT", | ||
187 | [VEC_INT4] = "LEVEL 4 INT", | ||
188 | [VEC_INT5] = "LEVEL 5 INT", | ||
189 | [VEC_INT6] = "LEVEL 6 INT", | ||
190 | [VEC_INT7] = "LEVEL 7 INT", | ||
191 | [VEC_SYS] = "SYSCALL", | ||
192 | [VEC_TRAP1] = "TRAP #1", | ||
193 | [VEC_TRAP2] = "TRAP #2", | ||
194 | [VEC_TRAP3] = "TRAP #3", | ||
195 | [VEC_TRAP4] = "TRAP #4", | ||
196 | [VEC_TRAP5] = "TRAP #5", | ||
197 | [VEC_TRAP6] = "TRAP #6", | ||
198 | [VEC_TRAP7] = "TRAP #7", | ||
199 | [VEC_TRAP8] = "TRAP #8", | ||
200 | [VEC_TRAP9] = "TRAP #9", | ||
201 | [VEC_TRAP10] = "TRAP #10", | ||
202 | [VEC_TRAP11] = "TRAP #11", | ||
203 | [VEC_TRAP12] = "TRAP #12", | ||
204 | [VEC_TRAP13] = "TRAP #13", | ||
205 | [VEC_TRAP14] = "TRAP #14", | ||
206 | [VEC_TRAP15] = "TRAP #15", | ||
207 | [VEC_FPBRUC] = "FPCP BSUN", | ||
208 | [VEC_FPIR] = "FPCP INEXACT", | ||
209 | [VEC_FPDIVZ] = "FPCP DIV BY 0", | ||
210 | [VEC_FPUNDER] = "FPCP UNDERFLOW", | ||
211 | [VEC_FPOE] = "FPCP OPERAND ERROR", | ||
212 | [VEC_FPOVER] = "FPCP OVERFLOW", | ||
213 | [VEC_FPNAN] = "FPCP SNAN", | ||
214 | [VEC_FPUNSUP] = "FPCP UNSUPPORTED OPERATION", | ||
215 | [VEC_MMUCFG] = "MMU CONFIGURATION ERROR", | ||
216 | [VEC_MMUILL] = "MMU ILLEGAL OPERATION ERROR", | ||
217 | [VEC_MMUACC] = "MMU ACCESS LEVEL VIOLATION ERROR", | ||
218 | [VEC_RESV59] = "UNASSIGNED RESERVED 59", | ||
219 | [VEC_UNIMPEA] = "UNASSIGNED RESERVED 60", | ||
220 | [VEC_UNIMPII] = "UNASSIGNED RESERVED 61", | ||
221 | [VEC_RESV62] = "UNASSIGNED RESERVED 62", | ||
222 | [VEC_RESV63] = "UNASSIGNED RESERVED 63", | ||
223 | }; | ||
224 | |||
225 | static const char *space_names[] = { | ||
226 | [0] = "Space 0", | ||
227 | [USER_DATA] = "User Data", | ||
228 | [USER_PROGRAM] = "User Program", | ||
229 | #ifndef CONFIG_SUN3 | ||
230 | [3] = "Space 3", | ||
231 | #else | ||
232 | [FC_CONTROL] = "Control", | ||
233 | #endif | ||
234 | [4] = "Space 4", | ||
235 | [SUPER_DATA] = "Super Data", | ||
236 | [SUPER_PROGRAM] = "Super Program", | ||
237 | [CPU_SPACE] = "CPU" | ||
238 | }; | ||
239 | |||
240 | void die_if_kernel(char *,struct pt_regs *,int); | ||
241 | asmlinkage int do_page_fault(struct pt_regs *regs, unsigned long address, | ||
242 | unsigned long error_code); | ||
243 | int send_fault_sig(struct pt_regs *regs); | ||
244 | |||
245 | asmlinkage void trap_c(struct frame *fp); | ||
246 | |||
247 | #if defined (CONFIG_M68060) | ||
248 | static inline void access_error060 (struct frame *fp) | ||
249 | { | ||
250 | unsigned long fslw = fp->un.fmt4.pc; /* is really FSLW for access error */ | ||
251 | |||
252 | #ifdef DEBUG | ||
253 | printk("fslw=%#lx, fa=%#lx\n", fslw, fp->un.fmt4.effaddr); | ||
254 | #endif | ||
255 | |||
256 | if (fslw & MMU060_BPE) { | ||
257 | /* branch prediction error -> clear branch cache */ | ||
258 | __asm__ __volatile__ ("movec %/cacr,%/d0\n\t" | ||
259 | "orl #0x00400000,%/d0\n\t" | ||
260 | "movec %/d0,%/cacr" | ||
261 | : : : "d0" ); | ||
262 | /* return if there's no other error */ | ||
263 | if (!(fslw & MMU060_ERR_BITS) && !(fslw & MMU060_SEE)) | ||
264 | return; | ||
265 | } | ||
266 | |||
267 | if (fslw & (MMU060_DESC_ERR | MMU060_WP | MMU060_SP)) { | ||
268 | unsigned long errorcode; | ||
269 | unsigned long addr = fp->un.fmt4.effaddr; | ||
270 | |||
271 | if (fslw & MMU060_MA) | ||
272 | addr = (addr + PAGE_SIZE - 1) & PAGE_MASK; | ||
273 | |||
274 | errorcode = 1; | ||
275 | if (fslw & MMU060_DESC_ERR) { | ||
276 | __flush_tlb040_one(addr); | ||
277 | errorcode = 0; | ||
278 | } | ||
279 | if (fslw & MMU060_W) | ||
280 | errorcode |= 2; | ||
281 | #ifdef DEBUG | ||
282 | printk("errorcode = %d\n", errorcode ); | ||
283 | #endif | ||
284 | do_page_fault(&fp->ptregs, addr, errorcode); | ||
285 | } else if (fslw & (MMU060_SEE)){ | ||
286 | /* Software Emulation Error. | ||
287 | * fault during mem_read/mem_write in ifpsp060/os.S | ||
288 | */ | ||
289 | send_fault_sig(&fp->ptregs); | ||
290 | } else if (!(fslw & (MMU060_RE|MMU060_WE)) || | ||
291 | send_fault_sig(&fp->ptregs) > 0) { | ||
292 | printk("pc=%#lx, fa=%#lx\n", fp->ptregs.pc, fp->un.fmt4.effaddr); | ||
293 | printk( "68060 access error, fslw=%lx\n", fslw ); | ||
294 | trap_c( fp ); | ||
295 | } | ||
296 | } | ||
297 | #endif /* CONFIG_M68060 */ | ||
298 | |||
299 | #if defined (CONFIG_M68040) | ||
300 | static inline unsigned long probe040(int iswrite, unsigned long addr, int wbs) | ||
301 | { | ||
302 | unsigned long mmusr; | ||
303 | mm_segment_t old_fs = get_fs(); | ||
304 | |||
305 | set_fs(MAKE_MM_SEG(wbs)); | ||
306 | |||
307 | if (iswrite) | ||
308 | asm volatile (".chip 68040; ptestw (%0); .chip 68k" : : "a" (addr)); | ||
309 | else | ||
310 | asm volatile (".chip 68040; ptestr (%0); .chip 68k" : : "a" (addr)); | ||
311 | |||
312 | asm volatile (".chip 68040; movec %%mmusr,%0; .chip 68k" : "=r" (mmusr)); | ||
313 | |||
314 | set_fs(old_fs); | ||
315 | |||
316 | return mmusr; | ||
317 | } | ||
318 | |||
319 | static inline int do_040writeback1(unsigned short wbs, unsigned long wba, | ||
320 | unsigned long wbd) | ||
321 | { | ||
322 | int res = 0; | ||
323 | mm_segment_t old_fs = get_fs(); | ||
324 | |||
325 | /* set_fs can not be moved, otherwise put_user() may oops */ | ||
326 | set_fs(MAKE_MM_SEG(wbs)); | ||
327 | |||
328 | switch (wbs & WBSIZ_040) { | ||
329 | case BA_SIZE_BYTE: | ||
330 | res = put_user(wbd & 0xff, (char __user *)wba); | ||
331 | break; | ||
332 | case BA_SIZE_WORD: | ||
333 | res = put_user(wbd & 0xffff, (short __user *)wba); | ||
334 | break; | ||
335 | case BA_SIZE_LONG: | ||
336 | res = put_user(wbd, (int __user *)wba); | ||
337 | break; | ||
338 | } | ||
339 | |||
340 | /* set_fs can not be moved, otherwise put_user() may oops */ | ||
341 | set_fs(old_fs); | ||
342 | |||
343 | |||
344 | #ifdef DEBUG | ||
345 | printk("do_040writeback1, res=%d\n",res); | ||
346 | #endif | ||
347 | |||
348 | return res; | ||
349 | } | ||
350 | |||
351 | /* after an exception in a writeback the stack frame corresponding | ||
352 | * to that exception is discarded, set a few bits in the old frame | ||
353 | * to simulate what it should look like | ||
354 | */ | ||
355 | static inline void fix_xframe040(struct frame *fp, unsigned long wba, unsigned short wbs) | ||
356 | { | ||
357 | fp->un.fmt7.faddr = wba; | ||
358 | fp->un.fmt7.ssw = wbs & 0xff; | ||
359 | if (wba != current->thread.faddr) | ||
360 | fp->un.fmt7.ssw |= MA_040; | ||
361 | } | ||
362 | |||
363 | static inline void do_040writebacks(struct frame *fp) | ||
364 | { | ||
365 | int res = 0; | ||
366 | #if 0 | ||
367 | if (fp->un.fmt7.wb1s & WBV_040) | ||
368 | printk("access_error040: cannot handle 1st writeback. oops.\n"); | ||
369 | #endif | ||
370 | |||
371 | if ((fp->un.fmt7.wb2s & WBV_040) && | ||
372 | !(fp->un.fmt7.wb2s & WBTT_040)) { | ||
373 | res = do_040writeback1(fp->un.fmt7.wb2s, fp->un.fmt7.wb2a, | ||
374 | fp->un.fmt7.wb2d); | ||
375 | if (res) | ||
376 | fix_xframe040(fp, fp->un.fmt7.wb2a, fp->un.fmt7.wb2s); | ||
377 | else | ||
378 | fp->un.fmt7.wb2s = 0; | ||
379 | } | ||
380 | |||
381 | /* do the 2nd wb only if the first one was successful (except for a kernel wb) */ | ||
382 | if (fp->un.fmt7.wb3s & WBV_040 && (!res || fp->un.fmt7.wb3s & 4)) { | ||
383 | res = do_040writeback1(fp->un.fmt7.wb3s, fp->un.fmt7.wb3a, | ||
384 | fp->un.fmt7.wb3d); | ||
385 | if (res) | ||
386 | { | ||
387 | fix_xframe040(fp, fp->un.fmt7.wb3a, fp->un.fmt7.wb3s); | ||
388 | |||
389 | fp->un.fmt7.wb2s = fp->un.fmt7.wb3s; | ||
390 | fp->un.fmt7.wb3s &= (~WBV_040); | ||
391 | fp->un.fmt7.wb2a = fp->un.fmt7.wb3a; | ||
392 | fp->un.fmt7.wb2d = fp->un.fmt7.wb3d; | ||
393 | } | ||
394 | else | ||
395 | fp->un.fmt7.wb3s = 0; | ||
396 | } | ||
397 | |||
398 | if (res) | ||
399 | send_fault_sig(&fp->ptregs); | ||
400 | } | ||
401 | |||
402 | /* | ||
403 | * called from sigreturn(), must ensure userspace code didn't | ||
404 | * manipulate exception frame to circumvent protection, then complete | ||
405 | * pending writebacks | ||
406 | * we just clear TM2 to turn it into a userspace access | ||
407 | */ | ||
408 | asmlinkage void berr_040cleanup(struct frame *fp) | ||
409 | { | ||
410 | fp->un.fmt7.wb2s &= ~4; | ||
411 | fp->un.fmt7.wb3s &= ~4; | ||
412 | |||
413 | do_040writebacks(fp); | ||
414 | } | ||
415 | |||
416 | static inline void access_error040(struct frame *fp) | ||
417 | { | ||
418 | unsigned short ssw = fp->un.fmt7.ssw; | ||
419 | unsigned long mmusr; | ||
420 | |||
421 | #ifdef DEBUG | ||
422 | printk("ssw=%#x, fa=%#lx\n", ssw, fp->un.fmt7.faddr); | ||
423 | printk("wb1s=%#x, wb2s=%#x, wb3s=%#x\n", fp->un.fmt7.wb1s, | ||
424 | fp->un.fmt7.wb2s, fp->un.fmt7.wb3s); | ||
425 | printk ("wb2a=%lx, wb3a=%lx, wb2d=%lx, wb3d=%lx\n", | ||
426 | fp->un.fmt7.wb2a, fp->un.fmt7.wb3a, | ||
427 | fp->un.fmt7.wb2d, fp->un.fmt7.wb3d); | ||
428 | #endif | ||
429 | |||
430 | if (ssw & ATC_040) { | ||
431 | unsigned long addr = fp->un.fmt7.faddr; | ||
432 | unsigned long errorcode; | ||
433 | |||
434 | /* | ||
435 | * The MMU status has to be determined AFTER the address | ||
436 | * has been corrected if there was a misaligned access (MA). | ||
437 | */ | ||
438 | if (ssw & MA_040) | ||
439 | addr = (addr + 7) & -8; | ||
440 | |||
441 | /* MMU error, get the MMUSR info for this access */ | ||
442 | mmusr = probe040(!(ssw & RW_040), addr, ssw); | ||
443 | #ifdef DEBUG | ||
444 | printk("mmusr = %lx\n", mmusr); | ||
445 | #endif | ||
446 | errorcode = 1; | ||
447 | if (!(mmusr & MMU_R_040)) { | ||
448 | /* clear the invalid atc entry */ | ||
449 | __flush_tlb040_one(addr); | ||
450 | errorcode = 0; | ||
451 | } | ||
452 | |||
453 | /* despite what documentation seems to say, RMW | ||
454 | * accesses have always both the LK and RW bits set */ | ||
455 | if (!(ssw & RW_040) || (ssw & LK_040)) | ||
456 | errorcode |= 2; | ||
457 | |||
458 | if (do_page_fault(&fp->ptregs, addr, errorcode)) { | ||
459 | #ifdef DEBUG | ||
460 | printk("do_page_fault() !=0\n"); | ||
461 | #endif | ||
462 | if (user_mode(&fp->ptregs)){ | ||
463 | /* delay writebacks after signal delivery */ | ||
464 | #ifdef DEBUG | ||
465 | printk(".. was usermode - return\n"); | ||
466 | #endif | ||
467 | return; | ||
468 | } | ||
469 | /* disable writeback into user space from kernel | ||
470 | * (if do_page_fault didn't fix the mapping, | ||
471 | * the writeback won't do good) | ||
472 | */ | ||
473 | disable_wb: | ||
474 | #ifdef DEBUG | ||
475 | printk(".. disabling wb2\n"); | ||
476 | #endif | ||
477 | if (fp->un.fmt7.wb2a == fp->un.fmt7.faddr) | ||
478 | fp->un.fmt7.wb2s &= ~WBV_040; | ||
479 | if (fp->un.fmt7.wb3a == fp->un.fmt7.faddr) | ||
480 | fp->un.fmt7.wb3s &= ~WBV_040; | ||
481 | } | ||
482 | } else { | ||
483 | /* In case of a bus error we either kill the process or expect | ||
484 | * the kernel to catch the fault, which then is also responsible | ||
485 | * for cleaning up the mess. | ||
486 | */ | ||
487 | current->thread.signo = SIGBUS; | ||
488 | current->thread.faddr = fp->un.fmt7.faddr; | ||
489 | if (send_fault_sig(&fp->ptregs) >= 0) | ||
490 | printk("68040 bus error (ssw=%x, faddr=%lx)\n", ssw, | ||
491 | fp->un.fmt7.faddr); | ||
492 | goto disable_wb; | ||
493 | } | ||
494 | |||
495 | do_040writebacks(fp); | ||
496 | } | ||
497 | #endif /* CONFIG_M68040 */ | ||
498 | |||
499 | #if defined(CONFIG_SUN3) | ||
500 | #include <asm/sun3mmu.h> | ||
501 | |||
502 | extern int mmu_emu_handle_fault (unsigned long, int, int); | ||
503 | |||
504 | /* sun3 version of bus_error030 */ | ||
505 | |||
506 | static inline void bus_error030 (struct frame *fp) | ||
507 | { | ||
508 | unsigned char buserr_type = sun3_get_buserr (); | ||
509 | unsigned long addr, errorcode; | ||
510 | unsigned short ssw = fp->un.fmtb.ssw; | ||
511 | extern unsigned long _sun3_map_test_start, _sun3_map_test_end; | ||
512 | |||
513 | #ifdef DEBUG | ||
514 | if (ssw & (FC | FB)) | ||
515 | printk ("Instruction fault at %#010lx\n", | ||
516 | ssw & FC ? | ||
517 | fp->ptregs.format == 0xa ? fp->ptregs.pc + 2 : fp->un.fmtb.baddr - 2 | ||
518 | : | ||
519 | fp->ptregs.format == 0xa ? fp->ptregs.pc + 4 : fp->un.fmtb.baddr); | ||
520 | if (ssw & DF) | ||
521 | printk ("Data %s fault at %#010lx in %s (pc=%#lx)\n", | ||
522 | ssw & RW ? "read" : "write", | ||
523 | fp->un.fmtb.daddr, | ||
524 | space_names[ssw & DFC], fp->ptregs.pc); | ||
525 | #endif | ||
526 | |||
527 | /* | ||
528 | * Check if this page should be demand-mapped. This needs to go before | ||
529 | * the testing for a bad kernel-space access (demand-mapping applies | ||
530 | * to kernel accesses too). | ||
531 | */ | ||
532 | |||
533 | if ((ssw & DF) | ||
534 | && (buserr_type & (SUN3_BUSERR_PROTERR | SUN3_BUSERR_INVALID))) { | ||
535 | if (mmu_emu_handle_fault (fp->un.fmtb.daddr, ssw & RW, 0)) | ||
536 | return; | ||
537 | } | ||
538 | |||
539 | /* Check for kernel-space pagefault (BAD). */ | ||
540 | if (fp->ptregs.sr & PS_S) { | ||
541 | /* kernel fault must be a data fault to user space */ | ||
542 | if (! ((ssw & DF) && ((ssw & DFC) == USER_DATA))) { | ||
543 | // try checking the kernel mappings before surrender | ||
544 | if (mmu_emu_handle_fault (fp->un.fmtb.daddr, ssw & RW, 1)) | ||
545 | return; | ||
546 | /* instruction fault or kernel data fault! */ | ||
547 | if (ssw & (FC | FB)) | ||
548 | printk ("Instruction fault at %#010lx\n", | ||
549 | fp->ptregs.pc); | ||
550 | if (ssw & DF) { | ||
551 | /* was this fault incurred testing bus mappings? */ | ||
552 | if((fp->ptregs.pc >= (unsigned long)&_sun3_map_test_start) && | ||
553 | (fp->ptregs.pc <= (unsigned long)&_sun3_map_test_end)) { | ||
554 | send_fault_sig(&fp->ptregs); | ||
555 | return; | ||
556 | } | ||
557 | |||
558 | printk ("Data %s fault at %#010lx in %s (pc=%#lx)\n", | ||
559 | ssw & RW ? "read" : "write", | ||
560 | fp->un.fmtb.daddr, | ||
561 | space_names[ssw & DFC], fp->ptregs.pc); | ||
562 | } | ||
563 | printk ("BAD KERNEL BUSERR\n"); | ||
564 | |||
565 | die_if_kernel("Oops", &fp->ptregs,0); | ||
566 | force_sig(SIGKILL, current); | ||
567 | return; | ||
568 | } | ||
569 | } else { | ||
570 | /* user fault */ | ||
571 | if (!(ssw & (FC | FB)) && !(ssw & DF)) | ||
572 | /* not an instruction fault or data fault! BAD */ | ||
573 | panic ("USER BUSERR w/o instruction or data fault"); | ||
574 | } | ||
575 | |||
576 | |||
577 | /* First handle the data fault, if any. */ | ||
578 | if (ssw & DF) { | ||
579 | addr = fp->un.fmtb.daddr; | ||
580 | |||
581 | // errorcode bit 0: 0 -> no page 1 -> protection fault | ||
582 | // errorcode bit 1: 0 -> read fault 1 -> write fault | ||
583 | |||
584 | // (buserr_type & SUN3_BUSERR_PROTERR) -> protection fault | ||
585 | // (buserr_type & SUN3_BUSERR_INVALID) -> invalid page fault | ||
586 | |||
587 | if (buserr_type & SUN3_BUSERR_PROTERR) | ||
588 | errorcode = 0x01; | ||
589 | else if (buserr_type & SUN3_BUSERR_INVALID) | ||
590 | errorcode = 0x00; | ||
591 | else { | ||
592 | #ifdef DEBUG | ||
593 | printk ("*** unexpected busfault type=%#04x\n", buserr_type); | ||
594 | printk ("invalid %s access at %#lx from pc %#lx\n", | ||
595 | !(ssw & RW) ? "write" : "read", addr, | ||
596 | fp->ptregs.pc); | ||
597 | #endif | ||
598 | die_if_kernel ("Oops", &fp->ptregs, buserr_type); | ||
599 | force_sig (SIGBUS, current); | ||
600 | return; | ||
601 | } | ||
602 | |||
603 | //todo: wtf is RM bit? --m | ||
604 | if (!(ssw & RW) || ssw & RM) | ||
605 | errorcode |= 0x02; | ||
606 | |||
607 | /* Handle page fault. */ | ||
608 | do_page_fault (&fp->ptregs, addr, errorcode); | ||
609 | |||
610 | /* Retry the data fault now. */ | ||
611 | return; | ||
612 | } | ||
613 | |||
614 | /* Now handle the instruction fault. */ | ||
615 | |||
616 | /* Get the fault address. */ | ||
617 | if (fp->ptregs.format == 0xA) | ||
618 | addr = fp->ptregs.pc + 4; | ||
619 | else | ||
620 | addr = fp->un.fmtb.baddr; | ||
621 | if (ssw & FC) | ||
622 | addr -= 2; | ||
623 | |||
624 | if (buserr_type & SUN3_BUSERR_INVALID) { | ||
625 | if (!mmu_emu_handle_fault (fp->un.fmtb.daddr, 1, 0)) | ||
626 | do_page_fault (&fp->ptregs, addr, 0); | ||
627 | } else { | ||
628 | #ifdef DEBUG | ||
629 | printk ("protection fault on insn access (segv).\n"); | ||
630 | #endif | ||
631 | force_sig (SIGSEGV, current); | ||
632 | } | ||
633 | } | ||
634 | #else | ||
635 | #if defined(CPU_M68020_OR_M68030) | ||
636 | static inline void bus_error030 (struct frame *fp) | ||
637 | { | ||
638 | volatile unsigned short temp; | ||
639 | unsigned short mmusr; | ||
640 | unsigned long addr, errorcode; | ||
641 | unsigned short ssw = fp->un.fmtb.ssw; | ||
642 | #ifdef DEBUG | ||
643 | unsigned long desc; | ||
644 | |||
645 | printk ("pid = %x ", current->pid); | ||
646 | printk ("SSW=%#06x ", ssw); | ||
647 | |||
648 | if (ssw & (FC | FB)) | ||
649 | printk ("Instruction fault at %#010lx\n", | ||
650 | ssw & FC ? | ||
651 | fp->ptregs.format == 0xa ? fp->ptregs.pc + 2 : fp->un.fmtb.baddr - 2 | ||
652 | : | ||
653 | fp->ptregs.format == 0xa ? fp->ptregs.pc + 4 : fp->un.fmtb.baddr); | ||
654 | if (ssw & DF) | ||
655 | printk ("Data %s fault at %#010lx in %s (pc=%#lx)\n", | ||
656 | ssw & RW ? "read" : "write", | ||
657 | fp->un.fmtb.daddr, | ||
658 | space_names[ssw & DFC], fp->ptregs.pc); | ||
659 | #endif | ||
660 | |||
661 | /* ++andreas: If a data fault and an instruction fault happen | ||
662 | at the same time map in both pages. */ | ||
663 | |||
664 | /* First handle the data fault, if any. */ | ||
665 | if (ssw & DF) { | ||
666 | addr = fp->un.fmtb.daddr; | ||
667 | |||
668 | #ifdef DEBUG | ||
669 | asm volatile ("ptestr %3,%2@,#7,%0\n\t" | ||
670 | "pmove %%psr,%1@" | ||
671 | : "=a&" (desc) | ||
672 | : "a" (&temp), "a" (addr), "d" (ssw)); | ||
673 | #else | ||
674 | asm volatile ("ptestr %2,%1@,#7\n\t" | ||
675 | "pmove %%psr,%0@" | ||
676 | : : "a" (&temp), "a" (addr), "d" (ssw)); | ||
677 | #endif | ||
678 | mmusr = temp; | ||
679 | |||
680 | #ifdef DEBUG | ||
681 | printk("mmusr is %#x for addr %#lx in task %p\n", | ||
682 | mmusr, addr, current); | ||
683 | printk("descriptor address is %#lx, contents %#lx\n", | ||
684 | __va(desc), *(unsigned long *)__va(desc)); | ||
685 | #endif | ||
686 | |||
687 | errorcode = (mmusr & MMU_I) ? 0 : 1; | ||
688 | if (!(ssw & RW) || (ssw & RM)) | ||
689 | errorcode |= 2; | ||
690 | |||
691 | if (mmusr & (MMU_I | MMU_WP)) { | ||
692 | if (ssw & 4) { | ||
693 | printk("Data %s fault at %#010lx in %s (pc=%#lx)\n", | ||
694 | ssw & RW ? "read" : "write", | ||
695 | fp->un.fmtb.daddr, | ||
696 | space_names[ssw & DFC], fp->ptregs.pc); | ||
697 | goto buserr; | ||
698 | } | ||
699 | /* Don't try to do anything further if an exception was | ||
700 | handled. */ | ||
701 | if (do_page_fault (&fp->ptregs, addr, errorcode) < 0) | ||
702 | return; | ||
703 | } else if (!(mmusr & MMU_I)) { | ||
704 | /* probably a 020 cas fault */ | ||
705 | if (!(ssw & RM) && send_fault_sig(&fp->ptregs) > 0) | ||
706 | printk("unexpected bus error (%#x,%#x)\n", ssw, mmusr); | ||
707 | } else if (mmusr & (MMU_B|MMU_L|MMU_S)) { | ||
708 | printk("invalid %s access at %#lx from pc %#lx\n", | ||
709 | !(ssw & RW) ? "write" : "read", addr, | ||
710 | fp->ptregs.pc); | ||
711 | die_if_kernel("Oops",&fp->ptregs,mmusr); | ||
712 | force_sig(SIGSEGV, current); | ||
713 | return; | ||
714 | } else { | ||
715 | #if 0 | ||
716 | static volatile long tlong; | ||
717 | #endif | ||
718 | |||
719 | printk("weird %s access at %#lx from pc %#lx (ssw is %#x)\n", | ||
720 | !(ssw & RW) ? "write" : "read", addr, | ||
721 | fp->ptregs.pc, ssw); | ||
722 | asm volatile ("ptestr #1,%1@,#0\n\t" | ||
723 | "pmove %%psr,%0@" | ||
724 | : /* no outputs */ | ||
725 | : "a" (&temp), "a" (addr)); | ||
726 | mmusr = temp; | ||
727 | |||
728 | printk ("level 0 mmusr is %#x\n", mmusr); | ||
729 | #if 0 | ||
730 | asm volatile ("pmove %%tt0,%0@" | ||
731 | : /* no outputs */ | ||
732 | : "a" (&tlong)); | ||
733 | printk("tt0 is %#lx, ", tlong); | ||
734 | asm volatile ("pmove %%tt1,%0@" | ||
735 | : /* no outputs */ | ||
736 | : "a" (&tlong)); | ||
737 | printk("tt1 is %#lx\n", tlong); | ||
738 | #endif | ||
739 | #ifdef DEBUG | ||
740 | printk("Unknown SIGSEGV - 1\n"); | ||
741 | #endif | ||
742 | die_if_kernel("Oops",&fp->ptregs,mmusr); | ||
743 | force_sig(SIGSEGV, current); | ||
744 | return; | ||
745 | } | ||
746 | |||
747 | /* setup an ATC entry for the access about to be retried */ | ||
748 | if (!(ssw & RW) || (ssw & RM)) | ||
749 | asm volatile ("ploadw %1,%0@" : /* no outputs */ | ||
750 | : "a" (addr), "d" (ssw)); | ||
751 | else | ||
752 | asm volatile ("ploadr %1,%0@" : /* no outputs */ | ||
753 | : "a" (addr), "d" (ssw)); | ||
754 | } | ||
755 | |||
756 | /* Now handle the instruction fault. */ | ||
757 | |||
758 | if (!(ssw & (FC|FB))) | ||
759 | return; | ||
760 | |||
761 | if (fp->ptregs.sr & PS_S) { | ||
762 | printk("Instruction fault at %#010lx\n", | ||
763 | fp->ptregs.pc); | ||
764 | buserr: | ||
765 | printk ("BAD KERNEL BUSERR\n"); | ||
766 | die_if_kernel("Oops",&fp->ptregs,0); | ||
767 | force_sig(SIGKILL, current); | ||
768 | return; | ||
769 | } | ||
770 | |||
771 | /* get the fault address */ | ||
772 | if (fp->ptregs.format == 10) | ||
773 | addr = fp->ptregs.pc + 4; | ||
774 | else | ||
775 | addr = fp->un.fmtb.baddr; | ||
776 | if (ssw & FC) | ||
777 | addr -= 2; | ||
778 | |||
779 | if ((ssw & DF) && ((addr ^ fp->un.fmtb.daddr) & PAGE_MASK) == 0) | ||
780 | /* Insn fault on same page as data fault. But we | ||
781 | should still create the ATC entry. */ | ||
782 | goto create_atc_entry; | ||
783 | |||
784 | #ifdef DEBUG | ||
785 | asm volatile ("ptestr #1,%2@,#7,%0\n\t" | ||
786 | "pmove %%psr,%1@" | ||
787 | : "=a&" (desc) | ||
788 | : "a" (&temp), "a" (addr)); | ||
789 | #else | ||
790 | asm volatile ("ptestr #1,%1@,#7\n\t" | ||
791 | "pmove %%psr,%0@" | ||
792 | : : "a" (&temp), "a" (addr)); | ||
793 | #endif | ||
794 | mmusr = temp; | ||
795 | |||
796 | #ifdef DEBUG | ||
797 | printk ("mmusr is %#x for addr %#lx in task %p\n", | ||
798 | mmusr, addr, current); | ||
799 | printk ("descriptor address is %#lx, contents %#lx\n", | ||
800 | __va(desc), *(unsigned long *)__va(desc)); | ||
801 | #endif | ||
802 | |||
803 | if (mmusr & MMU_I) | ||
804 | do_page_fault (&fp->ptregs, addr, 0); | ||
805 | else if (mmusr & (MMU_B|MMU_L|MMU_S)) { | ||
806 | printk ("invalid insn access at %#lx from pc %#lx\n", | ||
807 | addr, fp->ptregs.pc); | ||
808 | #ifdef DEBUG | ||
809 | printk("Unknown SIGSEGV - 2\n"); | ||
810 | #endif | ||
811 | die_if_kernel("Oops",&fp->ptregs,mmusr); | ||
812 | force_sig(SIGSEGV, current); | ||
813 | return; | ||
814 | } | ||
815 | |||
816 | create_atc_entry: | ||
817 | /* setup an ATC entry for the access about to be retried */ | ||
818 | asm volatile ("ploadr #2,%0@" : /* no outputs */ | ||
819 | : "a" (addr)); | ||
820 | } | ||
821 | #endif /* CPU_M68020_OR_M68030 */ | ||
822 | #endif /* !CONFIG_SUN3 */ | ||
823 | |||
824 | asmlinkage void buserr_c(struct frame *fp) | ||
825 | { | ||
826 | /* Only set esp0 if coming from user mode */ | ||
827 | if (user_mode(&fp->ptregs)) | ||
828 | current->thread.esp0 = (unsigned long) fp; | ||
829 | |||
830 | #ifdef DEBUG | ||
831 | printk ("*** Bus Error *** Format is %x\n", fp->ptregs.format); | ||
832 | #endif | ||
833 | |||
834 | switch (fp->ptregs.format) { | ||
835 | #if defined (CONFIG_M68060) | ||
836 | case 4: /* 68060 access error */ | ||
837 | access_error060 (fp); | ||
838 | break; | ||
839 | #endif | ||
840 | #if defined (CONFIG_M68040) | ||
841 | case 0x7: /* 68040 access error */ | ||
842 | access_error040 (fp); | ||
843 | break; | ||
844 | #endif | ||
845 | #if defined (CPU_M68020_OR_M68030) | ||
846 | case 0xa: | ||
847 | case 0xb: | ||
848 | bus_error030 (fp); | ||
849 | break; | ||
850 | #endif | ||
851 | default: | ||
852 | die_if_kernel("bad frame format",&fp->ptregs,0); | ||
853 | #ifdef DEBUG | ||
854 | printk("Unknown SIGSEGV - 4\n"); | ||
855 | #endif | ||
856 | force_sig(SIGSEGV, current); | ||
857 | } | ||
858 | } | ||
859 | |||
860 | |||
861 | static int kstack_depth_to_print = 48; | ||
862 | |||
863 | void show_trace(unsigned long *stack) | ||
864 | { | ||
865 | unsigned long *endstack; | ||
866 | unsigned long addr; | ||
867 | int i; | ||
868 | |||
869 | printk("Call Trace:"); | ||
870 | addr = (unsigned long)stack + THREAD_SIZE - 1; | ||
871 | endstack = (unsigned long *)(addr & -THREAD_SIZE); | ||
872 | i = 0; | ||
873 | while (stack + 1 <= endstack) { | ||
874 | addr = *stack++; | ||
875 | /* | ||
876 | * If the address is either in the text segment of the | ||
877 | * kernel, or in the region which contains vmalloc'ed | ||
878 | * memory, it *may* be the address of a calling | ||
879 | * routine; if so, print it so that someone tracing | ||
880 | * down the cause of the crash will be able to figure | ||
881 | * out the call path that was taken. | ||
882 | */ | ||
883 | if (__kernel_text_address(addr)) { | ||
884 | #ifndef CONFIG_KALLSYMS | ||
885 | if (i % 5 == 0) | ||
886 | printk("\n "); | ||
887 | #endif | ||
888 | printk(" [<%08lx>] %pS\n", addr, (void *)addr); | ||
889 | i++; | ||
890 | } | ||
891 | } | ||
892 | printk("\n"); | ||
893 | } | ||
894 | |||
895 | void show_registers(struct pt_regs *regs) | ||
896 | { | ||
897 | struct frame *fp = (struct frame *)regs; | ||
898 | mm_segment_t old_fs = get_fs(); | ||
899 | u16 c, *cp; | ||
900 | unsigned long addr; | ||
901 | int i; | ||
902 | |||
903 | print_modules(); | ||
904 | printk("PC: [<%08lx>] %pS\n", regs->pc, (void *)regs->pc); | ||
905 | printk("SR: %04x SP: %p a2: %08lx\n", regs->sr, regs, regs->a2); | ||
906 | printk("d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", | ||
907 | regs->d0, regs->d1, regs->d2, regs->d3); | ||
908 | printk("d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n", | ||
909 | regs->d4, regs->d5, regs->a0, regs->a1); | ||
910 | |||
911 | printk("Process %s (pid: %d, task=%p)\n", | ||
912 | current->comm, task_pid_nr(current), current); | ||
913 | addr = (unsigned long)&fp->un; | ||
914 | printk("Frame format=%X ", regs->format); | ||
915 | switch (regs->format) { | ||
916 | case 0x2: | ||
917 | printk("instr addr=%08lx\n", fp->un.fmt2.iaddr); | ||
918 | addr += sizeof(fp->un.fmt2); | ||
919 | break; | ||
920 | case 0x3: | ||
921 | printk("eff addr=%08lx\n", fp->un.fmt3.effaddr); | ||
922 | addr += sizeof(fp->un.fmt3); | ||
923 | break; | ||
924 | case 0x4: | ||
925 | printk((CPU_IS_060 ? "fault addr=%08lx fslw=%08lx\n" | ||
926 | : "eff addr=%08lx pc=%08lx\n"), | ||
927 | fp->un.fmt4.effaddr, fp->un.fmt4.pc); | ||
928 | addr += sizeof(fp->un.fmt4); | ||
929 | break; | ||
930 | case 0x7: | ||
931 | printk("eff addr=%08lx ssw=%04x faddr=%08lx\n", | ||
932 | fp->un.fmt7.effaddr, fp->un.fmt7.ssw, fp->un.fmt7.faddr); | ||
933 | printk("wb 1 stat/addr/data: %04x %08lx %08lx\n", | ||
934 | fp->un.fmt7.wb1s, fp->un.fmt7.wb1a, fp->un.fmt7.wb1dpd0); | ||
935 | printk("wb 2 stat/addr/data: %04x %08lx %08lx\n", | ||
936 | fp->un.fmt7.wb2s, fp->un.fmt7.wb2a, fp->un.fmt7.wb2d); | ||
937 | printk("wb 3 stat/addr/data: %04x %08lx %08lx\n", | ||
938 | fp->un.fmt7.wb3s, fp->un.fmt7.wb3a, fp->un.fmt7.wb3d); | ||
939 | printk("push data: %08lx %08lx %08lx %08lx\n", | ||
940 | fp->un.fmt7.wb1dpd0, fp->un.fmt7.pd1, fp->un.fmt7.pd2, | ||
941 | fp->un.fmt7.pd3); | ||
942 | addr += sizeof(fp->un.fmt7); | ||
943 | break; | ||
944 | case 0x9: | ||
945 | printk("instr addr=%08lx\n", fp->un.fmt9.iaddr); | ||
946 | addr += sizeof(fp->un.fmt9); | ||
947 | break; | ||
948 | case 0xa: | ||
949 | printk("ssw=%04x isc=%04x isb=%04x daddr=%08lx dobuf=%08lx\n", | ||
950 | fp->un.fmta.ssw, fp->un.fmta.isc, fp->un.fmta.isb, | ||
951 | fp->un.fmta.daddr, fp->un.fmta.dobuf); | ||
952 | addr += sizeof(fp->un.fmta); | ||
953 | break; | ||
954 | case 0xb: | ||
955 | printk("ssw=%04x isc=%04x isb=%04x daddr=%08lx dobuf=%08lx\n", | ||
956 | fp->un.fmtb.ssw, fp->un.fmtb.isc, fp->un.fmtb.isb, | ||
957 | fp->un.fmtb.daddr, fp->un.fmtb.dobuf); | ||
958 | printk("baddr=%08lx dibuf=%08lx ver=%x\n", | ||
959 | fp->un.fmtb.baddr, fp->un.fmtb.dibuf, fp->un.fmtb.ver); | ||
960 | addr += sizeof(fp->un.fmtb); | ||
961 | break; | ||
962 | default: | ||
963 | printk("\n"); | ||
964 | } | ||
965 | show_stack(NULL, (unsigned long *)addr); | ||
966 | |||
967 | printk("Code:"); | ||
968 | set_fs(KERNEL_DS); | ||
969 | cp = (u16 *)regs->pc; | ||
970 | for (i = -8; i < 16; i++) { | ||
971 | if (get_user(c, cp + i) && i >= 0) { | ||
972 | printk(" Bad PC value."); | ||
973 | break; | ||
974 | } | ||
975 | printk(i ? " %04x" : " <%04x>", c); | ||
976 | } | ||
977 | set_fs(old_fs); | ||
978 | printk ("\n"); | ||
979 | } | ||
980 | |||
981 | void show_stack(struct task_struct *task, unsigned long *stack) | ||
982 | { | ||
983 | unsigned long *p; | ||
984 | unsigned long *endstack; | ||
985 | int i; | ||
986 | |||
987 | if (!stack) { | ||
988 | if (task) | ||
989 | stack = (unsigned long *)task->thread.esp0; | ||
990 | else | ||
991 | stack = (unsigned long *)&stack; | ||
992 | } | ||
993 | endstack = (unsigned long *)(((unsigned long)stack + THREAD_SIZE - 1) & -THREAD_SIZE); | ||
994 | |||
995 | printk("Stack from %08lx:", (unsigned long)stack); | ||
996 | p = stack; | ||
997 | for (i = 0; i < kstack_depth_to_print; i++) { | ||
998 | if (p + 1 > endstack) | ||
999 | break; | ||
1000 | if (i % 8 == 0) | ||
1001 | printk("\n "); | ||
1002 | printk(" %08lx", *p++); | ||
1003 | } | ||
1004 | printk("\n"); | ||
1005 | show_trace(stack); | ||
1006 | } | ||
1007 | |||
1008 | /* | ||
1009 | * The architecture-independent backtrace generator | ||
1010 | */ | ||
1011 | void dump_stack(void) | ||
1012 | { | ||
1013 | unsigned long stack; | ||
1014 | |||
1015 | show_trace(&stack); | ||
1016 | } | ||
1017 | |||
1018 | EXPORT_SYMBOL(dump_stack); | ||
1019 | |||
1020 | void bad_super_trap (struct frame *fp) | ||
1021 | { | ||
1022 | console_verbose(); | ||
1023 | if (fp->ptregs.vector < 4 * ARRAY_SIZE(vec_names)) | ||
1024 | printk ("*** %s *** FORMAT=%X\n", | ||
1025 | vec_names[(fp->ptregs.vector) >> 2], | ||
1026 | fp->ptregs.format); | ||
1027 | else | ||
1028 | printk ("*** Exception %d *** FORMAT=%X\n", | ||
1029 | (fp->ptregs.vector) >> 2, | ||
1030 | fp->ptregs.format); | ||
1031 | if (fp->ptregs.vector >> 2 == VEC_ADDRERR && CPU_IS_020_OR_030) { | ||
1032 | unsigned short ssw = fp->un.fmtb.ssw; | ||
1033 | |||
1034 | printk ("SSW=%#06x ", ssw); | ||
1035 | |||
1036 | if (ssw & RC) | ||
1037 | printk ("Pipe stage C instruction fault at %#010lx\n", | ||
1038 | (fp->ptregs.format) == 0xA ? | ||
1039 | fp->ptregs.pc + 2 : fp->un.fmtb.baddr - 2); | ||
1040 | if (ssw & RB) | ||
1041 | printk ("Pipe stage B instruction fault at %#010lx\n", | ||
1042 | (fp->ptregs.format) == 0xA ? | ||
1043 | fp->ptregs.pc + 4 : fp->un.fmtb.baddr); | ||
1044 | if (ssw & DF) | ||
1045 | printk ("Data %s fault at %#010lx in %s (pc=%#lx)\n", | ||
1046 | ssw & RW ? "read" : "write", | ||
1047 | fp->un.fmtb.daddr, space_names[ssw & DFC], | ||
1048 | fp->ptregs.pc); | ||
1049 | } | ||
1050 | printk ("Current process id is %d\n", task_pid_nr(current)); | ||
1051 | die_if_kernel("BAD KERNEL TRAP", &fp->ptregs, 0); | ||
1052 | } | ||
1053 | |||
1054 | asmlinkage void trap_c(struct frame *fp) | ||
1055 | { | ||
1056 | int sig; | ||
1057 | siginfo_t info; | ||
1058 | |||
1059 | if (fp->ptregs.sr & PS_S) { | ||
1060 | if (fp->ptregs.vector == VEC_TRACE << 2) { | ||
1061 | /* traced a trapping instruction on a 68020/30, | ||
1062 | * real exception will be executed afterwards. | ||
1063 | */ | ||
1064 | } else if (!handle_kernel_fault(&fp->ptregs)) | ||
1065 | bad_super_trap(fp); | ||
1066 | return; | ||
1067 | } | ||
1068 | |||
1069 | /* send the appropriate signal to the user program */ | ||
1070 | switch ((fp->ptregs.vector) >> 2) { | ||
1071 | case VEC_ADDRERR: | ||
1072 | info.si_code = BUS_ADRALN; | ||
1073 | sig = SIGBUS; | ||
1074 | break; | ||
1075 | case VEC_ILLEGAL: | ||
1076 | case VEC_LINE10: | ||
1077 | case VEC_LINE11: | ||
1078 | info.si_code = ILL_ILLOPC; | ||
1079 | sig = SIGILL; | ||
1080 | break; | ||
1081 | case VEC_PRIV: | ||
1082 | info.si_code = ILL_PRVOPC; | ||
1083 | sig = SIGILL; | ||
1084 | break; | ||
1085 | case VEC_COPROC: | ||
1086 | info.si_code = ILL_COPROC; | ||
1087 | sig = SIGILL; | ||
1088 | break; | ||
1089 | case VEC_TRAP1: | ||
1090 | case VEC_TRAP2: | ||
1091 | case VEC_TRAP3: | ||
1092 | case VEC_TRAP4: | ||
1093 | case VEC_TRAP5: | ||
1094 | case VEC_TRAP6: | ||
1095 | case VEC_TRAP7: | ||
1096 | case VEC_TRAP8: | ||
1097 | case VEC_TRAP9: | ||
1098 | case VEC_TRAP10: | ||
1099 | case VEC_TRAP11: | ||
1100 | case VEC_TRAP12: | ||
1101 | case VEC_TRAP13: | ||
1102 | case VEC_TRAP14: | ||
1103 | info.si_code = ILL_ILLTRP; | ||
1104 | sig = SIGILL; | ||
1105 | break; | ||
1106 | case VEC_FPBRUC: | ||
1107 | case VEC_FPOE: | ||
1108 | case VEC_FPNAN: | ||
1109 | info.si_code = FPE_FLTINV; | ||
1110 | sig = SIGFPE; | ||
1111 | break; | ||
1112 | case VEC_FPIR: | ||
1113 | info.si_code = FPE_FLTRES; | ||
1114 | sig = SIGFPE; | ||
1115 | break; | ||
1116 | case VEC_FPDIVZ: | ||
1117 | info.si_code = FPE_FLTDIV; | ||
1118 | sig = SIGFPE; | ||
1119 | break; | ||
1120 | case VEC_FPUNDER: | ||
1121 | info.si_code = FPE_FLTUND; | ||
1122 | sig = SIGFPE; | ||
1123 | break; | ||
1124 | case VEC_FPOVER: | ||
1125 | info.si_code = FPE_FLTOVF; | ||
1126 | sig = SIGFPE; | ||
1127 | break; | ||
1128 | case VEC_ZERODIV: | ||
1129 | info.si_code = FPE_INTDIV; | ||
1130 | sig = SIGFPE; | ||
1131 | break; | ||
1132 | case VEC_CHK: | ||
1133 | case VEC_TRAP: | ||
1134 | info.si_code = FPE_INTOVF; | ||
1135 | sig = SIGFPE; | ||
1136 | break; | ||
1137 | case VEC_TRACE: /* ptrace single step */ | ||
1138 | info.si_code = TRAP_TRACE; | ||
1139 | sig = SIGTRAP; | ||
1140 | break; | ||
1141 | case VEC_TRAP15: /* breakpoint */ | ||
1142 | info.si_code = TRAP_BRKPT; | ||
1143 | sig = SIGTRAP; | ||
1144 | break; | ||
1145 | default: | ||
1146 | info.si_code = ILL_ILLOPC; | ||
1147 | sig = SIGILL; | ||
1148 | break; | ||
1149 | } | ||
1150 | info.si_signo = sig; | ||
1151 | info.si_errno = 0; | ||
1152 | switch (fp->ptregs.format) { | ||
1153 | default: | ||
1154 | info.si_addr = (void *) fp->ptregs.pc; | ||
1155 | break; | ||
1156 | case 2: | ||
1157 | info.si_addr = (void *) fp->un.fmt2.iaddr; | ||
1158 | break; | ||
1159 | case 7: | ||
1160 | info.si_addr = (void *) fp->un.fmt7.effaddr; | ||
1161 | break; | ||
1162 | case 9: | ||
1163 | info.si_addr = (void *) fp->un.fmt9.iaddr; | ||
1164 | break; | ||
1165 | case 10: | ||
1166 | info.si_addr = (void *) fp->un.fmta.daddr; | ||
1167 | break; | ||
1168 | case 11: | ||
1169 | info.si_addr = (void *) fp->un.fmtb.daddr; | ||
1170 | break; | ||
1171 | } | ||
1172 | force_sig_info (sig, &info, current); | ||
1173 | } | ||
1174 | |||
1175 | void die_if_kernel (char *str, struct pt_regs *fp, int nr) | ||
1176 | { | ||
1177 | if (!(fp->sr & PS_S)) | ||
1178 | return; | ||
1179 | |||
1180 | console_verbose(); | ||
1181 | printk("%s: %08x\n",str,nr); | ||
1182 | show_registers(fp); | ||
1183 | add_taint(TAINT_DIE); | ||
1184 | do_exit(SIGSEGV); | ||
1185 | } | ||
1186 | |||
1187 | /* | ||
1188 | * This function is called if an error occur while accessing | ||
1189 | * user-space from the fpsp040 code. | ||
1190 | */ | ||
1191 | asmlinkage void fpsp040_die(void) | ||
1192 | { | ||
1193 | do_exit(SIGSEGV); | ||
1194 | } | ||
1195 | |||
1196 | #ifdef CONFIG_M68KFPU_EMU | ||
1197 | asmlinkage void fpemu_signal(int signal, int code, void *addr) | ||
1198 | { | ||
1199 | siginfo_t info; | ||
1200 | |||
1201 | info.si_signo = signal; | ||
1202 | info.si_errno = 0; | ||
1203 | info.si_code = code; | ||
1204 | info.si_addr = addr; | ||
1205 | force_sig_info(signal, &info, current); | ||
1206 | } | ||
1207 | #endif | ||
diff --git a/arch/m68k/kernel/traps_no.c b/arch/m68k/kernel/traps_no.c new file mode 100644 index 000000000000..a768008dfd06 --- /dev/null +++ b/arch/m68k/kernel/traps_no.c | |||
@@ -0,0 +1,365 @@ | |||
1 | /* | ||
2 | * linux/arch/m68knommu/kernel/traps.c | ||
3 | * | ||
4 | * Copyright (C) 1993, 1994 by Hamish Macdonald | ||
5 | * | ||
6 | * 68040 fixes by Michael Rausch | ||
7 | * 68040 fixes by Martin Apel | ||
8 | * 68060 fixes by Roman Hodek | ||
9 | * 68060 fixes by Jesper Skov | ||
10 | * | ||
11 | * This file is subject to the terms and conditions of the GNU General Public | ||
12 | * License. See the file COPYING in the main directory of this archive | ||
13 | * for more details. | ||
14 | */ | ||
15 | |||
16 | /* | ||
17 | * Sets up all exception vectors | ||
18 | */ | ||
19 | #include <linux/sched.h> | ||
20 | #include <linux/signal.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/mm.h> | ||
23 | #include <linux/module.h> | ||
24 | #include <linux/types.h> | ||
25 | #include <linux/user.h> | ||
26 | #include <linux/string.h> | ||
27 | #include <linux/linkage.h> | ||
28 | #include <linux/init.h> | ||
29 | #include <linux/ptrace.h> | ||
30 | #include <linux/kallsyms.h> | ||
31 | |||
32 | #include <asm/setup.h> | ||
33 | #include <asm/fpu.h> | ||
34 | #include <asm/system.h> | ||
35 | #include <asm/uaccess.h> | ||
36 | #include <asm/traps.h> | ||
37 | #include <asm/pgtable.h> | ||
38 | #include <asm/machdep.h> | ||
39 | #include <asm/siginfo.h> | ||
40 | |||
41 | static char const * const vec_names[] = { | ||
42 | "RESET SP", "RESET PC", "BUS ERROR", "ADDRESS ERROR", | ||
43 | "ILLEGAL INSTRUCTION", "ZERO DIVIDE", "CHK", "TRAPcc", | ||
44 | "PRIVILEGE VIOLATION", "TRACE", "LINE 1010", "LINE 1111", | ||
45 | "UNASSIGNED RESERVED 12", "COPROCESSOR PROTOCOL VIOLATION", | ||
46 | "FORMAT ERROR", "UNINITIALIZED INTERRUPT", | ||
47 | "UNASSIGNED RESERVED 16", "UNASSIGNED RESERVED 17", | ||
48 | "UNASSIGNED RESERVED 18", "UNASSIGNED RESERVED 19", | ||
49 | "UNASSIGNED RESERVED 20", "UNASSIGNED RESERVED 21", | ||
50 | "UNASSIGNED RESERVED 22", "UNASSIGNED RESERVED 23", | ||
51 | "SPURIOUS INTERRUPT", "LEVEL 1 INT", "LEVEL 2 INT", "LEVEL 3 INT", | ||
52 | "LEVEL 4 INT", "LEVEL 5 INT", "LEVEL 6 INT", "LEVEL 7 INT", | ||
53 | "SYSCALL", "TRAP #1", "TRAP #2", "TRAP #3", | ||
54 | "TRAP #4", "TRAP #5", "TRAP #6", "TRAP #7", | ||
55 | "TRAP #8", "TRAP #9", "TRAP #10", "TRAP #11", | ||
56 | "TRAP #12", "TRAP #13", "TRAP #14", "TRAP #15", | ||
57 | "FPCP BSUN", "FPCP INEXACT", "FPCP DIV BY 0", "FPCP UNDERFLOW", | ||
58 | "FPCP OPERAND ERROR", "FPCP OVERFLOW", "FPCP SNAN", | ||
59 | "FPCP UNSUPPORTED OPERATION", | ||
60 | "MMU CONFIGURATION ERROR" | ||
61 | }; | ||
62 | |||
63 | void __init trap_init(void) | ||
64 | { | ||
65 | } | ||
66 | |||
67 | void die_if_kernel(char *str, struct pt_regs *fp, int nr) | ||
68 | { | ||
69 | if (!(fp->sr & PS_S)) | ||
70 | return; | ||
71 | |||
72 | console_verbose(); | ||
73 | printk(KERN_EMERG "%s: %08x\n",str,nr); | ||
74 | printk(KERN_EMERG "PC: [<%08lx>]\nSR: %04x SP: %p a2: %08lx\n", | ||
75 | fp->pc, fp->sr, fp, fp->a2); | ||
76 | printk(KERN_EMERG "d0: %08lx d1: %08lx d2: %08lx d3: %08lx\n", | ||
77 | fp->d0, fp->d1, fp->d2, fp->d3); | ||
78 | printk(KERN_EMERG "d4: %08lx d5: %08lx a0: %08lx a1: %08lx\n", | ||
79 | fp->d4, fp->d5, fp->a0, fp->a1); | ||
80 | |||
81 | printk(KERN_EMERG "Process %s (pid: %d, stackpage=%08lx)\n", | ||
82 | current->comm, current->pid, PAGE_SIZE+(unsigned long)current); | ||
83 | show_stack(NULL, (unsigned long *)(fp + 1)); | ||
84 | add_taint(TAINT_DIE); | ||
85 | do_exit(SIGSEGV); | ||
86 | } | ||
87 | |||
88 | asmlinkage void buserr_c(struct frame *fp) | ||
89 | { | ||
90 | /* Only set esp0 if coming from user mode */ | ||
91 | if (user_mode(&fp->ptregs)) | ||
92 | current->thread.esp0 = (unsigned long) fp; | ||
93 | |||
94 | #if defined(DEBUG) | ||
95 | printk (KERN_DEBUG "*** Bus Error *** Format is %x\n", fp->ptregs.format); | ||
96 | #endif | ||
97 | |||
98 | die_if_kernel("bad frame format",&fp->ptregs,0); | ||
99 | #if defined(DEBUG) | ||
100 | printk(KERN_DEBUG "Unknown SIGSEGV - 4\n"); | ||
101 | #endif | ||
102 | force_sig(SIGSEGV, current); | ||
103 | } | ||
104 | |||
105 | static void print_this_address(unsigned long addr, int i) | ||
106 | { | ||
107 | #ifdef CONFIG_KALLSYMS | ||
108 | printk(KERN_EMERG " [%08lx] ", addr); | ||
109 | print_symbol(KERN_CONT "%s\n", addr); | ||
110 | #else | ||
111 | if (i % 5) | ||
112 | printk(KERN_CONT " [%08lx] ", addr); | ||
113 | else | ||
114 | printk(KERN_EMERG " [%08lx] ", addr); | ||
115 | i++; | ||
116 | #endif | ||
117 | } | ||
118 | |||
119 | int kstack_depth_to_print = 48; | ||
120 | |||
121 | static void __show_stack(struct task_struct *task, unsigned long *stack) | ||
122 | { | ||
123 | unsigned long *endstack, addr; | ||
124 | #ifdef CONFIG_FRAME_POINTER | ||
125 | unsigned long *last_stack; | ||
126 | #endif | ||
127 | int i; | ||
128 | |||
129 | if (!stack) | ||
130 | stack = (unsigned long *)task->thread.ksp; | ||
131 | |||
132 | addr = (unsigned long) stack; | ||
133 | endstack = (unsigned long *) PAGE_ALIGN(addr); | ||
134 | |||
135 | printk(KERN_EMERG "Stack from %08lx:", (unsigned long)stack); | ||
136 | for (i = 0; i < kstack_depth_to_print; i++) { | ||
137 | if (stack + 1 + i > endstack) | ||
138 | break; | ||
139 | if (i % 8 == 0) | ||
140 | printk(KERN_EMERG " "); | ||
141 | printk(KERN_CONT " %08lx", *(stack + i)); | ||
142 | } | ||
143 | printk("\n"); | ||
144 | i = 0; | ||
145 | |||
146 | #ifdef CONFIG_FRAME_POINTER | ||
147 | printk(KERN_EMERG "Call Trace:\n"); | ||
148 | |||
149 | last_stack = stack - 1; | ||
150 | while (stack <= endstack && stack > last_stack) { | ||
151 | |||
152 | addr = *(stack + 1); | ||
153 | print_this_address(addr, i); | ||
154 | i++; | ||
155 | |||
156 | last_stack = stack; | ||
157 | stack = (unsigned long *)*stack; | ||
158 | } | ||
159 | printk("\n"); | ||
160 | #else | ||
161 | printk(KERN_EMERG "Call Trace with CONFIG_FRAME_POINTER disabled:\n"); | ||
162 | while (stack <= endstack) { | ||
163 | addr = *stack++; | ||
164 | /* | ||
165 | * If the address is either in the text segment of the kernel, | ||
166 | * or in a region which is occupied by a module then it *may* | ||
167 | * be the address of a calling routine; if so, print it so that | ||
168 | * someone tracing down the cause of the crash will be able to | ||
169 | * figure out the call path that was taken. | ||
170 | */ | ||
171 | if (__kernel_text_address(addr)) { | ||
172 | print_this_address(addr, i); | ||
173 | i++; | ||
174 | } | ||
175 | } | ||
176 | printk(KERN_CONT "\n"); | ||
177 | #endif | ||
178 | } | ||
179 | |||
180 | void bad_super_trap(struct frame *fp) | ||
181 | { | ||
182 | int vector = (fp->ptregs.vector >> 2) & 0xff; | ||
183 | |||
184 | console_verbose(); | ||
185 | if (vector < ARRAY_SIZE(vec_names)) | ||
186 | printk (KERN_WARNING "*** %s *** FORMAT=%X\n", | ||
187 | vec_names[vector], | ||
188 | fp->ptregs.format); | ||
189 | else | ||
190 | printk (KERN_WARNING "*** Exception %d *** FORMAT=%X\n", | ||
191 | vector, | ||
192 | fp->ptregs.format); | ||
193 | printk (KERN_WARNING "Current process id is %d\n", current->pid); | ||
194 | die_if_kernel("BAD KERNEL TRAP", &fp->ptregs, 0); | ||
195 | } | ||
196 | |||
197 | asmlinkage void trap_c(struct frame *fp) | ||
198 | { | ||
199 | int sig; | ||
200 | int vector = (fp->ptregs.vector >> 2) & 0xff; | ||
201 | siginfo_t info; | ||
202 | |||
203 | if (fp->ptregs.sr & PS_S) { | ||
204 | if (vector == VEC_TRACE) { | ||
205 | /* traced a trapping instruction */ | ||
206 | } else | ||
207 | bad_super_trap(fp); | ||
208 | return; | ||
209 | } | ||
210 | |||
211 | /* send the appropriate signal to the user program */ | ||
212 | switch (vector) { | ||
213 | case VEC_ADDRERR: | ||
214 | info.si_code = BUS_ADRALN; | ||
215 | sig = SIGBUS; | ||
216 | break; | ||
217 | case VEC_ILLEGAL: | ||
218 | case VEC_LINE10: | ||
219 | case VEC_LINE11: | ||
220 | info.si_code = ILL_ILLOPC; | ||
221 | sig = SIGILL; | ||
222 | break; | ||
223 | case VEC_PRIV: | ||
224 | info.si_code = ILL_PRVOPC; | ||
225 | sig = SIGILL; | ||
226 | break; | ||
227 | case VEC_COPROC: | ||
228 | info.si_code = ILL_COPROC; | ||
229 | sig = SIGILL; | ||
230 | break; | ||
231 | case VEC_TRAP1: /* gdbserver breakpoint */ | ||
232 | fp->ptregs.pc -= 2; | ||
233 | info.si_code = TRAP_TRACE; | ||
234 | sig = SIGTRAP; | ||
235 | break; | ||
236 | case VEC_TRAP2: | ||
237 | case VEC_TRAP3: | ||
238 | case VEC_TRAP4: | ||
239 | case VEC_TRAP5: | ||
240 | case VEC_TRAP6: | ||
241 | case VEC_TRAP7: | ||
242 | case VEC_TRAP8: | ||
243 | case VEC_TRAP9: | ||
244 | case VEC_TRAP10: | ||
245 | case VEC_TRAP11: | ||
246 | case VEC_TRAP12: | ||
247 | case VEC_TRAP13: | ||
248 | case VEC_TRAP14: | ||
249 | info.si_code = ILL_ILLTRP; | ||
250 | sig = SIGILL; | ||
251 | break; | ||
252 | case VEC_FPBRUC: | ||
253 | case VEC_FPOE: | ||
254 | case VEC_FPNAN: | ||
255 | info.si_code = FPE_FLTINV; | ||
256 | sig = SIGFPE; | ||
257 | break; | ||
258 | case VEC_FPIR: | ||
259 | info.si_code = FPE_FLTRES; | ||
260 | sig = SIGFPE; | ||
261 | break; | ||
262 | case VEC_FPDIVZ: | ||
263 | info.si_code = FPE_FLTDIV; | ||
264 | sig = SIGFPE; | ||
265 | break; | ||
266 | case VEC_FPUNDER: | ||
267 | info.si_code = FPE_FLTUND; | ||
268 | sig = SIGFPE; | ||
269 | break; | ||
270 | case VEC_FPOVER: | ||
271 | info.si_code = FPE_FLTOVF; | ||
272 | sig = SIGFPE; | ||
273 | break; | ||
274 | case VEC_ZERODIV: | ||
275 | info.si_code = FPE_INTDIV; | ||
276 | sig = SIGFPE; | ||
277 | break; | ||
278 | case VEC_CHK: | ||
279 | case VEC_TRAP: | ||
280 | info.si_code = FPE_INTOVF; | ||
281 | sig = SIGFPE; | ||
282 | break; | ||
283 | case VEC_TRACE: /* ptrace single step */ | ||
284 | info.si_code = TRAP_TRACE; | ||
285 | sig = SIGTRAP; | ||
286 | break; | ||
287 | case VEC_TRAP15: /* breakpoint */ | ||
288 | info.si_code = TRAP_BRKPT; | ||
289 | sig = SIGTRAP; | ||
290 | break; | ||
291 | default: | ||
292 | info.si_code = ILL_ILLOPC; | ||
293 | sig = SIGILL; | ||
294 | break; | ||
295 | } | ||
296 | info.si_signo = sig; | ||
297 | info.si_errno = 0; | ||
298 | switch (fp->ptregs.format) { | ||
299 | default: | ||
300 | info.si_addr = (void *) fp->ptregs.pc; | ||
301 | break; | ||
302 | case 2: | ||
303 | info.si_addr = (void *) fp->un.fmt2.iaddr; | ||
304 | break; | ||
305 | case 7: | ||
306 | info.si_addr = (void *) fp->un.fmt7.effaddr; | ||
307 | break; | ||
308 | case 9: | ||
309 | info.si_addr = (void *) fp->un.fmt9.iaddr; | ||
310 | break; | ||
311 | case 10: | ||
312 | info.si_addr = (void *) fp->un.fmta.daddr; | ||
313 | break; | ||
314 | case 11: | ||
315 | info.si_addr = (void *) fp->un.fmtb.daddr; | ||
316 | break; | ||
317 | } | ||
318 | force_sig_info (sig, &info, current); | ||
319 | } | ||
320 | |||
321 | asmlinkage void set_esp0(unsigned long ssp) | ||
322 | { | ||
323 | current->thread.esp0 = ssp; | ||
324 | } | ||
325 | |||
326 | /* | ||
327 | * The architecture-independent backtrace generator | ||
328 | */ | ||
329 | void dump_stack(void) | ||
330 | { | ||
331 | /* | ||
332 | * We need frame pointers for this little trick, which works as follows: | ||
333 | * | ||
334 | * +------------+ 0x00 | ||
335 | * | Next SP | -> 0x0c | ||
336 | * +------------+ 0x04 | ||
337 | * | Caller | | ||
338 | * +------------+ 0x08 | ||
339 | * | Local vars | -> our stack var | ||
340 | * +------------+ 0x0c | ||
341 | * | Next SP | -> 0x18, that is what we pass to show_stack() | ||
342 | * +------------+ 0x10 | ||
343 | * | Caller | | ||
344 | * +------------+ 0x14 | ||
345 | * | Local vars | | ||
346 | * +------------+ 0x18 | ||
347 | * | ... | | ||
348 | * +------------+ | ||
349 | */ | ||
350 | |||
351 | unsigned long *stack; | ||
352 | |||
353 | stack = (unsigned long *)&stack; | ||
354 | stack++; | ||
355 | __show_stack(current, stack); | ||
356 | } | ||
357 | EXPORT_SYMBOL(dump_stack); | ||
358 | |||
359 | void show_stack(struct task_struct *task, unsigned long *stack) | ||
360 | { | ||
361 | if (!stack && !task) | ||
362 | dump_stack(); | ||
363 | else | ||
364 | __show_stack(task, stack); | ||
365 | } | ||
diff --git a/arch/m68k/kernel/vmlinux.lds.S b/arch/m68k/kernel/vmlinux.lds.S index 99ba315bd0a8..030dabf0bc53 100644 --- a/arch/m68k/kernel/vmlinux.lds.S +++ b/arch/m68k/kernel/vmlinux.lds.S | |||
@@ -1,10 +1,5 @@ | |||
1 | PHDRS | 1 | #ifdef CONFIG_MMU |
2 | { | 2 | #include "vmlinux.lds_mm.S" |
3 | text PT_LOAD FILEHDR PHDRS FLAGS (7); | ||
4 | data PT_LOAD FLAGS (7); | ||
5 | } | ||
6 | #ifdef CONFIG_SUN3 | ||
7 | #include "vmlinux-sun3.lds" | ||
8 | #else | 3 | #else |
9 | #include "vmlinux-std.lds" | 4 | #include "vmlinux.lds_no.S" |
10 | #endif | 5 | #endif |
diff --git a/arch/m68k/kernel/vmlinux.lds_mm.S b/arch/m68k/kernel/vmlinux.lds_mm.S new file mode 100644 index 000000000000..99ba315bd0a8 --- /dev/null +++ b/arch/m68k/kernel/vmlinux.lds_mm.S | |||
@@ -0,0 +1,10 @@ | |||
1 | PHDRS | ||
2 | { | ||
3 | text PT_LOAD FILEHDR PHDRS FLAGS (7); | ||
4 | data PT_LOAD FLAGS (7); | ||
5 | } | ||
6 | #ifdef CONFIG_SUN3 | ||
7 | #include "vmlinux-sun3.lds" | ||
8 | #else | ||
9 | #include "vmlinux-std.lds" | ||
10 | #endif | ||
diff --git a/arch/m68k/kernel/vmlinux.lds_no.S b/arch/m68k/kernel/vmlinux.lds_no.S new file mode 100644 index 000000000000..47e15ebfd893 --- /dev/null +++ b/arch/m68k/kernel/vmlinux.lds_no.S | |||
@@ -0,0 +1,188 @@ | |||
1 | /* | ||
2 | * vmlinux.lds.S -- master linker script for m68knommu arch | ||
3 | * | ||
4 | * (C) Copyright 2002-2006, Greg Ungerer <gerg@snapgear.com> | ||
5 | * | ||
6 | * This linker script is equiped to build either ROM loaded or RAM | ||
7 | * run kernels. | ||
8 | */ | ||
9 | |||
10 | #include <asm-generic/vmlinux.lds.h> | ||
11 | #include <asm/page.h> | ||
12 | #include <asm/thread_info.h> | ||
13 | |||
14 | #if defined(CONFIG_RAMKERNEL) | ||
15 | #define RAM_START CONFIG_KERNELBASE | ||
16 | #define RAM_LENGTH (CONFIG_RAMBASE + CONFIG_RAMSIZE - CONFIG_KERNELBASE) | ||
17 | #define TEXT ram | ||
18 | #define DATA ram | ||
19 | #define INIT ram | ||
20 | #define BSSS ram | ||
21 | #endif | ||
22 | #if defined(CONFIG_ROMKERNEL) || defined(CONFIG_HIMEMKERNEL) | ||
23 | #define RAM_START CONFIG_RAMBASE | ||
24 | #define RAM_LENGTH CONFIG_RAMSIZE | ||
25 | #define ROMVEC_START CONFIG_ROMVEC | ||
26 | #define ROMVEC_LENGTH CONFIG_ROMVECSIZE | ||
27 | #define ROM_START CONFIG_ROMSTART | ||
28 | #define ROM_LENGTH CONFIG_ROMSIZE | ||
29 | #define TEXT rom | ||
30 | #define DATA ram | ||
31 | #define INIT ram | ||
32 | #define BSSS ram | ||
33 | #endif | ||
34 | |||
35 | #ifndef DATA_ADDR | ||
36 | #define DATA_ADDR | ||
37 | #endif | ||
38 | |||
39 | |||
40 | OUTPUT_ARCH(m68k) | ||
41 | ENTRY(_start) | ||
42 | |||
43 | MEMORY { | ||
44 | ram : ORIGIN = RAM_START, LENGTH = RAM_LENGTH | ||
45 | #ifdef ROM_START | ||
46 | romvec : ORIGIN = ROMVEC_START, LENGTH = ROMVEC_LENGTH | ||
47 | rom : ORIGIN = ROM_START, LENGTH = ROM_LENGTH | ||
48 | #endif | ||
49 | } | ||
50 | |||
51 | jiffies = jiffies_64 + 4; | ||
52 | |||
53 | SECTIONS { | ||
54 | |||
55 | #ifdef ROMVEC_START | ||
56 | . = ROMVEC_START ; | ||
57 | .romvec : { | ||
58 | __rom_start = . ; | ||
59 | _romvec = .; | ||
60 | *(.data..initvect) | ||
61 | } > romvec | ||
62 | #endif | ||
63 | |||
64 | .text : { | ||
65 | _text = .; | ||
66 | _stext = . ; | ||
67 | HEAD_TEXT | ||
68 | TEXT_TEXT | ||
69 | SCHED_TEXT | ||
70 | LOCK_TEXT | ||
71 | *(.text..lock) | ||
72 | |||
73 | . = ALIGN(16); /* Exception table */ | ||
74 | __start___ex_table = .; | ||
75 | *(__ex_table) | ||
76 | __stop___ex_table = .; | ||
77 | |||
78 | *(.rodata) *(.rodata.*) | ||
79 | *(__vermagic) /* Kernel version magic */ | ||
80 | *(__markers_strings) | ||
81 | *(.rodata1) | ||
82 | *(.rodata.str1.1) | ||
83 | |||
84 | /* Kernel symbol table: Normal symbols */ | ||
85 | . = ALIGN(4); | ||
86 | __start___ksymtab = .; | ||
87 | *(__ksymtab) | ||
88 | __stop___ksymtab = .; | ||
89 | |||
90 | /* Kernel symbol table: GPL-only symbols */ | ||
91 | __start___ksymtab_gpl = .; | ||
92 | *(__ksymtab_gpl) | ||
93 | __stop___ksymtab_gpl = .; | ||
94 | |||
95 | /* Kernel symbol table: Normal unused symbols */ | ||
96 | __start___ksymtab_unused = .; | ||
97 | *(__ksymtab_unused) | ||
98 | __stop___ksymtab_unused = .; | ||
99 | |||
100 | /* Kernel symbol table: GPL-only unused symbols */ | ||
101 | __start___ksymtab_unused_gpl = .; | ||
102 | *(__ksymtab_unused_gpl) | ||
103 | __stop___ksymtab_unused_gpl = .; | ||
104 | |||
105 | /* Kernel symbol table: GPL-future symbols */ | ||
106 | __start___ksymtab_gpl_future = .; | ||
107 | *(__ksymtab_gpl_future) | ||
108 | __stop___ksymtab_gpl_future = .; | ||
109 | |||
110 | /* Kernel symbol table: Normal symbols */ | ||
111 | __start___kcrctab = .; | ||
112 | *(__kcrctab) | ||
113 | __stop___kcrctab = .; | ||
114 | |||
115 | /* Kernel symbol table: GPL-only symbols */ | ||
116 | __start___kcrctab_gpl = .; | ||
117 | *(__kcrctab_gpl) | ||
118 | __stop___kcrctab_gpl = .; | ||
119 | |||
120 | /* Kernel symbol table: Normal unused symbols */ | ||
121 | __start___kcrctab_unused = .; | ||
122 | *(__kcrctab_unused) | ||
123 | __stop___kcrctab_unused = .; | ||
124 | |||
125 | /* Kernel symbol table: GPL-only unused symbols */ | ||
126 | __start___kcrctab_unused_gpl = .; | ||
127 | *(__kcrctab_unused_gpl) | ||
128 | __stop___kcrctab_unused_gpl = .; | ||
129 | |||
130 | /* Kernel symbol table: GPL-future symbols */ | ||
131 | __start___kcrctab_gpl_future = .; | ||
132 | *(__kcrctab_gpl_future) | ||
133 | __stop___kcrctab_gpl_future = .; | ||
134 | |||
135 | /* Kernel symbol table: strings */ | ||
136 | *(__ksymtab_strings) | ||
137 | |||
138 | /* Built-in module parameters */ | ||
139 | . = ALIGN(4) ; | ||
140 | __start___param = .; | ||
141 | *(__param) | ||
142 | __stop___param = .; | ||
143 | |||
144 | /* Built-in module versions */ | ||
145 | . = ALIGN(4) ; | ||
146 | __start___modver = .; | ||
147 | *(__modver) | ||
148 | __stop___modver = .; | ||
149 | |||
150 | . = ALIGN(4) ; | ||
151 | _etext = . ; | ||
152 | } > TEXT | ||
153 | |||
154 | .data DATA_ADDR : { | ||
155 | . = ALIGN(4); | ||
156 | _sdata = . ; | ||
157 | DATA_DATA | ||
158 | CACHELINE_ALIGNED_DATA(32) | ||
159 | PAGE_ALIGNED_DATA(PAGE_SIZE) | ||
160 | *(.data..shared_aligned) | ||
161 | INIT_TASK_DATA(THREAD_SIZE) | ||
162 | _edata = . ; | ||
163 | } > DATA | ||
164 | |||
165 | .init.text : { | ||
166 | . = ALIGN(PAGE_SIZE); | ||
167 | __init_begin = .; | ||
168 | } > INIT | ||
169 | INIT_TEXT_SECTION(PAGE_SIZE) > INIT | ||
170 | INIT_DATA_SECTION(16) > INIT | ||
171 | .init.data : { | ||
172 | . = ALIGN(PAGE_SIZE); | ||
173 | __init_end = .; | ||
174 | } > INIT | ||
175 | |||
176 | .bss : { | ||
177 | . = ALIGN(4); | ||
178 | _sbss = . ; | ||
179 | *(.bss) | ||
180 | *(COMMON) | ||
181 | . = ALIGN(4) ; | ||
182 | _ebss = . ; | ||
183 | _end = . ; | ||
184 | } > BSSS | ||
185 | |||
186 | DISCARDS | ||
187 | } | ||
188 | |||