diff options
Diffstat (limited to 'arch/sparc/kernel')
-rw-r--r-- | arch/sparc/kernel/Makefile | 1 | ||||
-rw-r--r-- | arch/sparc/kernel/asm-offsets.c | 6 | ||||
-rw-r--r-- | arch/sparc/kernel/entry.S | 127 | ||||
-rw-r--r-- | arch/sparc/kernel/head.S | 9 | ||||
-rw-r--r-- | arch/sparc/kernel/kgdb.c | 164 | ||||
-rw-r--r-- | arch/sparc/kernel/process.c | 25 | ||||
-rw-r--r-- | arch/sparc/kernel/ptrace.c | 6 | ||||
-rw-r--r-- | arch/sparc/kernel/rtrap.S | 11 | ||||
-rw-r--r-- | arch/sparc/kernel/setup.c | 5 | ||||
-rw-r--r-- | arch/sparc/kernel/signal.c | 84 | ||||
-rw-r--r-- | arch/sparc/kernel/sparc-stub.c | 724 | ||||
-rw-r--r-- | arch/sparc/kernel/sparc_ksyms.c | 2 | ||||
-rw-r--r-- | arch/sparc/kernel/sun4d_smp.c | 32 | ||||
-rw-r--r-- | arch/sparc/kernel/sun4m_smp.c | 45 | ||||
-rw-r--r-- | arch/sparc/kernel/sys_sparc.c | 51 |
15 files changed, 299 insertions, 993 deletions
diff --git a/arch/sparc/kernel/Makefile b/arch/sparc/kernel/Makefile index 59700aaaae93..6e03a2a7863c 100644 --- a/arch/sparc/kernel/Makefile +++ b/arch/sparc/kernel/Makefile | |||
@@ -25,3 +25,4 @@ obj-$(CONFIG_PCI) += ebus.o | |||
25 | obj-$(CONFIG_SUN_PM) += apc.o pmc.o | 25 | obj-$(CONFIG_SUN_PM) += apc.o pmc.o |
26 | obj-$(CONFIG_MODULES) += module.o sparc_ksyms.o | 26 | obj-$(CONFIG_MODULES) += module.o sparc_ksyms.o |
27 | obj-$(CONFIG_SPARC_LED) += led.o | 27 | obj-$(CONFIG_SPARC_LED) += led.o |
28 | obj-$(CONFIG_KGDB) += kgdb.o | ||
diff --git a/arch/sparc/kernel/asm-offsets.c b/arch/sparc/kernel/asm-offsets.c index 6773ed76e414..cd3f7694e9b9 100644 --- a/arch/sparc/kernel/asm-offsets.c +++ b/arch/sparc/kernel/asm-offsets.c | |||
@@ -12,11 +12,7 @@ | |||
12 | 12 | ||
13 | #include <linux/sched.h> | 13 | #include <linux/sched.h> |
14 | // #include <linux/mm.h> | 14 | // #include <linux/mm.h> |
15 | 15 | #include <linux/kbuild.h> | |
16 | #define DEFINE(sym, val) \ | ||
17 | asm volatile("\n->" #sym " %0 " #val : : "i" (val)) | ||
18 | |||
19 | #define BLANK() asm volatile("\n->" : : ) | ||
20 | 16 | ||
21 | int foo(void) | 17 | int foo(void) |
22 | { | 18 | { |
diff --git a/arch/sparc/kernel/entry.S b/arch/sparc/kernel/entry.S index 484c83d23eef..4bcfe54f878d 100644 --- a/arch/sparc/kernel/entry.S +++ b/arch/sparc/kernel/entry.S | |||
@@ -12,7 +12,6 @@ | |||
12 | #include <asm/head.h> | 12 | #include <asm/head.h> |
13 | #include <asm/asi.h> | 13 | #include <asm/asi.h> |
14 | #include <asm/smp.h> | 14 | #include <asm/smp.h> |
15 | #include <asm/kgdb.h> | ||
16 | #include <asm/contregs.h> | 15 | #include <asm/contregs.h> |
17 | #include <asm/ptrace.h> | 16 | #include <asm/ptrace.h> |
18 | #include <asm/asm-offsets.h> | 17 | #include <asm/asm-offsets.h> |
@@ -45,91 +44,20 @@ | |||
45 | _SV; _SV; _SV; _SV; _SV; _SV; _SV; \ | 44 | _SV; _SV; _SV; _SV; _SV; _SV; _SV; \ |
46 | _RS; _RS; _RS; _RS; _RS; _RS; _RS; | 45 | _RS; _RS; _RS; _RS; _RS; _RS; _RS; |
47 | 46 | ||
48 | /* First, KGDB low level things. This is a rewrite | ||
49 | * of the routines found in the sparc-stub.c asm() statement | ||
50 | * from the gdb distribution. This is also dual-purpose | ||
51 | * as a software trap for userlevel programs. | ||
52 | */ | ||
53 | .data | ||
54 | .align 4 | ||
55 | |||
56 | in_trap_handler: | ||
57 | .word 0 | ||
58 | |||
59 | .text | 47 | .text |
60 | .align 4 | ||
61 | 48 | ||
62 | #if 0 /* kgdb is dropped from 2.5.33 */ | 49 | #ifdef CONFIG_KGDB |
63 | ! This function is called when any SPARC trap (except window overflow or | 50 | .align 4 |
64 | ! underflow) occurs. It makes sure that the invalid register window is still | 51 | .globl arch_kgdb_breakpoint |
65 | ! available before jumping into C code. It will also restore the world if you | 52 | .type arch_kgdb_breakpoint,#function |
66 | ! return from handle_exception. | 53 | arch_kgdb_breakpoint: |
67 | 54 | ta 0x7d | |
68 | .globl trap_low | 55 | retl |
69 | trap_low: | 56 | nop |
70 | rd %wim, %l3 | 57 | .size arch_kgdb_breakpoint,.-arch_kgdb_breakpoint |
71 | SAVE_ALL | ||
72 | |||
73 | sethi %hi(in_trap_handler), %l4 | ||
74 | ld [%lo(in_trap_handler) + %l4], %l5 | ||
75 | inc %l5 | ||
76 | st %l5, [%lo(in_trap_handler) + %l4] | ||
77 | |||
78 | /* Make sure kgdb sees the same state we just saved. */ | ||
79 | LOAD_PT_GLOBALS(sp) | ||
80 | LOAD_PT_INS(sp) | ||
81 | ld [%sp + STACKFRAME_SZ + PT_Y], %l4 | ||
82 | ld [%sp + STACKFRAME_SZ + PT_WIM], %l3 | ||
83 | ld [%sp + STACKFRAME_SZ + PT_PSR], %l0 | ||
84 | ld [%sp + STACKFRAME_SZ + PT_PC], %l1 | ||
85 | ld [%sp + STACKFRAME_SZ + PT_NPC], %l2 | ||
86 | rd %tbr, %l5 /* Never changes... */ | ||
87 | |||
88 | /* Make kgdb exception frame. */ | ||
89 | sub %sp,(16+1+6+1+72)*4,%sp ! Make room for input & locals | ||
90 | ! + hidden arg + arg spill | ||
91 | ! + doubleword alignment | ||
92 | ! + registers[72] local var | ||
93 | SAVE_KGDB_GLOBALS(sp) | ||
94 | SAVE_KGDB_INS(sp) | ||
95 | SAVE_KGDB_SREGS(sp, l4, l0, l3, l5, l1, l2) | ||
96 | |||
97 | /* We are increasing PIL, so two writes. */ | ||
98 | or %l0, PSR_PIL, %l0 | ||
99 | wr %l0, 0, %psr | ||
100 | WRITE_PAUSE | ||
101 | wr %l0, PSR_ET, %psr | ||
102 | WRITE_PAUSE | ||
103 | |||
104 | call handle_exception | ||
105 | add %sp, STACKFRAME_SZ, %o0 ! Pass address of registers | ||
106 | |||
107 | /* Load new kgdb register set. */ | ||
108 | LOAD_KGDB_GLOBALS(sp) | ||
109 | LOAD_KGDB_INS(sp) | ||
110 | LOAD_KGDB_SREGS(sp, l4, l0, l3, l5, l1, l2) | ||
111 | wr %l4, 0x0, %y | ||
112 | |||
113 | sethi %hi(in_trap_handler), %l4 | ||
114 | ld [%lo(in_trap_handler) + %l4], %l5 | ||
115 | dec %l5 | ||
116 | st %l5, [%lo(in_trap_handler) + %l4] | ||
117 | |||
118 | add %sp,(16+1+6+1+72)*4,%sp ! Undo the kgdb trap frame. | ||
119 | |||
120 | /* Now take what kgdb did and place it into the pt_regs | ||
121 | * frame which SparcLinux RESTORE_ALL understands., | ||
122 | */ | ||
123 | STORE_PT_INS(sp) | ||
124 | STORE_PT_GLOBALS(sp) | ||
125 | STORE_PT_YREG(sp, g2) | ||
126 | STORE_PT_PRIV(sp, l0, l1, l2) | ||
127 | |||
128 | RESTORE_ALL | ||
129 | #endif | 58 | #endif |
130 | 59 | ||
131 | #if defined(CONFIG_BLK_DEV_FD) || defined(CONFIG_BLK_DEV_FD_MODULE) | 60 | #if defined(CONFIG_BLK_DEV_FD) || defined(CONFIG_BLK_DEV_FD_MODULE) |
132 | .text | ||
133 | .align 4 | 61 | .align 4 |
134 | .globl floppy_hardint | 62 | .globl floppy_hardint |
135 | floppy_hardint: | 63 | floppy_hardint: |
@@ -1378,6 +1306,8 @@ ret_from_fork: | |||
1378 | .align 4 | 1306 | .align 4 |
1379 | .globl linux_sparc_syscall | 1307 | .globl linux_sparc_syscall |
1380 | linux_sparc_syscall: | 1308 | linux_sparc_syscall: |
1309 | sethi %hi(PSR_SYSCALL), %l4 | ||
1310 | or %l0, %l4, %l0 | ||
1381 | /* Direct access to user regs, must faster. */ | 1311 | /* Direct access to user regs, must faster. */ |
1382 | cmp %g1, NR_SYSCALLS | 1312 | cmp %g1, NR_SYSCALLS |
1383 | bgeu linux_sparc_ni_syscall | 1313 | bgeu linux_sparc_ni_syscall |
@@ -1596,6 +1526,23 @@ breakpoint_trap: | |||
1596 | 1526 | ||
1597 | RESTORE_ALL | 1527 | RESTORE_ALL |
1598 | 1528 | ||
1529 | #ifdef CONFIG_KGDB | ||
1530 | .align 4 | ||
1531 | .globl kgdb_trap_low | ||
1532 | .type kgdb_trap_low,#function | ||
1533 | kgdb_trap_low: | ||
1534 | rd %wim,%l3 | ||
1535 | SAVE_ALL | ||
1536 | wr %l0, PSR_ET, %psr | ||
1537 | WRITE_PAUSE | ||
1538 | |||
1539 | call kgdb_trap | ||
1540 | add %sp, STACKFRAME_SZ, %o0 | ||
1541 | |||
1542 | RESTORE_ALL | ||
1543 | .size kgdb_trap_low,.-kgdb_trap_low | ||
1544 | #endif | ||
1545 | |||
1599 | .align 4 | 1546 | .align 4 |
1600 | .globl __handle_exception, flush_patch_exception | 1547 | .globl __handle_exception, flush_patch_exception |
1601 | __handle_exception: | 1548 | __handle_exception: |
@@ -1698,4 +1645,22 @@ pcic_nmi_trap_patch: | |||
1698 | 1645 | ||
1699 | #endif /* CONFIG_PCI */ | 1646 | #endif /* CONFIG_PCI */ |
1700 | 1647 | ||
1648 | .globl flushw_all | ||
1649 | flushw_all: | ||
1650 | save %sp, -0x40, %sp | ||
1651 | save %sp, -0x40, %sp | ||
1652 | save %sp, -0x40, %sp | ||
1653 | save %sp, -0x40, %sp | ||
1654 | save %sp, -0x40, %sp | ||
1655 | save %sp, -0x40, %sp | ||
1656 | save %sp, -0x40, %sp | ||
1657 | restore | ||
1658 | restore | ||
1659 | restore | ||
1660 | restore | ||
1661 | restore | ||
1662 | restore | ||
1663 | ret | ||
1664 | restore | ||
1665 | |||
1701 | /* End of entry.S */ | 1666 | /* End of entry.S */ |
diff --git a/arch/sparc/kernel/head.S b/arch/sparc/kernel/head.S index b7f1e81c8ff2..8bec05fa5795 100644 --- a/arch/sparc/kernel/head.S +++ b/arch/sparc/kernel/head.S | |||
@@ -191,7 +191,8 @@ t_bade8:BAD_TRAP(0xe8) BAD_TRAP(0xe9) BAD_TRAP(0xea) BAD_TRAP(0xeb) BAD_TRAP(0xe | |||
191 | t_baded:BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1) | 191 | t_baded:BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1) |
192 | t_badf2:BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6) | 192 | t_badf2:BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6) |
193 | t_badf7:BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb) | 193 | t_badf7:BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb) |
194 | t_badfc:BAD_TRAP(0xfc) BAD_TRAP(0xfd) | 194 | t_badfc:BAD_TRAP(0xfc) |
195 | t_kgdb: KGDB_TRAP(0xfd) | ||
195 | dbtrap: BAD_TRAP(0xfe) /* Debugger/PROM breakpoint #1 */ | 196 | dbtrap: BAD_TRAP(0xfe) /* Debugger/PROM breakpoint #1 */ |
196 | dbtrap2:BAD_TRAP(0xff) /* Debugger/PROM breakpoint #2 */ | 197 | dbtrap2:BAD_TRAP(0xff) /* Debugger/PROM breakpoint #2 */ |
197 | 198 | ||
@@ -267,7 +268,7 @@ trapbase_cpu1: | |||
267 | BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1) | 268 | BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1) |
268 | BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6) | 269 | BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6) |
269 | BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb) | 270 | BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb) |
270 | BAD_TRAP(0xfc) BAD_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff) | 271 | BAD_TRAP(0xfc) KGDB_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff) |
271 | 272 | ||
272 | trapbase_cpu2: | 273 | trapbase_cpu2: |
273 | BAD_TRAP(0x0) SRMMU_TFAULT TRAP_ENTRY(0x2, bad_instruction) | 274 | BAD_TRAP(0x0) SRMMU_TFAULT TRAP_ENTRY(0x2, bad_instruction) |
@@ -335,7 +336,7 @@ trapbase_cpu2: | |||
335 | BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1) | 336 | BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1) |
336 | BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6) | 337 | BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6) |
337 | BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb) | 338 | BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb) |
338 | BAD_TRAP(0xfc) BAD_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff) | 339 | BAD_TRAP(0xfc) KGDB_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff) |
339 | 340 | ||
340 | trapbase_cpu3: | 341 | trapbase_cpu3: |
341 | BAD_TRAP(0x0) SRMMU_TFAULT TRAP_ENTRY(0x2, bad_instruction) | 342 | BAD_TRAP(0x0) SRMMU_TFAULT TRAP_ENTRY(0x2, bad_instruction) |
@@ -403,7 +404,7 @@ trapbase_cpu3: | |||
403 | BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1) | 404 | BAD_TRAP(0xed) BAD_TRAP(0xee) BAD_TRAP(0xef) BAD_TRAP(0xf0) BAD_TRAP(0xf1) |
404 | BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6) | 405 | BAD_TRAP(0xf2) BAD_TRAP(0xf3) BAD_TRAP(0xf4) BAD_TRAP(0xf5) BAD_TRAP(0xf6) |
405 | BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb) | 406 | BAD_TRAP(0xf7) BAD_TRAP(0xf8) BAD_TRAP(0xf9) BAD_TRAP(0xfa) BAD_TRAP(0xfb) |
406 | BAD_TRAP(0xfc) BAD_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff) | 407 | BAD_TRAP(0xfc) KGDB_TRAP(0xfd) BAD_TRAP(0xfe) BAD_TRAP(0xff) |
407 | 408 | ||
408 | #endif | 409 | #endif |
409 | .align PAGE_SIZE | 410 | .align PAGE_SIZE |
diff --git a/arch/sparc/kernel/kgdb.c b/arch/sparc/kernel/kgdb.c new file mode 100644 index 000000000000..757805ce02ee --- /dev/null +++ b/arch/sparc/kernel/kgdb.c | |||
@@ -0,0 +1,164 @@ | |||
1 | /* kgdb.c: KGDB support for 32-bit sparc. | ||
2 | * | ||
3 | * Copyright (C) 2008 David S. Miller <davem@davemloft.net> | ||
4 | */ | ||
5 | |||
6 | #include <linux/kgdb.h> | ||
7 | #include <linux/kdebug.h> | ||
8 | |||
9 | #include <asm/kdebug.h> | ||
10 | #include <asm/ptrace.h> | ||
11 | #include <asm/irq.h> | ||
12 | |||
13 | extern unsigned long trapbase; | ||
14 | |||
15 | void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs) | ||
16 | { | ||
17 | struct reg_window *win; | ||
18 | int i; | ||
19 | |||
20 | gdb_regs[GDB_G0] = 0; | ||
21 | for (i = 0; i < 15; i++) | ||
22 | gdb_regs[GDB_G1 + i] = regs->u_regs[UREG_G1 + i]; | ||
23 | |||
24 | win = (struct reg_window *) regs->u_regs[UREG_FP]; | ||
25 | for (i = 0; i < 8; i++) | ||
26 | gdb_regs[GDB_L0 + i] = win->locals[i]; | ||
27 | for (i = 0; i < 8; i++) | ||
28 | gdb_regs[GDB_I0 + i] = win->ins[i]; | ||
29 | |||
30 | for (i = GDB_F0; i <= GDB_F31; i++) | ||
31 | gdb_regs[i] = 0; | ||
32 | |||
33 | gdb_regs[GDB_Y] = regs->y; | ||
34 | gdb_regs[GDB_PSR] = regs->psr; | ||
35 | gdb_regs[GDB_WIM] = 0; | ||
36 | gdb_regs[GDB_TBR] = (unsigned long) &trapbase; | ||
37 | gdb_regs[GDB_PC] = regs->pc; | ||
38 | gdb_regs[GDB_NPC] = regs->npc; | ||
39 | gdb_regs[GDB_FSR] = 0; | ||
40 | gdb_regs[GDB_CSR] = 0; | ||
41 | } | ||
42 | |||
43 | void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p) | ||
44 | { | ||
45 | struct thread_info *t = task_thread_info(p); | ||
46 | struct reg_window *win; | ||
47 | int i; | ||
48 | |||
49 | for (i = GDB_G0; i < GDB_G6; i++) | ||
50 | gdb_regs[i] = 0; | ||
51 | gdb_regs[GDB_G6] = (unsigned long) t; | ||
52 | gdb_regs[GDB_G7] = 0; | ||
53 | for (i = GDB_O0; i < GDB_SP; i++) | ||
54 | gdb_regs[i] = 0; | ||
55 | gdb_regs[GDB_SP] = t->ksp; | ||
56 | gdb_regs[GDB_O7] = 0; | ||
57 | |||
58 | win = (struct reg_window *) t->ksp; | ||
59 | for (i = 0; i < 8; i++) | ||
60 | gdb_regs[GDB_L0 + i] = win->locals[i]; | ||
61 | for (i = 0; i < 8; i++) | ||
62 | gdb_regs[GDB_I0 + i] = win->ins[i]; | ||
63 | |||
64 | for (i = GDB_F0; i <= GDB_F31; i++) | ||
65 | gdb_regs[i] = 0; | ||
66 | |||
67 | gdb_regs[GDB_Y] = 0; | ||
68 | |||
69 | gdb_regs[GDB_PSR] = t->kpsr; | ||
70 | gdb_regs[GDB_WIM] = t->kwim; | ||
71 | gdb_regs[GDB_TBR] = (unsigned long) &trapbase; | ||
72 | gdb_regs[GDB_PC] = t->kpc; | ||
73 | gdb_regs[GDB_NPC] = t->kpc + 4; | ||
74 | gdb_regs[GDB_FSR] = 0; | ||
75 | gdb_regs[GDB_CSR] = 0; | ||
76 | } | ||
77 | |||
78 | void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs) | ||
79 | { | ||
80 | struct reg_window *win; | ||
81 | int i; | ||
82 | |||
83 | for (i = 0; i < 15; i++) | ||
84 | regs->u_regs[UREG_G1 + i] = gdb_regs[GDB_G1 + i]; | ||
85 | |||
86 | /* If the PSR register is changing, we have to preserve | ||
87 | * the CWP field, otherwise window save/restore explodes. | ||
88 | */ | ||
89 | if (regs->psr != gdb_regs[GDB_PSR]) { | ||
90 | unsigned long cwp = regs->psr & PSR_CWP; | ||
91 | |||
92 | regs->psr = (gdb_regs[GDB_PSR] & ~PSR_CWP) | cwp; | ||
93 | } | ||
94 | |||
95 | regs->pc = gdb_regs[GDB_PC]; | ||
96 | regs->npc = gdb_regs[GDB_NPC]; | ||
97 | regs->y = gdb_regs[GDB_Y]; | ||
98 | |||
99 | win = (struct reg_window *) regs->u_regs[UREG_FP]; | ||
100 | for (i = 0; i < 8; i++) | ||
101 | win->locals[i] = gdb_regs[GDB_L0 + i]; | ||
102 | for (i = 0; i < 8; i++) | ||
103 | win->ins[i] = gdb_regs[GDB_I0 + i]; | ||
104 | } | ||
105 | |||
106 | int kgdb_arch_handle_exception(int e_vector, int signo, int err_code, | ||
107 | char *remcomInBuffer, char *remcomOutBuffer, | ||
108 | struct pt_regs *linux_regs) | ||
109 | { | ||
110 | unsigned long addr; | ||
111 | char *ptr; | ||
112 | |||
113 | switch (remcomInBuffer[0]) { | ||
114 | case 'c': | ||
115 | /* try to read optional parameter, pc unchanged if no parm */ | ||
116 | ptr = &remcomInBuffer[1]; | ||
117 | if (kgdb_hex2long(&ptr, &addr)) { | ||
118 | linux_regs->pc = addr; | ||
119 | linux_regs->npc = addr + 4; | ||
120 | } | ||
121 | /* fallthru */ | ||
122 | |||
123 | case 'D': | ||
124 | case 'k': | ||
125 | if (linux_regs->pc == (unsigned long) arch_kgdb_breakpoint) { | ||
126 | linux_regs->pc = linux_regs->npc; | ||
127 | linux_regs->npc += 4; | ||
128 | } | ||
129 | return 0; | ||
130 | } | ||
131 | return -1; | ||
132 | } | ||
133 | |||
134 | extern void do_hw_interrupt(struct pt_regs *regs, unsigned long type); | ||
135 | |||
136 | asmlinkage void kgdb_trap(struct pt_regs *regs) | ||
137 | { | ||
138 | unsigned long flags; | ||
139 | |||
140 | if (user_mode(regs)) { | ||
141 | do_hw_interrupt(regs, 0xfd); | ||
142 | return; | ||
143 | } | ||
144 | |||
145 | flushw_all(); | ||
146 | |||
147 | local_irq_save(flags); | ||
148 | kgdb_handle_exception(0x172, SIGTRAP, 0, regs); | ||
149 | local_irq_restore(flags); | ||
150 | } | ||
151 | |||
152 | int kgdb_arch_init(void) | ||
153 | { | ||
154 | return 0; | ||
155 | } | ||
156 | |||
157 | void kgdb_arch_exit(void) | ||
158 | { | ||
159 | } | ||
160 | |||
161 | struct kgdb_arch arch_kgdb_ops = { | ||
162 | /* Breakpoint instruction: ta 0x7d */ | ||
163 | .gdb_bpt_instr = { 0x91, 0xd0, 0x20, 0x7d }, | ||
164 | }; | ||
diff --git a/arch/sparc/kernel/process.c b/arch/sparc/kernel/process.c index e7f35198ae34..da48d248cc17 100644 --- a/arch/sparc/kernel/process.c +++ b/arch/sparc/kernel/process.c | |||
@@ -419,14 +419,26 @@ asmlinkage int sparc_do_fork(unsigned long clone_flags, | |||
419 | unsigned long stack_size) | 419 | unsigned long stack_size) |
420 | { | 420 | { |
421 | unsigned long parent_tid_ptr, child_tid_ptr; | 421 | unsigned long parent_tid_ptr, child_tid_ptr; |
422 | unsigned long orig_i1 = regs->u_regs[UREG_I1]; | ||
423 | long ret; | ||
422 | 424 | ||
423 | parent_tid_ptr = regs->u_regs[UREG_I2]; | 425 | parent_tid_ptr = regs->u_regs[UREG_I2]; |
424 | child_tid_ptr = regs->u_regs[UREG_I4]; | 426 | child_tid_ptr = regs->u_regs[UREG_I4]; |
425 | 427 | ||
426 | return do_fork(clone_flags, stack_start, | 428 | ret = do_fork(clone_flags, stack_start, |
427 | regs, stack_size, | 429 | regs, stack_size, |
428 | (int __user *) parent_tid_ptr, | 430 | (int __user *) parent_tid_ptr, |
429 | (int __user *) child_tid_ptr); | 431 | (int __user *) child_tid_ptr); |
432 | |||
433 | /* If we get an error and potentially restart the system | ||
434 | * call, we're screwed because copy_thread() clobbered | ||
435 | * the parent's %o1. So detect that case and restore it | ||
436 | * here. | ||
437 | */ | ||
438 | if ((unsigned long)ret >= -ERESTART_RESTARTBLOCK) | ||
439 | regs->u_regs[UREG_I1] = orig_i1; | ||
440 | |||
441 | return ret; | ||
430 | } | 442 | } |
431 | 443 | ||
432 | /* Copy a Sparc thread. The fork() return value conventions | 444 | /* Copy a Sparc thread. The fork() return value conventions |
@@ -626,11 +638,6 @@ asmlinkage int sparc_execve(struct pt_regs *regs) | |||
626 | (char __user * __user *)regs->u_regs[base + UREG_I2], | 638 | (char __user * __user *)regs->u_regs[base + UREG_I2], |
627 | regs); | 639 | regs); |
628 | putname(filename); | 640 | putname(filename); |
629 | if (error == 0) { | ||
630 | task_lock(current); | ||
631 | current->ptrace &= ~PT_DTRACE; | ||
632 | task_unlock(current); | ||
633 | } | ||
634 | out: | 641 | out: |
635 | return error; | 642 | return error; |
636 | } | 643 | } |
diff --git a/arch/sparc/kernel/ptrace.c b/arch/sparc/kernel/ptrace.c index 7f44ae69b29e..81f3b929743f 100644 --- a/arch/sparc/kernel/ptrace.c +++ b/arch/sparc/kernel/ptrace.c | |||
@@ -170,8 +170,8 @@ static int genregs32_set(struct task_struct *target, | |||
170 | switch (pos) { | 170 | switch (pos) { |
171 | case 32: /* PSR */ | 171 | case 32: /* PSR */ |
172 | psr = regs->psr; | 172 | psr = regs->psr; |
173 | psr &= ~PSR_ICC; | 173 | psr &= ~(PSR_ICC | PSR_SYSCALL); |
174 | psr |= (reg & PSR_ICC); | 174 | psr |= (reg & (PSR_ICC | PSR_SYSCALL)); |
175 | regs->psr = psr; | 175 | regs->psr = psr; |
176 | break; | 176 | break; |
177 | case 33: /* PC */ | 177 | case 33: /* PC */ |
@@ -441,6 +441,8 @@ long arch_ptrace(struct task_struct *child, long request, long addr, long data) | |||
441 | break; | 441 | break; |
442 | 442 | ||
443 | default: | 443 | default: |
444 | if (request == PTRACE_SPARC_DETACH) | ||
445 | request = PTRACE_DETACH; | ||
444 | ret = ptrace_request(child, request, addr, data); | 446 | ret = ptrace_request(child, request, addr, data); |
445 | break; | 447 | break; |
446 | } | 448 | } |
diff --git a/arch/sparc/kernel/rtrap.S b/arch/sparc/kernel/rtrap.S index 77ca6fd81253..ab818cdc4cc0 100644 --- a/arch/sparc/kernel/rtrap.S +++ b/arch/sparc/kernel/rtrap.S | |||
@@ -50,8 +50,9 @@ rtrap_7win_patch5: and %g1, 0x7f, %g1 | |||
50 | ret_trap_entry: | 50 | ret_trap_entry: |
51 | ret_trap_lockless_ipi: | 51 | ret_trap_lockless_ipi: |
52 | andcc %t_psr, PSR_PS, %g0 | 52 | andcc %t_psr, PSR_PS, %g0 |
53 | sethi %hi(PSR_SYSCALL), %g1 | ||
53 | be 1f | 54 | be 1f |
54 | nop | 55 | andn %t_psr, %g1, %t_psr |
55 | 56 | ||
56 | wr %t_psr, 0x0, %psr | 57 | wr %t_psr, 0x0, %psr |
57 | b ret_trap_kernel | 58 | b ret_trap_kernel |
@@ -73,7 +74,6 @@ signal_p: | |||
73 | ld [%sp + STACKFRAME_SZ + PT_PSR], %t_psr | 74 | ld [%sp + STACKFRAME_SZ + PT_PSR], %t_psr |
74 | 75 | ||
75 | mov %l5, %o1 | 76 | mov %l5, %o1 |
76 | mov %l6, %o2 | ||
77 | call do_signal | 77 | call do_signal |
78 | add %sp, STACKFRAME_SZ, %o0 ! pt_regs ptr | 78 | add %sp, STACKFRAME_SZ, %o0 ! pt_regs ptr |
79 | 79 | ||
@@ -81,6 +81,8 @@ signal_p: | |||
81 | ld [%sp + STACKFRAME_SZ + PT_PSR], %t_psr | 81 | ld [%sp + STACKFRAME_SZ + PT_PSR], %t_psr |
82 | clr %l6 | 82 | clr %l6 |
83 | ret_trap_continue: | 83 | ret_trap_continue: |
84 | sethi %hi(PSR_SYSCALL), %g1 | ||
85 | andn %t_psr, %g1, %t_psr | ||
84 | wr %t_psr, 0x0, %psr | 86 | wr %t_psr, 0x0, %psr |
85 | WRITE_PAUSE | 87 | WRITE_PAUSE |
86 | 88 | ||
@@ -137,8 +139,9 @@ ret_trap_userwins_ok: | |||
137 | LOAD_PT_PRIV(sp, t_psr, t_pc, t_npc) | 139 | LOAD_PT_PRIV(sp, t_psr, t_pc, t_npc) |
138 | or %t_pc, %t_npc, %g2 | 140 | or %t_pc, %t_npc, %g2 |
139 | andcc %g2, 0x3, %g0 | 141 | andcc %g2, 0x3, %g0 |
142 | sethi %hi(PSR_SYSCALL), %g2 | ||
140 | be 1f | 143 | be 1f |
141 | nop | 144 | andn %t_psr, %g2, %t_psr |
142 | 145 | ||
143 | b ret_trap_unaligned_pc | 146 | b ret_trap_unaligned_pc |
144 | add %sp, STACKFRAME_SZ, %o0 | 147 | add %sp, STACKFRAME_SZ, %o0 |
@@ -201,6 +204,8 @@ rtrap_patch5: and %g1, 0xff, %g1 | |||
201 | 1: | 204 | 1: |
202 | LOAD_PT_ALL(sp, t_psr, t_pc, t_npc, g1) | 205 | LOAD_PT_ALL(sp, t_psr, t_pc, t_npc, g1) |
203 | 2: | 206 | 2: |
207 | sethi %hi(PSR_SYSCALL), %twin_tmp1 | ||
208 | andn %t_psr, %twin_tmp1, %t_psr | ||
204 | wr %t_psr, 0x0, %psr | 209 | wr %t_psr, 0x0, %psr |
205 | WRITE_PAUSE | 210 | WRITE_PAUSE |
206 | 211 | ||
diff --git a/arch/sparc/kernel/setup.c b/arch/sparc/kernel/setup.c index 3c13137685da..8a55c4f0df84 100644 --- a/arch/sparc/kernel/setup.c +++ b/arch/sparc/kernel/setup.c | |||
@@ -180,11 +180,9 @@ static void __init boot_flags_init(char *commands) | |||
180 | 180 | ||
181 | /* This routine will in the future do all the nasty prom stuff | 181 | /* This routine will in the future do all the nasty prom stuff |
182 | * to probe for the mmu type and its parameters, etc. This will | 182 | * to probe for the mmu type and its parameters, etc. This will |
183 | * also be where SMP things happen plus the Sparc specific memory | 183 | * also be where SMP things happen. |
184 | * physical memory probe as on the alpha. | ||
185 | */ | 184 | */ |
186 | 185 | ||
187 | extern int prom_probe_memory(void); | ||
188 | extern void sun4c_probe_vac(void); | 186 | extern void sun4c_probe_vac(void); |
189 | extern char cputypval; | 187 | extern char cputypval; |
190 | extern unsigned long start, end; | 188 | extern unsigned long start, end; |
@@ -268,7 +266,6 @@ void __init setup_arch(char **cmdline_p) | |||
268 | if (ARCH_SUN4C_SUN4) | 266 | if (ARCH_SUN4C_SUN4) |
269 | sun4c_probe_vac(); | 267 | sun4c_probe_vac(); |
270 | load_mmu(); | 268 | load_mmu(); |
271 | (void) prom_probe_memory(); | ||
272 | 269 | ||
273 | phys_base = 0xffffffffUL; | 270 | phys_base = 0xffffffffUL; |
274 | highest_paddr = 0UL; | 271 | highest_paddr = 0UL; |
diff --git a/arch/sparc/kernel/signal.c b/arch/sparc/kernel/signal.c index 3c312290c3c2..3fd1df9f9ba7 100644 --- a/arch/sparc/kernel/signal.c +++ b/arch/sparc/kernel/signal.c | |||
@@ -145,6 +145,9 @@ asmlinkage void do_sigreturn(struct pt_regs *regs) | |||
145 | regs->psr = (up_psr & ~(PSR_ICC | PSR_EF)) | 145 | regs->psr = (up_psr & ~(PSR_ICC | PSR_EF)) |
146 | | (regs->psr & (PSR_ICC | PSR_EF)); | 146 | | (regs->psr & (PSR_ICC | PSR_EF)); |
147 | 147 | ||
148 | /* Prevent syscall restart. */ | ||
149 | pt_regs_clear_syscall(regs); | ||
150 | |||
148 | err |= __get_user(fpu_save, &sf->fpu_save); | 151 | err |= __get_user(fpu_save, &sf->fpu_save); |
149 | 152 | ||
150 | if (fpu_save) | 153 | if (fpu_save) |
@@ -199,6 +202,9 @@ asmlinkage void do_rt_sigreturn(struct pt_regs *regs) | |||
199 | 202 | ||
200 | regs->psr = (regs->psr & ~PSR_ICC) | (psr & PSR_ICC); | 203 | regs->psr = (regs->psr & ~PSR_ICC) | (psr & PSR_ICC); |
201 | 204 | ||
205 | /* Prevent syscall restart. */ | ||
206 | pt_regs_clear_syscall(regs); | ||
207 | |||
202 | err |= __get_user(fpu_save, &sf->fpu_save); | 208 | err |= __get_user(fpu_save, &sf->fpu_save); |
203 | 209 | ||
204 | if (fpu_save) | 210 | if (fpu_save) |
@@ -245,15 +251,29 @@ static inline int invalid_frame_pointer(void __user *fp, int fplen) | |||
245 | 251 | ||
246 | static inline void __user *get_sigframe(struct sigaction *sa, struct pt_regs *regs, unsigned long framesize) | 252 | static inline void __user *get_sigframe(struct sigaction *sa, struct pt_regs *regs, unsigned long framesize) |
247 | { | 253 | { |
248 | unsigned long sp; | 254 | unsigned long sp = regs->u_regs[UREG_FP]; |
249 | 255 | ||
250 | sp = regs->u_regs[UREG_FP]; | 256 | /* |
257 | * If we are on the alternate signal stack and would overflow it, don't. | ||
258 | * Return an always-bogus address instead so we will die with SIGSEGV. | ||
259 | */ | ||
260 | if (on_sig_stack(sp) && !likely(on_sig_stack(sp - framesize))) | ||
261 | return (void __user *) -1L; | ||
251 | 262 | ||
252 | /* This is the X/Open sanctioned signal stack switching. */ | 263 | /* This is the X/Open sanctioned signal stack switching. */ |
253 | if (sa->sa_flags & SA_ONSTACK) { | 264 | if (sa->sa_flags & SA_ONSTACK) { |
254 | if (!on_sig_stack(sp) && !((current->sas_ss_sp + current->sas_ss_size) & 7)) | 265 | if (sas_ss_flags(sp) == 0) |
255 | sp = current->sas_ss_sp + current->sas_ss_size; | 266 | sp = current->sas_ss_sp + current->sas_ss_size; |
256 | } | 267 | } |
268 | |||
269 | /* Always align the stack frame. This handles two cases. First, | ||
270 | * sigaltstack need not be mindful of platform specific stack | ||
271 | * alignment. Second, if we took this signal because the stack | ||
272 | * is not aligned properly, we'd like to take the signal cleanly | ||
273 | * and report that. | ||
274 | */ | ||
275 | sp &= ~7UL; | ||
276 | |||
257 | return (void __user *)(sp - framesize); | 277 | return (void __user *)(sp - framesize); |
258 | } | 278 | } |
259 | 279 | ||
@@ -493,26 +513,36 @@ static inline void syscall_restart(unsigned long orig_i0, struct pt_regs *regs, | |||
493 | * want to handle. Thus you cannot kill init even with a SIGKILL even by | 513 | * want to handle. Thus you cannot kill init even with a SIGKILL even by |
494 | * mistake. | 514 | * mistake. |
495 | */ | 515 | */ |
496 | asmlinkage void do_signal(struct pt_regs * regs, unsigned long orig_i0, int restart_syscall) | 516 | asmlinkage void do_signal(struct pt_regs * regs, unsigned long orig_i0) |
497 | { | 517 | { |
498 | siginfo_t info; | ||
499 | struct sparc_deliver_cookie cookie; | ||
500 | struct k_sigaction ka; | 518 | struct k_sigaction ka; |
501 | int signr; | 519 | int restart_syscall; |
502 | sigset_t *oldset; | 520 | sigset_t *oldset; |
521 | siginfo_t info; | ||
522 | int signr; | ||
503 | 523 | ||
504 | cookie.restart_syscall = restart_syscall; | 524 | if (pt_regs_is_syscall(regs) && (regs->psr & PSR_C)) |
505 | cookie.orig_i0 = orig_i0; | 525 | restart_syscall = 1; |
526 | else | ||
527 | restart_syscall = 0; | ||
506 | 528 | ||
507 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) | 529 | if (test_thread_flag(TIF_RESTORE_SIGMASK)) |
508 | oldset = ¤t->saved_sigmask; | 530 | oldset = ¤t->saved_sigmask; |
509 | else | 531 | else |
510 | oldset = ¤t->blocked; | 532 | oldset = ¤t->blocked; |
511 | 533 | ||
512 | signr = get_signal_to_deliver(&info, &ka, regs, &cookie); | 534 | signr = get_signal_to_deliver(&info, &ka, regs, NULL); |
535 | |||
536 | /* If the debugger messes with the program counter, it clears | ||
537 | * the software "in syscall" bit, directing us to not perform | ||
538 | * a syscall restart. | ||
539 | */ | ||
540 | if (restart_syscall && !pt_regs_is_syscall(regs)) | ||
541 | restart_syscall = 0; | ||
542 | |||
513 | if (signr > 0) { | 543 | if (signr > 0) { |
514 | if (cookie.restart_syscall) | 544 | if (restart_syscall) |
515 | syscall_restart(cookie.orig_i0, regs, &ka.sa); | 545 | syscall_restart(orig_i0, regs, &ka.sa); |
516 | handle_signal(signr, &ka, &info, oldset, regs); | 546 | handle_signal(signr, &ka, &info, oldset, regs); |
517 | 547 | ||
518 | /* a signal was successfully delivered; the saved | 548 | /* a signal was successfully delivered; the saved |
@@ -524,16 +554,16 @@ asmlinkage void do_signal(struct pt_regs * regs, unsigned long orig_i0, int rest | |||
524 | clear_thread_flag(TIF_RESTORE_SIGMASK); | 554 | clear_thread_flag(TIF_RESTORE_SIGMASK); |
525 | return; | 555 | return; |
526 | } | 556 | } |
527 | if (cookie.restart_syscall && | 557 | if (restart_syscall && |
528 | (regs->u_regs[UREG_I0] == ERESTARTNOHAND || | 558 | (regs->u_regs[UREG_I0] == ERESTARTNOHAND || |
529 | regs->u_regs[UREG_I0] == ERESTARTSYS || | 559 | regs->u_regs[UREG_I0] == ERESTARTSYS || |
530 | regs->u_regs[UREG_I0] == ERESTARTNOINTR)) { | 560 | regs->u_regs[UREG_I0] == ERESTARTNOINTR)) { |
531 | /* replay the system call when we are done */ | 561 | /* replay the system call when we are done */ |
532 | regs->u_regs[UREG_I0] = cookie.orig_i0; | 562 | regs->u_regs[UREG_I0] = orig_i0; |
533 | regs->pc -= 4; | 563 | regs->pc -= 4; |
534 | regs->npc -= 4; | 564 | regs->npc -= 4; |
535 | } | 565 | } |
536 | if (cookie.restart_syscall && | 566 | if (restart_syscall && |
537 | regs->u_regs[UREG_I0] == ERESTART_RESTARTBLOCK) { | 567 | regs->u_regs[UREG_I0] == ERESTART_RESTARTBLOCK) { |
538 | regs->u_regs[UREG_G1] = __NR_restart_syscall; | 568 | regs->u_regs[UREG_G1] = __NR_restart_syscall; |
539 | regs->pc -= 4; | 569 | regs->pc -= 4; |
@@ -585,27 +615,3 @@ do_sys_sigstack(struct sigstack __user *ssptr, struct sigstack __user *ossptr, | |||
585 | out: | 615 | out: |
586 | return ret; | 616 | return ret; |
587 | } | 617 | } |
588 | |||
589 | void ptrace_signal_deliver(struct pt_regs *regs, void *cookie) | ||
590 | { | ||
591 | struct sparc_deliver_cookie *cp = cookie; | ||
592 | |||
593 | if (cp->restart_syscall && | ||
594 | (regs->u_regs[UREG_I0] == ERESTARTNOHAND || | ||
595 | regs->u_regs[UREG_I0] == ERESTARTSYS || | ||
596 | regs->u_regs[UREG_I0] == ERESTARTNOINTR)) { | ||
597 | /* replay the system call when we are done */ | ||
598 | regs->u_regs[UREG_I0] = cp->orig_i0; | ||
599 | regs->pc -= 4; | ||
600 | regs->npc -= 4; | ||
601 | cp->restart_syscall = 0; | ||
602 | } | ||
603 | |||
604 | if (cp->restart_syscall && | ||
605 | regs->u_regs[UREG_I0] == ERESTART_RESTARTBLOCK) { | ||
606 | regs->u_regs[UREG_G1] = __NR_restart_syscall; | ||
607 | regs->pc -= 4; | ||
608 | regs->npc -= 4; | ||
609 | cp->restart_syscall = 0; | ||
610 | } | ||
611 | } | ||
diff --git a/arch/sparc/kernel/sparc-stub.c b/arch/sparc/kernel/sparc-stub.c deleted file mode 100644 index e84f815e6903..000000000000 --- a/arch/sparc/kernel/sparc-stub.c +++ /dev/null | |||
@@ -1,724 +0,0 @@ | |||
1 | /* $Id: sparc-stub.c,v 1.28 2001/10/30 04:54:21 davem Exp $ | ||
2 | * sparc-stub.c: KGDB support for the Linux kernel. | ||
3 | * | ||
4 | * Modifications to run under Linux | ||
5 | * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu) | ||
6 | * | ||
7 | * This file originally came from the gdb sources, and the | ||
8 | * copyright notices have been retained below. | ||
9 | */ | ||
10 | |||
11 | /**************************************************************************** | ||
12 | |||
13 | THIS SOFTWARE IS NOT COPYRIGHTED | ||
14 | |||
15 | HP offers the following for use in the public domain. HP makes no | ||
16 | warranty with regard to the software or its performance and the | ||
17 | user accepts the software "AS IS" with all faults. | ||
18 | |||
19 | HP DISCLAIMS ANY WARRANTIES, EXPRESS OR IMPLIED, WITH REGARD | ||
20 | TO THIS SOFTWARE INCLUDING BUT NOT LIMITED TO THE WARRANTIES | ||
21 | OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. | ||
22 | |||
23 | ****************************************************************************/ | ||
24 | |||
25 | /**************************************************************************** | ||
26 | * Header: remcom.c,v 1.34 91/03/09 12:29:49 glenne Exp $ | ||
27 | * | ||
28 | * Module name: remcom.c $ | ||
29 | * Revision: 1.34 $ | ||
30 | * Date: 91/03/09 12:29:49 $ | ||
31 | * Contributor: Lake Stevens Instrument Division$ | ||
32 | * | ||
33 | * Description: low level support for gdb debugger. $ | ||
34 | * | ||
35 | * Considerations: only works on target hardware $ | ||
36 | * | ||
37 | * Written by: Glenn Engel $ | ||
38 | * ModuleState: Experimental $ | ||
39 | * | ||
40 | * NOTES: See Below $ | ||
41 | * | ||
42 | * Modified for SPARC by Stu Grossman, Cygnus Support. | ||
43 | * | ||
44 | * This code has been extensively tested on the Fujitsu SPARClite demo board. | ||
45 | * | ||
46 | * To enable debugger support, two things need to happen. One, a | ||
47 | * call to set_debug_traps() is necessary in order to allow any breakpoints | ||
48 | * or error conditions to be properly intercepted and reported to gdb. | ||
49 | * Two, a breakpoint needs to be generated to begin communication. This | ||
50 | * is most easily accomplished by a call to breakpoint(). Breakpoint() | ||
51 | * simulates a breakpoint by executing a trap #1. | ||
52 | * | ||
53 | ************* | ||
54 | * | ||
55 | * The following gdb commands are supported: | ||
56 | * | ||
57 | * command function Return value | ||
58 | * | ||
59 | * g return the value of the CPU registers hex data or ENN | ||
60 | * G set the value of the CPU registers OK or ENN | ||
61 | * | ||
62 | * mAA..AA,LLLL Read LLLL bytes at address AA..AA hex data or ENN | ||
63 | * MAA..AA,LLLL: Write LLLL bytes at address AA.AA OK or ENN | ||
64 | * | ||
65 | * c Resume at current address SNN ( signal NN) | ||
66 | * cAA..AA Continue at address AA..AA SNN | ||
67 | * | ||
68 | * s Step one instruction SNN | ||
69 | * sAA..AA Step one instruction from AA..AA SNN | ||
70 | * | ||
71 | * k kill | ||
72 | * | ||
73 | * ? What was the last sigval ? SNN (signal NN) | ||
74 | * | ||
75 | * bBB..BB Set baud rate to BB..BB OK or BNN, then sets | ||
76 | * baud rate | ||
77 | * | ||
78 | * All commands and responses are sent with a packet which includes a | ||
79 | * checksum. A packet consists of | ||
80 | * | ||
81 | * $<packet info>#<checksum>. | ||
82 | * | ||
83 | * where | ||
84 | * <packet info> :: <characters representing the command or response> | ||
85 | * <checksum> :: < two hex digits computed as modulo 256 sum of <packetinfo>> | ||
86 | * | ||
87 | * When a packet is received, it is first acknowledged with either '+' or '-'. | ||
88 | * '+' indicates a successful transfer. '-' indicates a failed transfer. | ||
89 | * | ||
90 | * Example: | ||
91 | * | ||
92 | * Host: Reply: | ||
93 | * $m0,10#2a +$00010203040506070809101112131415#42 | ||
94 | * | ||
95 | ****************************************************************************/ | ||
96 | |||
97 | #include <linux/kernel.h> | ||
98 | #include <linux/string.h> | ||
99 | #include <linux/mm.h> | ||
100 | #include <linux/smp.h> | ||
101 | #include <linux/smp_lock.h> | ||
102 | |||
103 | #include <asm/system.h> | ||
104 | #include <asm/signal.h> | ||
105 | #include <asm/oplib.h> | ||
106 | #include <asm/head.h> | ||
107 | #include <asm/traps.h> | ||
108 | #include <asm/vac-ops.h> | ||
109 | #include <asm/kgdb.h> | ||
110 | #include <asm/pgalloc.h> | ||
111 | #include <asm/pgtable.h> | ||
112 | #include <asm/cacheflush.h> | ||
113 | |||
114 | /* | ||
115 | * | ||
116 | * external low-level support routines | ||
117 | */ | ||
118 | |||
119 | extern void putDebugChar(char); /* write a single character */ | ||
120 | extern char getDebugChar(void); /* read and return a single char */ | ||
121 | |||
122 | /* | ||
123 | * BUFMAX defines the maximum number of characters in inbound/outbound buffers | ||
124 | * at least NUMREGBYTES*2 are needed for register packets | ||
125 | */ | ||
126 | #define BUFMAX 2048 | ||
127 | |||
128 | static int initialized; /* !0 means we've been initialized */ | ||
129 | |||
130 | static const char hexchars[]="0123456789abcdef"; | ||
131 | |||
132 | #define NUMREGS 72 | ||
133 | |||
134 | /* Number of bytes of registers. */ | ||
135 | #define NUMREGBYTES (NUMREGS * 4) | ||
136 | enum regnames {G0, G1, G2, G3, G4, G5, G6, G7, | ||
137 | O0, O1, O2, O3, O4, O5, SP, O7, | ||
138 | L0, L1, L2, L3, L4, L5, L6, L7, | ||
139 | I0, I1, I2, I3, I4, I5, FP, I7, | ||
140 | |||
141 | F0, F1, F2, F3, F4, F5, F6, F7, | ||
142 | F8, F9, F10, F11, F12, F13, F14, F15, | ||
143 | F16, F17, F18, F19, F20, F21, F22, F23, | ||
144 | F24, F25, F26, F27, F28, F29, F30, F31, | ||
145 | Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR }; | ||
146 | |||
147 | |||
148 | extern void trap_low(void); /* In arch/sparc/kernel/entry.S */ | ||
149 | |||
150 | unsigned long get_sun4cpte(unsigned long addr) | ||
151 | { | ||
152 | unsigned long entry; | ||
153 | |||
154 | __asm__ __volatile__("\n\tlda [%1] %2, %0\n\t" : | ||
155 | "=r" (entry) : | ||
156 | "r" (addr), "i" (ASI_PTE)); | ||
157 | return entry; | ||
158 | } | ||
159 | |||
160 | unsigned long get_sun4csegmap(unsigned long addr) | ||
161 | { | ||
162 | unsigned long entry; | ||
163 | |||
164 | __asm__ __volatile__("\n\tlduba [%1] %2, %0\n\t" : | ||
165 | "=r" (entry) : | ||
166 | "r" (addr), "i" (ASI_SEGMAP)); | ||
167 | return entry; | ||
168 | } | ||
169 | |||
170 | #if 0 | ||
171 | /* Have to sort this out. This cannot be done after initialization. */ | ||
172 | static void flush_cache_all_nop(void) {} | ||
173 | #endif | ||
174 | |||
175 | /* Place where we save old trap entries for restoration */ | ||
176 | struct tt_entry kgdb_savettable[256]; | ||
177 | typedef void (*trapfunc_t)(void); | ||
178 | |||
179 | /* Helper routine for manipulation of kgdb_savettable */ | ||
180 | static inline void copy_ttentry(struct tt_entry *src, struct tt_entry *dest) | ||
181 | { | ||
182 | dest->inst_one = src->inst_one; | ||
183 | dest->inst_two = src->inst_two; | ||
184 | dest->inst_three = src->inst_three; | ||
185 | dest->inst_four = src->inst_four; | ||
186 | } | ||
187 | |||
188 | /* Initialize the kgdb_savettable so that debugging can commence */ | ||
189 | static void eh_init(void) | ||
190 | { | ||
191 | int i; | ||
192 | |||
193 | for(i=0; i < 256; i++) | ||
194 | copy_ttentry(&sparc_ttable[i], &kgdb_savettable[i]); | ||
195 | } | ||
196 | |||
197 | /* Install an exception handler for kgdb */ | ||
198 | static void exceptionHandler(int tnum, trapfunc_t trap_entry) | ||
199 | { | ||
200 | unsigned long te_addr = (unsigned long) trap_entry; | ||
201 | |||
202 | /* Make new vector */ | ||
203 | sparc_ttable[tnum].inst_one = | ||
204 | SPARC_BRANCH((unsigned long) te_addr, | ||
205 | (unsigned long) &sparc_ttable[tnum].inst_one); | ||
206 | sparc_ttable[tnum].inst_two = SPARC_RD_PSR_L0; | ||
207 | sparc_ttable[tnum].inst_three = SPARC_NOP; | ||
208 | sparc_ttable[tnum].inst_four = SPARC_NOP; | ||
209 | } | ||
210 | |||
211 | /* Convert ch from a hex digit to an int */ | ||
212 | static int | ||
213 | hex(unsigned char ch) | ||
214 | { | ||
215 | if (ch >= 'a' && ch <= 'f') | ||
216 | return ch-'a'+10; | ||
217 | if (ch >= '0' && ch <= '9') | ||
218 | return ch-'0'; | ||
219 | if (ch >= 'A' && ch <= 'F') | ||
220 | return ch-'A'+10; | ||
221 | return -1; | ||
222 | } | ||
223 | |||
224 | /* scan for the sequence $<data>#<checksum> */ | ||
225 | static void | ||
226 | getpacket(char *buffer) | ||
227 | { | ||
228 | unsigned char checksum; | ||
229 | unsigned char xmitcsum; | ||
230 | int i; | ||
231 | int count; | ||
232 | unsigned char ch; | ||
233 | |||
234 | do { | ||
235 | /* wait around for the start character, ignore all other characters */ | ||
236 | while ((ch = (getDebugChar() & 0x7f)) != '$') ; | ||
237 | |||
238 | checksum = 0; | ||
239 | xmitcsum = -1; | ||
240 | |||
241 | count = 0; | ||
242 | |||
243 | /* now, read until a # or end of buffer is found */ | ||
244 | while (count < BUFMAX) { | ||
245 | ch = getDebugChar() & 0x7f; | ||
246 | if (ch == '#') | ||
247 | break; | ||
248 | checksum = checksum + ch; | ||
249 | buffer[count] = ch; | ||
250 | count = count + 1; | ||
251 | } | ||
252 | |||
253 | if (count >= BUFMAX) | ||
254 | continue; | ||
255 | |||
256 | buffer[count] = 0; | ||
257 | |||
258 | if (ch == '#') { | ||
259 | xmitcsum = hex(getDebugChar() & 0x7f) << 4; | ||
260 | xmitcsum |= hex(getDebugChar() & 0x7f); | ||
261 | if (checksum != xmitcsum) | ||
262 | putDebugChar('-'); /* failed checksum */ | ||
263 | else { | ||
264 | putDebugChar('+'); /* successful transfer */ | ||
265 | /* if a sequence char is present, reply the ID */ | ||
266 | if (buffer[2] == ':') { | ||
267 | putDebugChar(buffer[0]); | ||
268 | putDebugChar(buffer[1]); | ||
269 | /* remove sequence chars from buffer */ | ||
270 | count = strlen(buffer); | ||
271 | for (i=3; i <= count; i++) | ||
272 | buffer[i-3] = buffer[i]; | ||
273 | } | ||
274 | } | ||
275 | } | ||
276 | } while (checksum != xmitcsum); | ||
277 | } | ||
278 | |||
279 | /* send the packet in buffer. */ | ||
280 | |||
281 | static void | ||
282 | putpacket(unsigned char *buffer) | ||
283 | { | ||
284 | unsigned char checksum; | ||
285 | int count; | ||
286 | unsigned char ch, recv; | ||
287 | |||
288 | /* $<packet info>#<checksum>. */ | ||
289 | do { | ||
290 | putDebugChar('$'); | ||
291 | checksum = 0; | ||
292 | count = 0; | ||
293 | |||
294 | while ((ch = buffer[count])) { | ||
295 | putDebugChar(ch); | ||
296 | checksum += ch; | ||
297 | count += 1; | ||
298 | } | ||
299 | |||
300 | putDebugChar('#'); | ||
301 | putDebugChar(hexchars[checksum >> 4]); | ||
302 | putDebugChar(hexchars[checksum & 0xf]); | ||
303 | recv = getDebugChar(); | ||
304 | } while ((recv & 0x7f) != '+'); | ||
305 | } | ||
306 | |||
307 | static char remcomInBuffer[BUFMAX]; | ||
308 | static char remcomOutBuffer[BUFMAX]; | ||
309 | |||
310 | /* Convert the memory pointed to by mem into hex, placing result in buf. | ||
311 | * Return a pointer to the last char put in buf (null), in case of mem fault, | ||
312 | * return 0. | ||
313 | */ | ||
314 | |||
315 | static unsigned char * | ||
316 | mem2hex(char *mem, char *buf, int count) | ||
317 | { | ||
318 | unsigned char ch; | ||
319 | |||
320 | while (count-- > 0) { | ||
321 | /* This assembler code is basically: ch = *mem++; | ||
322 | * except that we use the SPARC/Linux exception table | ||
323 | * mechanism (see how "fixup" works in kernel_mna_trap_fault) | ||
324 | * to arrange for a "return 0" upon a memory fault | ||
325 | */ | ||
326 | __asm__( | ||
327 | "\n1:\n\t" | ||
328 | "ldub [%0], %1\n\t" | ||
329 | "inc %0\n\t" | ||
330 | ".section .fixup,#alloc,#execinstr\n\t" | ||
331 | ".align 4\n" | ||
332 | "2:\n\t" | ||
333 | "retl\n\t" | ||
334 | " mov 0, %%o0\n\t" | ||
335 | ".section __ex_table, #alloc\n\t" | ||
336 | ".align 4\n\t" | ||
337 | ".word 1b, 2b\n\t" | ||
338 | ".text\n" | ||
339 | : "=r" (mem), "=r" (ch) : "0" (mem)); | ||
340 | *buf++ = hexchars[ch >> 4]; | ||
341 | *buf++ = hexchars[ch & 0xf]; | ||
342 | } | ||
343 | |||
344 | *buf = 0; | ||
345 | return buf; | ||
346 | } | ||
347 | |||
348 | /* convert the hex array pointed to by buf into binary to be placed in mem | ||
349 | * return a pointer to the character AFTER the last byte written. | ||
350 | */ | ||
351 | static char * | ||
352 | hex2mem(char *buf, char *mem, int count) | ||
353 | { | ||
354 | int i; | ||
355 | unsigned char ch; | ||
356 | |||
357 | for (i=0; i<count; i++) { | ||
358 | |||
359 | ch = hex(*buf++) << 4; | ||
360 | ch |= hex(*buf++); | ||
361 | /* Assembler code is *mem++ = ch; with return 0 on fault */ | ||
362 | __asm__( | ||
363 | "\n1:\n\t" | ||
364 | "stb %1, [%0]\n\t" | ||
365 | "inc %0\n\t" | ||
366 | ".section .fixup,#alloc,#execinstr\n\t" | ||
367 | ".align 4\n" | ||
368 | "2:\n\t" | ||
369 | "retl\n\t" | ||
370 | " mov 0, %%o0\n\t" | ||
371 | ".section __ex_table, #alloc\n\t" | ||
372 | ".align 4\n\t" | ||
373 | ".word 1b, 2b\n\t" | ||
374 | ".text\n" | ||
375 | : "=r" (mem) : "r" (ch) , "0" (mem)); | ||
376 | } | ||
377 | return mem; | ||
378 | } | ||
379 | |||
380 | /* This table contains the mapping between SPARC hardware trap types, and | ||
381 | signals, which are primarily what GDB understands. It also indicates | ||
382 | which hardware traps we need to commandeer when initializing the stub. */ | ||
383 | |||
384 | static struct hard_trap_info | ||
385 | { | ||
386 | unsigned char tt; /* Trap type code for SPARC */ | ||
387 | unsigned char signo; /* Signal that we map this trap into */ | ||
388 | } hard_trap_info[] = { | ||
389 | {SP_TRAP_SBPT, SIGTRAP}, /* ta 1 - Linux/KGDB software breakpoint */ | ||
390 | {0, 0} /* Must be last */ | ||
391 | }; | ||
392 | |||
393 | /* Set up exception handlers for tracing and breakpoints */ | ||
394 | |||
395 | void | ||
396 | set_debug_traps(void) | ||
397 | { | ||
398 | struct hard_trap_info *ht; | ||
399 | unsigned long flags; | ||
400 | |||
401 | local_irq_save(flags); | ||
402 | #if 0 | ||
403 | /* Have to sort this out. This cannot be done after initialization. */ | ||
404 | BTFIXUPSET_CALL(flush_cache_all, flush_cache_all_nop, BTFIXUPCALL_NOP); | ||
405 | #endif | ||
406 | |||
407 | /* Initialize our copy of the Linux Sparc trap table */ | ||
408 | eh_init(); | ||
409 | |||
410 | for (ht = hard_trap_info; ht->tt && ht->signo; ht++) { | ||
411 | /* Only if it doesn't destroy our fault handlers */ | ||
412 | if((ht->tt != SP_TRAP_TFLT) && | ||
413 | (ht->tt != SP_TRAP_DFLT)) | ||
414 | exceptionHandler(ht->tt, trap_low); | ||
415 | } | ||
416 | |||
417 | /* In case GDB is started before us, ack any packets (presumably | ||
418 | * "$?#xx") sitting there. | ||
419 | * | ||
420 | * I've found this code causes more problems than it solves, | ||
421 | * so that's why it's commented out. GDB seems to work fine | ||
422 | * now starting either before or after the kernel -bwb | ||
423 | */ | ||
424 | #if 0 | ||
425 | while((c = getDebugChar()) != '$'); | ||
426 | while((c = getDebugChar()) != '#'); | ||
427 | c = getDebugChar(); /* eat first csum byte */ | ||
428 | c = getDebugChar(); /* eat second csum byte */ | ||
429 | putDebugChar('+'); /* ack it */ | ||
430 | #endif | ||
431 | |||
432 | initialized = 1; /* connect! */ | ||
433 | local_irq_restore(flags); | ||
434 | } | ||
435 | |||
436 | /* Convert the SPARC hardware trap type code to a unix signal number. */ | ||
437 | |||
438 | static int | ||
439 | computeSignal(int tt) | ||
440 | { | ||
441 | struct hard_trap_info *ht; | ||
442 | |||
443 | for (ht = hard_trap_info; ht->tt && ht->signo; ht++) | ||
444 | if (ht->tt == tt) | ||
445 | return ht->signo; | ||
446 | |||
447 | return SIGHUP; /* default for things we don't know about */ | ||
448 | } | ||
449 | |||
450 | /* | ||
451 | * While we find nice hex chars, build an int. | ||
452 | * Return number of chars processed. | ||
453 | */ | ||
454 | |||
455 | static int | ||
456 | hexToInt(char **ptr, int *intValue) | ||
457 | { | ||
458 | int numChars = 0; | ||
459 | int hexValue; | ||
460 | |||
461 | *intValue = 0; | ||
462 | |||
463 | while (**ptr) { | ||
464 | hexValue = hex(**ptr); | ||
465 | if (hexValue < 0) | ||
466 | break; | ||
467 | |||
468 | *intValue = (*intValue << 4) | hexValue; | ||
469 | numChars ++; | ||
470 | |||
471 | (*ptr)++; | ||
472 | } | ||
473 | |||
474 | return (numChars); | ||
475 | } | ||
476 | |||
477 | /* | ||
478 | * This function does all command processing for interfacing to gdb. It | ||
479 | * returns 1 if you should skip the instruction at the trap address, 0 | ||
480 | * otherwise. | ||
481 | */ | ||
482 | |||
483 | extern void breakinst(void); | ||
484 | |||
485 | void | ||
486 | handle_exception (unsigned long *registers) | ||
487 | { | ||
488 | int tt; /* Trap type */ | ||
489 | int sigval; | ||
490 | int addr; | ||
491 | int length; | ||
492 | char *ptr; | ||
493 | unsigned long *sp; | ||
494 | |||
495 | /* First, we must force all of the windows to be spilled out */ | ||
496 | |||
497 | asm("save %sp, -64, %sp\n\t" | ||
498 | "save %sp, -64, %sp\n\t" | ||
499 | "save %sp, -64, %sp\n\t" | ||
500 | "save %sp, -64, %sp\n\t" | ||
501 | "save %sp, -64, %sp\n\t" | ||
502 | "save %sp, -64, %sp\n\t" | ||
503 | "save %sp, -64, %sp\n\t" | ||
504 | "save %sp, -64, %sp\n\t" | ||
505 | "restore\n\t" | ||
506 | "restore\n\t" | ||
507 | "restore\n\t" | ||
508 | "restore\n\t" | ||
509 | "restore\n\t" | ||
510 | "restore\n\t" | ||
511 | "restore\n\t" | ||
512 | "restore\n\t"); | ||
513 | |||
514 | lock_kernel(); | ||
515 | if (registers[PC] == (unsigned long)breakinst) { | ||
516 | /* Skip over breakpoint trap insn */ | ||
517 | registers[PC] = registers[NPC]; | ||
518 | registers[NPC] += 4; | ||
519 | } | ||
520 | |||
521 | sp = (unsigned long *)registers[SP]; | ||
522 | |||
523 | tt = (registers[TBR] >> 4) & 0xff; | ||
524 | |||
525 | /* reply to host that an exception has occurred */ | ||
526 | sigval = computeSignal(tt); | ||
527 | ptr = remcomOutBuffer; | ||
528 | |||
529 | *ptr++ = 'T'; | ||
530 | *ptr++ = hexchars[sigval >> 4]; | ||
531 | *ptr++ = hexchars[sigval & 0xf]; | ||
532 | |||
533 | *ptr++ = hexchars[PC >> 4]; | ||
534 | *ptr++ = hexchars[PC & 0xf]; | ||
535 | *ptr++ = ':'; | ||
536 | ptr = mem2hex((char *)®isters[PC], ptr, 4); | ||
537 | *ptr++ = ';'; | ||
538 | |||
539 | *ptr++ = hexchars[FP >> 4]; | ||
540 | *ptr++ = hexchars[FP & 0xf]; | ||
541 | *ptr++ = ':'; | ||
542 | ptr = mem2hex((char *) (sp + 8 + 6), ptr, 4); /* FP */ | ||
543 | *ptr++ = ';'; | ||
544 | |||
545 | *ptr++ = hexchars[SP >> 4]; | ||
546 | *ptr++ = hexchars[SP & 0xf]; | ||
547 | *ptr++ = ':'; | ||
548 | ptr = mem2hex((char *)&sp, ptr, 4); | ||
549 | *ptr++ = ';'; | ||
550 | |||
551 | *ptr++ = hexchars[NPC >> 4]; | ||
552 | *ptr++ = hexchars[NPC & 0xf]; | ||
553 | *ptr++ = ':'; | ||
554 | ptr = mem2hex((char *)®isters[NPC], ptr, 4); | ||
555 | *ptr++ = ';'; | ||
556 | |||
557 | *ptr++ = hexchars[O7 >> 4]; | ||
558 | *ptr++ = hexchars[O7 & 0xf]; | ||
559 | *ptr++ = ':'; | ||
560 | ptr = mem2hex((char *)®isters[O7], ptr, 4); | ||
561 | *ptr++ = ';'; | ||
562 | |||
563 | *ptr++ = 0; | ||
564 | |||
565 | putpacket(remcomOutBuffer); | ||
566 | |||
567 | /* XXX We may want to add some features dealing with poking the | ||
568 | * XXX page tables, the real ones on the srmmu, and what is currently | ||
569 | * XXX loaded in the sun4/sun4c tlb at this point in time. But this | ||
570 | * XXX also required hacking to the gdb sources directly... | ||
571 | */ | ||
572 | |||
573 | while (1) { | ||
574 | remcomOutBuffer[0] = 0; | ||
575 | |||
576 | getpacket(remcomInBuffer); | ||
577 | switch (remcomInBuffer[0]) { | ||
578 | case '?': | ||
579 | remcomOutBuffer[0] = 'S'; | ||
580 | remcomOutBuffer[1] = hexchars[sigval >> 4]; | ||
581 | remcomOutBuffer[2] = hexchars[sigval & 0xf]; | ||
582 | remcomOutBuffer[3] = 0; | ||
583 | break; | ||
584 | |||
585 | case 'd': | ||
586 | /* toggle debug flag */ | ||
587 | break; | ||
588 | |||
589 | case 'g': /* return the value of the CPU registers */ | ||
590 | { | ||
591 | ptr = remcomOutBuffer; | ||
592 | /* G & O regs */ | ||
593 | ptr = mem2hex((char *)registers, ptr, 16 * 4); | ||
594 | /* L & I regs */ | ||
595 | ptr = mem2hex((char *) (sp + 0), ptr, 16 * 4); | ||
596 | /* Floating point */ | ||
597 | memset(ptr, '0', 32 * 8); | ||
598 | /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ | ||
599 | mem2hex((char *)®isters[Y], (ptr + 32 * 4 * 2), (8 * 4)); | ||
600 | } | ||
601 | break; | ||
602 | |||
603 | case 'G': /* set the value of the CPU registers - return OK */ | ||
604 | { | ||
605 | unsigned long *newsp, psr; | ||
606 | |||
607 | psr = registers[PSR]; | ||
608 | |||
609 | ptr = &remcomInBuffer[1]; | ||
610 | /* G & O regs */ | ||
611 | hex2mem(ptr, (char *)registers, 16 * 4); | ||
612 | /* L & I regs */ | ||
613 | hex2mem(ptr + 16 * 4 * 2, (char *) (sp + 0), 16 * 4); | ||
614 | /* Y, PSR, WIM, TBR, PC, NPC, FPSR, CPSR */ | ||
615 | hex2mem(ptr + 64 * 4 * 2, (char *)®isters[Y], 8 * 4); | ||
616 | |||
617 | /* See if the stack pointer has moved. If so, | ||
618 | * then copy the saved locals and ins to the | ||
619 | * new location. This keeps the window | ||
620 | * overflow and underflow routines happy. | ||
621 | */ | ||
622 | |||
623 | newsp = (unsigned long *)registers[SP]; | ||
624 | if (sp != newsp) | ||
625 | sp = memcpy(newsp, sp, 16 * 4); | ||
626 | |||
627 | /* Don't allow CWP to be modified. */ | ||
628 | |||
629 | if (psr != registers[PSR]) | ||
630 | registers[PSR] = (psr & 0x1f) | (registers[PSR] & ~0x1f); | ||
631 | |||
632 | strcpy(remcomOutBuffer,"OK"); | ||
633 | } | ||
634 | break; | ||
635 | |||
636 | case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ | ||
637 | /* Try to read %x,%x. */ | ||
638 | |||
639 | ptr = &remcomInBuffer[1]; | ||
640 | |||
641 | if (hexToInt(&ptr, &addr) | ||
642 | && *ptr++ == ',' | ||
643 | && hexToInt(&ptr, &length)) { | ||
644 | if (mem2hex((char *)addr, remcomOutBuffer, length)) | ||
645 | break; | ||
646 | |||
647 | strcpy (remcomOutBuffer, "E03"); | ||
648 | } else { | ||
649 | strcpy(remcomOutBuffer,"E01"); | ||
650 | } | ||
651 | break; | ||
652 | |||
653 | case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ | ||
654 | /* Try to read '%x,%x:'. */ | ||
655 | |||
656 | ptr = &remcomInBuffer[1]; | ||
657 | |||
658 | if (hexToInt(&ptr, &addr) | ||
659 | && *ptr++ == ',' | ||
660 | && hexToInt(&ptr, &length) | ||
661 | && *ptr++ == ':') { | ||
662 | if (hex2mem(ptr, (char *)addr, length)) { | ||
663 | strcpy(remcomOutBuffer, "OK"); | ||
664 | } else { | ||
665 | strcpy(remcomOutBuffer, "E03"); | ||
666 | } | ||
667 | } else { | ||
668 | strcpy(remcomOutBuffer, "E02"); | ||
669 | } | ||
670 | break; | ||
671 | |||
672 | case 'c': /* cAA..AA Continue at address AA..AA(optional) */ | ||
673 | /* try to read optional parameter, pc unchanged if no parm */ | ||
674 | |||
675 | ptr = &remcomInBuffer[1]; | ||
676 | if (hexToInt(&ptr, &addr)) { | ||
677 | registers[PC] = addr; | ||
678 | registers[NPC] = addr + 4; | ||
679 | } | ||
680 | |||
681 | /* Need to flush the instruction cache here, as we may have deposited a | ||
682 | * breakpoint, and the icache probably has no way of knowing that a data ref to | ||
683 | * some location may have changed something that is in the instruction cache. | ||
684 | */ | ||
685 | flush_cache_all(); | ||
686 | unlock_kernel(); | ||
687 | return; | ||
688 | |||
689 | /* kill the program */ | ||
690 | case 'k' : /* do nothing */ | ||
691 | break; | ||
692 | case 'r': /* Reset */ | ||
693 | asm ("call 0\n\t" | ||
694 | "nop\n\t"); | ||
695 | break; | ||
696 | } /* switch */ | ||
697 | |||
698 | /* reply to the request */ | ||
699 | putpacket(remcomOutBuffer); | ||
700 | } /* while(1) */ | ||
701 | } | ||
702 | |||
703 | /* This function will generate a breakpoint exception. It is used at the | ||
704 | beginning of a program to sync up with a debugger and can be used | ||
705 | otherwise as a quick means to stop program execution and "break" into | ||
706 | the debugger. */ | ||
707 | |||
708 | void | ||
709 | breakpoint(void) | ||
710 | { | ||
711 | if (!initialized) | ||
712 | return; | ||
713 | |||
714 | /* Again, watch those c-prefixes for ELF kernels */ | ||
715 | #if defined(__svr4__) || defined(__ELF__) | ||
716 | asm(".globl breakinst\n" | ||
717 | "breakinst:\n\t" | ||
718 | "ta 1\n"); | ||
719 | #else | ||
720 | asm(".globl _breakinst\n" | ||
721 | "_breakinst:\n\t" | ||
722 | "ta 1\n"); | ||
723 | #endif | ||
724 | } | ||
diff --git a/arch/sparc/kernel/sparc_ksyms.c b/arch/sparc/kernel/sparc_ksyms.c index 0bcf98a7ef38..aa8ee06cf488 100644 --- a/arch/sparc/kernel/sparc_ksyms.c +++ b/arch/sparc/kernel/sparc_ksyms.c | |||
@@ -282,3 +282,5 @@ EXPORT_SYMBOL(do_BUG); | |||
282 | 282 | ||
283 | /* Sun Power Management Idle Handler */ | 283 | /* Sun Power Management Idle Handler */ |
284 | EXPORT_SYMBOL(pm_idle); | 284 | EXPORT_SYMBOL(pm_idle); |
285 | |||
286 | EXPORT_SYMBOL(empty_zero_page); | ||
diff --git a/arch/sparc/kernel/sun4d_smp.c b/arch/sparc/kernel/sun4d_smp.c index 0def48158c7d..dfde77ff0848 100644 --- a/arch/sparc/kernel/sun4d_smp.c +++ b/arch/sparc/kernel/sun4d_smp.c | |||
@@ -335,37 +335,6 @@ void smp4d_cross_call_irq(void) | |||
335 | ccall_info.processors_out[i] = 1; | 335 | ccall_info.processors_out[i] = 1; |
336 | } | 336 | } |
337 | 337 | ||
338 | static int smp4d_stop_cpu_sender; | ||
339 | |||
340 | static void smp4d_stop_cpu(void) | ||
341 | { | ||
342 | int me = hard_smp4d_processor_id(); | ||
343 | |||
344 | if (me != smp4d_stop_cpu_sender) | ||
345 | while(1) barrier(); | ||
346 | } | ||
347 | |||
348 | /* Cross calls, in order to work efficiently and atomically do all | ||
349 | * the message passing work themselves, only stopcpu and reschedule | ||
350 | * messages come through here. | ||
351 | */ | ||
352 | void smp4d_message_pass(int target, int msg, unsigned long data, int wait) | ||
353 | { | ||
354 | int me = hard_smp4d_processor_id(); | ||
355 | |||
356 | SMP_PRINTK(("smp4d_message_pass %d %d %08lx %d\n", target, msg, data, wait)); | ||
357 | if (msg == MSG_STOP_CPU && target == MSG_ALL_BUT_SELF) { | ||
358 | unsigned long flags; | ||
359 | static DEFINE_SPINLOCK(stop_cpu_lock); | ||
360 | spin_lock_irqsave(&stop_cpu_lock, flags); | ||
361 | smp4d_stop_cpu_sender = me; | ||
362 | smp4d_cross_call((smpfunc_t)smp4d_stop_cpu, 0, 0, 0, 0, 0); | ||
363 | spin_unlock_irqrestore(&stop_cpu_lock, flags); | ||
364 | } | ||
365 | printk("Yeeee, trying to send SMP msg(%d) to %d on cpu %d\n", msg, target, me); | ||
366 | panic("Bogon SMP message pass."); | ||
367 | } | ||
368 | |||
369 | void smp4d_percpu_timer_interrupt(struct pt_regs *regs) | 338 | void smp4d_percpu_timer_interrupt(struct pt_regs *regs) |
370 | { | 339 | { |
371 | struct pt_regs *old_regs; | 340 | struct pt_regs *old_regs; |
@@ -439,7 +408,6 @@ void __init sun4d_init_smp(void) | |||
439 | BTFIXUPSET_BLACKBOX(hard_smp_processor_id, smp4d_blackbox_id); | 408 | BTFIXUPSET_BLACKBOX(hard_smp_processor_id, smp4d_blackbox_id); |
440 | BTFIXUPSET_BLACKBOX(load_current, smp4d_blackbox_current); | 409 | BTFIXUPSET_BLACKBOX(load_current, smp4d_blackbox_current); |
441 | BTFIXUPSET_CALL(smp_cross_call, smp4d_cross_call, BTFIXUPCALL_NORM); | 410 | BTFIXUPSET_CALL(smp_cross_call, smp4d_cross_call, BTFIXUPCALL_NORM); |
442 | BTFIXUPSET_CALL(smp_message_pass, smp4d_message_pass, BTFIXUPCALL_NORM); | ||
443 | BTFIXUPSET_CALL(__hard_smp_processor_id, __smp4d_processor_id, BTFIXUPCALL_NORM); | 411 | BTFIXUPSET_CALL(__hard_smp_processor_id, __smp4d_processor_id, BTFIXUPCALL_NORM); |
444 | 412 | ||
445 | for (i = 0; i < NR_CPUS; i++) { | 413 | for (i = 0; i < NR_CPUS; i++) { |
diff --git a/arch/sparc/kernel/sun4m_smp.c b/arch/sparc/kernel/sun4m_smp.c index 0b9407267162..ffb875aacb7e 100644 --- a/arch/sparc/kernel/sun4m_smp.c +++ b/arch/sparc/kernel/sun4m_smp.c | |||
@@ -34,8 +34,6 @@ | |||
34 | 34 | ||
35 | #include "irq.h" | 35 | #include "irq.h" |
36 | 36 | ||
37 | #define IRQ_RESCHEDULE 13 | ||
38 | #define IRQ_STOP_CPU 14 | ||
39 | #define IRQ_CROSS_CALL 15 | 37 | #define IRQ_CROSS_CALL 15 |
40 | 38 | ||
41 | extern ctxd_t *srmmu_ctx_table_phys; | 39 | extern ctxd_t *srmmu_ctx_table_phys; |
@@ -232,48 +230,6 @@ void smp4m_irq_rotate(int cpu) | |||
232 | set_irq_udt(next); | 230 | set_irq_udt(next); |
233 | } | 231 | } |
234 | 232 | ||
235 | /* Cross calls, in order to work efficiently and atomically do all | ||
236 | * the message passing work themselves, only stopcpu and reschedule | ||
237 | * messages come through here. | ||
238 | */ | ||
239 | void smp4m_message_pass(int target, int msg, unsigned long data, int wait) | ||
240 | { | ||
241 | static unsigned long smp_cpu_in_msg[NR_CPUS]; | ||
242 | cpumask_t mask; | ||
243 | int me = smp_processor_id(); | ||
244 | int irq, i; | ||
245 | |||
246 | if(msg == MSG_RESCHEDULE) { | ||
247 | irq = IRQ_RESCHEDULE; | ||
248 | |||
249 | if(smp_cpu_in_msg[me]) | ||
250 | return; | ||
251 | } else if(msg == MSG_STOP_CPU) { | ||
252 | irq = IRQ_STOP_CPU; | ||
253 | } else { | ||
254 | goto barf; | ||
255 | } | ||
256 | |||
257 | smp_cpu_in_msg[me]++; | ||
258 | if(target == MSG_ALL_BUT_SELF || target == MSG_ALL) { | ||
259 | mask = cpu_online_map; | ||
260 | if(target == MSG_ALL_BUT_SELF) | ||
261 | cpu_clear(me, mask); | ||
262 | for(i = 0; i < 4; i++) { | ||
263 | if (cpu_isset(i, mask)) | ||
264 | set_cpu_int(i, irq); | ||
265 | } | ||
266 | } else { | ||
267 | set_cpu_int(target, irq); | ||
268 | } | ||
269 | smp_cpu_in_msg[me]--; | ||
270 | |||
271 | return; | ||
272 | barf: | ||
273 | printk("Yeeee, trying to send SMP msg(%d) on cpu %d\n", msg, me); | ||
274 | panic("Bogon SMP message pass."); | ||
275 | } | ||
276 | |||
277 | static struct smp_funcall { | 233 | static struct smp_funcall { |
278 | smpfunc_t func; | 234 | smpfunc_t func; |
279 | unsigned long arg1; | 235 | unsigned long arg1; |
@@ -413,6 +369,5 @@ void __init sun4m_init_smp(void) | |||
413 | BTFIXUPSET_BLACKBOX(hard_smp_processor_id, smp4m_blackbox_id); | 369 | BTFIXUPSET_BLACKBOX(hard_smp_processor_id, smp4m_blackbox_id); |
414 | BTFIXUPSET_BLACKBOX(load_current, smp4m_blackbox_current); | 370 | BTFIXUPSET_BLACKBOX(load_current, smp4m_blackbox_current); |
415 | BTFIXUPSET_CALL(smp_cross_call, smp4m_cross_call, BTFIXUPCALL_NORM); | 371 | BTFIXUPSET_CALL(smp_cross_call, smp4m_cross_call, BTFIXUPCALL_NORM); |
416 | BTFIXUPSET_CALL(smp_message_pass, smp4m_message_pass, BTFIXUPCALL_NORM); | ||
417 | BTFIXUPSET_CALL(__hard_smp_processor_id, __smp4m_processor_id, BTFIXUPCALL_NORM); | 372 | BTFIXUPSET_CALL(__hard_smp_processor_id, __smp4m_processor_id, BTFIXUPCALL_NORM); |
418 | } | 373 | } |
diff --git a/arch/sparc/kernel/sys_sparc.c b/arch/sparc/kernel/sys_sparc.c index f188b5dc9fd0..3c6b49a53ae8 100644 --- a/arch/sparc/kernel/sys_sparc.c +++ b/arch/sparc/kernel/sys_sparc.c | |||
@@ -219,12 +219,11 @@ out: | |||
219 | return err; | 219 | return err; |
220 | } | 220 | } |
221 | 221 | ||
222 | int sparc_mmap_check(unsigned long addr, unsigned long len, unsigned long flags) | 222 | int sparc_mmap_check(unsigned long addr, unsigned long len) |
223 | { | 223 | { |
224 | if (ARCH_SUN4C_SUN4 && | 224 | if (ARCH_SUN4C_SUN4 && |
225 | (len > 0x20000000 || | 225 | (len > 0x20000000 || |
226 | ((flags & MAP_FIXED) && | 226 | (addr < 0xe0000000 && addr + len > 0x20000000))) |
227 | addr < 0xe0000000 && addr + len > 0x20000000))) | ||
228 | return -EINVAL; | 227 | return -EINVAL; |
229 | 228 | ||
230 | /* See asm-sparc/uaccess.h */ | 229 | /* See asm-sparc/uaccess.h */ |
@@ -296,52 +295,14 @@ asmlinkage unsigned long sparc_mremap(unsigned long addr, | |||
296 | unsigned long old_len, unsigned long new_len, | 295 | unsigned long old_len, unsigned long new_len, |
297 | unsigned long flags, unsigned long new_addr) | 296 | unsigned long flags, unsigned long new_addr) |
298 | { | 297 | { |
299 | struct vm_area_struct *vma; | ||
300 | unsigned long ret = -EINVAL; | 298 | unsigned long ret = -EINVAL; |
301 | if (ARCH_SUN4C_SUN4) { | 299 | |
302 | if (old_len > 0x20000000 || new_len > 0x20000000) | 300 | if (unlikely(sparc_mmap_check(addr, old_len))) |
303 | goto out; | 301 | goto out; |
304 | if (addr < 0xe0000000 && addr + old_len > 0x20000000) | 302 | if (unlikely(sparc_mmap_check(new_addr, new_len))) |
305 | goto out; | ||
306 | } | ||
307 | if (old_len > TASK_SIZE - PAGE_SIZE || | ||
308 | new_len > TASK_SIZE - PAGE_SIZE) | ||
309 | goto out; | 303 | goto out; |
310 | down_write(¤t->mm->mmap_sem); | 304 | down_write(¤t->mm->mmap_sem); |
311 | if (flags & MREMAP_FIXED) { | ||
312 | if (ARCH_SUN4C_SUN4 && | ||
313 | new_addr < 0xe0000000 && | ||
314 | new_addr + new_len > 0x20000000) | ||
315 | goto out_sem; | ||
316 | if (new_addr + new_len > TASK_SIZE - PAGE_SIZE) | ||
317 | goto out_sem; | ||
318 | } else if ((ARCH_SUN4C_SUN4 && addr < 0xe0000000 && | ||
319 | addr + new_len > 0x20000000) || | ||
320 | addr + new_len > TASK_SIZE - PAGE_SIZE) { | ||
321 | unsigned long map_flags = 0; | ||
322 | struct file *file = NULL; | ||
323 | |||
324 | ret = -ENOMEM; | ||
325 | if (!(flags & MREMAP_MAYMOVE)) | ||
326 | goto out_sem; | ||
327 | |||
328 | vma = find_vma(current->mm, addr); | ||
329 | if (vma) { | ||
330 | if (vma->vm_flags & VM_SHARED) | ||
331 | map_flags |= MAP_SHARED; | ||
332 | file = vma->vm_file; | ||
333 | } | ||
334 | |||
335 | new_addr = get_unmapped_area(file, addr, new_len, | ||
336 | vma ? vma->vm_pgoff : 0, | ||
337 | map_flags); | ||
338 | ret = new_addr; | ||
339 | if (new_addr & ~PAGE_MASK) | ||
340 | goto out_sem; | ||
341 | flags |= MREMAP_FIXED; | ||
342 | } | ||
343 | ret = do_mremap(addr, old_len, new_len, flags, new_addr); | 305 | ret = do_mremap(addr, old_len, new_len, flags, new_addr); |
344 | out_sem: | ||
345 | up_write(¤t->mm->mmap_sem); | 306 | up_write(¤t->mm->mmap_sem); |
346 | out: | 307 | out: |
347 | return ret; | 308 | return ret; |