Commit f0514ae323f19ba1ad4bea4174ea274c812f7eee
Committed by
Kyle McMartin
1 parent
0be7d1fe43
Exists in
master
and in
7 other branches
parisc: initialize unwinder much earlier
The unwinder was being initialized way too late to be any use debugging early boot crashes. Instead of relying on module_init initcalls to initialize it, let's do it explicitly as early as we can. Signed-off-by: James Bottomley <James.Bottomley@HansenPartnership.com> Signed-off-by: Kyle McMartin <kyle@mcmartin.ca>
Showing 3 changed files with 5 additions and 3 deletions Inline Diff
arch/parisc/include/asm/unwind.h
1 | #ifndef _UNWIND_H_ | 1 | #ifndef _UNWIND_H_ |
2 | #define _UNWIND_H_ | 2 | #define _UNWIND_H_ |
3 | 3 | ||
4 | #include <linux/list.h> | 4 | #include <linux/list.h> |
5 | 5 | ||
6 | /* From ABI specifications */ | 6 | /* From ABI specifications */ |
7 | struct unwind_table_entry { | 7 | struct unwind_table_entry { |
8 | unsigned int region_start; | 8 | unsigned int region_start; |
9 | unsigned int region_end; | 9 | unsigned int region_end; |
10 | unsigned int Cannot_unwind:1; /* 0 */ | 10 | unsigned int Cannot_unwind:1; /* 0 */ |
11 | unsigned int Millicode:1; /* 1 */ | 11 | unsigned int Millicode:1; /* 1 */ |
12 | unsigned int Millicode_save_sr0:1; /* 2 */ | 12 | unsigned int Millicode_save_sr0:1; /* 2 */ |
13 | unsigned int Region_description:2; /* 3..4 */ | 13 | unsigned int Region_description:2; /* 3..4 */ |
14 | unsigned int reserved1:1; /* 5 */ | 14 | unsigned int reserved1:1; /* 5 */ |
15 | unsigned int Entry_SR:1; /* 6 */ | 15 | unsigned int Entry_SR:1; /* 6 */ |
16 | unsigned int Entry_FR:4; /* number saved *//* 7..10 */ | 16 | unsigned int Entry_FR:4; /* number saved *//* 7..10 */ |
17 | unsigned int Entry_GR:5; /* number saved *//* 11..15 */ | 17 | unsigned int Entry_GR:5; /* number saved *//* 11..15 */ |
18 | unsigned int Args_stored:1; /* 16 */ | 18 | unsigned int Args_stored:1; /* 16 */ |
19 | unsigned int Variable_Frame:1; /* 17 */ | 19 | unsigned int Variable_Frame:1; /* 17 */ |
20 | unsigned int Separate_Package_Body:1; /* 18 */ | 20 | unsigned int Separate_Package_Body:1; /* 18 */ |
21 | unsigned int Frame_Extension_Millicode:1; /* 19 */ | 21 | unsigned int Frame_Extension_Millicode:1; /* 19 */ |
22 | unsigned int Stack_Overflow_Check:1; /* 20 */ | 22 | unsigned int Stack_Overflow_Check:1; /* 20 */ |
23 | unsigned int Two_Instruction_SP_Increment:1; /* 21 */ | 23 | unsigned int Two_Instruction_SP_Increment:1; /* 21 */ |
24 | unsigned int Ada_Region:1; /* 22 */ | 24 | unsigned int Ada_Region:1; /* 22 */ |
25 | unsigned int cxx_info:1; /* 23 */ | 25 | unsigned int cxx_info:1; /* 23 */ |
26 | unsigned int cxx_try_catch:1; /* 24 */ | 26 | unsigned int cxx_try_catch:1; /* 24 */ |
27 | unsigned int sched_entry_seq:1; /* 25 */ | 27 | unsigned int sched_entry_seq:1; /* 25 */ |
28 | unsigned int reserved2:1; /* 26 */ | 28 | unsigned int reserved2:1; /* 26 */ |
29 | unsigned int Save_SP:1; /* 27 */ | 29 | unsigned int Save_SP:1; /* 27 */ |
30 | unsigned int Save_RP:1; /* 28 */ | 30 | unsigned int Save_RP:1; /* 28 */ |
31 | unsigned int Save_MRP_in_frame:1; /* 29 */ | 31 | unsigned int Save_MRP_in_frame:1; /* 29 */ |
32 | unsigned int extn_ptr_defined:1; /* 30 */ | 32 | unsigned int extn_ptr_defined:1; /* 30 */ |
33 | unsigned int Cleanup_defined:1; /* 31 */ | 33 | unsigned int Cleanup_defined:1; /* 31 */ |
34 | 34 | ||
35 | unsigned int MPE_XL_interrupt_marker:1; /* 0 */ | 35 | unsigned int MPE_XL_interrupt_marker:1; /* 0 */ |
36 | unsigned int HP_UX_interrupt_marker:1; /* 1 */ | 36 | unsigned int HP_UX_interrupt_marker:1; /* 1 */ |
37 | unsigned int Large_frame:1; /* 2 */ | 37 | unsigned int Large_frame:1; /* 2 */ |
38 | unsigned int Pseudo_SP_Set:1; /* 3 */ | 38 | unsigned int Pseudo_SP_Set:1; /* 3 */ |
39 | unsigned int reserved4:1; /* 4 */ | 39 | unsigned int reserved4:1; /* 4 */ |
40 | unsigned int Total_frame_size:27; /* 5..31 */ | 40 | unsigned int Total_frame_size:27; /* 5..31 */ |
41 | }; | 41 | }; |
42 | 42 | ||
43 | struct unwind_table { | 43 | struct unwind_table { |
44 | struct list_head list; | 44 | struct list_head list; |
45 | const char *name; | 45 | const char *name; |
46 | unsigned long gp; | 46 | unsigned long gp; |
47 | unsigned long base_addr; | 47 | unsigned long base_addr; |
48 | unsigned long start; | 48 | unsigned long start; |
49 | unsigned long end; | 49 | unsigned long end; |
50 | const struct unwind_table_entry *table; | 50 | const struct unwind_table_entry *table; |
51 | unsigned long length; | 51 | unsigned long length; |
52 | }; | 52 | }; |
53 | 53 | ||
54 | struct unwind_frame_info { | 54 | struct unwind_frame_info { |
55 | struct task_struct *t; | 55 | struct task_struct *t; |
56 | /* Eventually we would like to be able to get at any of the registers | 56 | /* Eventually we would like to be able to get at any of the registers |
57 | available; but for now we only try to get the sp and ip for each | 57 | available; but for now we only try to get the sp and ip for each |
58 | frame */ | 58 | frame */ |
59 | /* struct pt_regs regs; */ | 59 | /* struct pt_regs regs; */ |
60 | unsigned long sp, ip, rp, r31; | 60 | unsigned long sp, ip, rp, r31; |
61 | unsigned long prev_sp, prev_ip; | 61 | unsigned long prev_sp, prev_ip; |
62 | }; | 62 | }; |
63 | 63 | ||
64 | struct unwind_table * | 64 | struct unwind_table * |
65 | unwind_table_add(const char *name, unsigned long base_addr, | 65 | unwind_table_add(const char *name, unsigned long base_addr, |
66 | unsigned long gp, void *start, void *end); | 66 | unsigned long gp, void *start, void *end); |
67 | void | 67 | void |
68 | unwind_table_remove(struct unwind_table *table); | 68 | unwind_table_remove(struct unwind_table *table); |
69 | 69 | ||
70 | void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t, | 70 | void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t, |
71 | struct pt_regs *regs); | 71 | struct pt_regs *regs); |
72 | void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t); | 72 | void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t); |
73 | void unwind_frame_init_running(struct unwind_frame_info *info, struct pt_regs *regs); | 73 | void unwind_frame_init_running(struct unwind_frame_info *info, struct pt_regs *regs); |
74 | int unwind_once(struct unwind_frame_info *info); | 74 | int unwind_once(struct unwind_frame_info *info); |
75 | int unwind_to_user(struct unwind_frame_info *info); | 75 | int unwind_to_user(struct unwind_frame_info *info); |
76 | 76 | ||
77 | int unwind_init(void); | ||
78 | |||
77 | #endif | 79 | #endif |
78 | 80 |
arch/parisc/kernel/setup.c
1 | /* $Id: setup.c,v 1.8 2000/02/02 04:42:38 prumpf Exp $ | 1 | /* $Id: setup.c,v 1.8 2000/02/02 04:42:38 prumpf Exp $ |
2 | * | 2 | * |
3 | * Initial setup-routines for HP 9000 based hardware. | 3 | * Initial setup-routines for HP 9000 based hardware. |
4 | * | 4 | * |
5 | * Copyright (C) 1991, 1992, 1995 Linus Torvalds | 5 | * Copyright (C) 1991, 1992, 1995 Linus Torvalds |
6 | * Modifications for PA-RISC (C) 1999 Helge Deller <deller@gmx.de> | 6 | * Modifications for PA-RISC (C) 1999 Helge Deller <deller@gmx.de> |
7 | * Modifications copyright 1999 SuSE GmbH (Philipp Rumpf) | 7 | * Modifications copyright 1999 SuSE GmbH (Philipp Rumpf) |
8 | * Modifications copyright 2000 Martin K. Petersen <mkp@mkp.net> | 8 | * Modifications copyright 2000 Martin K. Petersen <mkp@mkp.net> |
9 | * Modifications copyright 2000 Philipp Rumpf <prumpf@tux.org> | 9 | * Modifications copyright 2000 Philipp Rumpf <prumpf@tux.org> |
10 | * Modifications copyright 2001 Ryan Bradetich <rbradetich@uswest.net> | 10 | * Modifications copyright 2001 Ryan Bradetich <rbradetich@uswest.net> |
11 | * | 11 | * |
12 | * Initial PA-RISC Version: 04-23-1999 by Helge Deller | 12 | * Initial PA-RISC Version: 04-23-1999 by Helge Deller |
13 | * | 13 | * |
14 | * This program is free software; you can redistribute it and/or modify | 14 | * This program is free software; you can redistribute it and/or modify |
15 | * it under the terms of the GNU General Public License as published by | 15 | * it under the terms of the GNU General Public License as published by |
16 | * the Free Software Foundation; either version 2, or (at your option) | 16 | * the Free Software Foundation; either version 2, or (at your option) |
17 | * any later version. | 17 | * any later version. |
18 | * | 18 | * |
19 | * This program is distributed in the hope that it will be useful, | 19 | * This program is distributed in the hope that it will be useful, |
20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 20 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 21 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
22 | * GNU General Public License for more details. | 22 | * GNU General Public License for more details. |
23 | * | 23 | * |
24 | * You should have received a copy of the GNU General Public License | 24 | * You should have received a copy of the GNU General Public License |
25 | * along with this program; if not, write to the Free Software | 25 | * along with this program; if not, write to the Free Software |
26 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 26 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
27 | * | 27 | * |
28 | */ | 28 | */ |
29 | 29 | ||
30 | #include <linux/kernel.h> | 30 | #include <linux/kernel.h> |
31 | #include <linux/initrd.h> | 31 | #include <linux/initrd.h> |
32 | #include <linux/init.h> | 32 | #include <linux/init.h> |
33 | #include <linux/console.h> | 33 | #include <linux/console.h> |
34 | #include <linux/seq_file.h> | 34 | #include <linux/seq_file.h> |
35 | #define PCI_DEBUG | 35 | #define PCI_DEBUG |
36 | #include <linux/pci.h> | 36 | #include <linux/pci.h> |
37 | #undef PCI_DEBUG | 37 | #undef PCI_DEBUG |
38 | #include <linux/proc_fs.h> | 38 | #include <linux/proc_fs.h> |
39 | 39 | ||
40 | #include <asm/processor.h> | 40 | #include <asm/processor.h> |
41 | #include <asm/pdc.h> | 41 | #include <asm/pdc.h> |
42 | #include <asm/led.h> | 42 | #include <asm/led.h> |
43 | #include <asm/machdep.h> /* for pa7300lc_init() proto */ | 43 | #include <asm/machdep.h> /* for pa7300lc_init() proto */ |
44 | #include <asm/pdc_chassis.h> | 44 | #include <asm/pdc_chassis.h> |
45 | #include <asm/io.h> | 45 | #include <asm/io.h> |
46 | #include <asm/setup.h> | 46 | #include <asm/setup.h> |
47 | #include <asm/unwind.h> | ||
47 | 48 | ||
48 | static char __initdata command_line[COMMAND_LINE_SIZE]; | 49 | static char __initdata command_line[COMMAND_LINE_SIZE]; |
49 | 50 | ||
50 | /* Intended for ccio/sba/cpu statistics under /proc/bus/{runway|gsc} */ | 51 | /* Intended for ccio/sba/cpu statistics under /proc/bus/{runway|gsc} */ |
51 | struct proc_dir_entry * proc_runway_root __read_mostly = NULL; | 52 | struct proc_dir_entry * proc_runway_root __read_mostly = NULL; |
52 | struct proc_dir_entry * proc_gsc_root __read_mostly = NULL; | 53 | struct proc_dir_entry * proc_gsc_root __read_mostly = NULL; |
53 | struct proc_dir_entry * proc_mckinley_root __read_mostly = NULL; | 54 | struct proc_dir_entry * proc_mckinley_root __read_mostly = NULL; |
54 | 55 | ||
55 | #if !defined(CONFIG_PA20) && (defined(CONFIG_IOMMU_CCIO) || defined(CONFIG_IOMMU_SBA)) | 56 | #if !defined(CONFIG_PA20) && (defined(CONFIG_IOMMU_CCIO) || defined(CONFIG_IOMMU_SBA)) |
56 | int parisc_bus_is_phys __read_mostly = 1; /* Assume no IOMMU is present */ | 57 | int parisc_bus_is_phys __read_mostly = 1; /* Assume no IOMMU is present */ |
57 | EXPORT_SYMBOL(parisc_bus_is_phys); | 58 | EXPORT_SYMBOL(parisc_bus_is_phys); |
58 | #endif | 59 | #endif |
59 | 60 | ||
60 | /* This sets the vmerge boundary and size, it's here because it has to | 61 | /* This sets the vmerge boundary and size, it's here because it has to |
61 | * be available on all platforms (zero means no-virtual merging) */ | 62 | * be available on all platforms (zero means no-virtual merging) */ |
62 | unsigned long parisc_vmerge_boundary = 0; | 63 | unsigned long parisc_vmerge_boundary = 0; |
63 | unsigned long parisc_vmerge_max_size = 0; | 64 | unsigned long parisc_vmerge_max_size = 0; |
64 | 65 | ||
65 | void __init setup_cmdline(char **cmdline_p) | 66 | void __init setup_cmdline(char **cmdline_p) |
66 | { | 67 | { |
67 | extern unsigned int boot_args[]; | 68 | extern unsigned int boot_args[]; |
68 | 69 | ||
69 | /* Collect stuff passed in from the boot loader */ | 70 | /* Collect stuff passed in from the boot loader */ |
70 | 71 | ||
71 | /* boot_args[0] is free-mem start, boot_args[1] is ptr to command line */ | 72 | /* boot_args[0] is free-mem start, boot_args[1] is ptr to command line */ |
72 | if (boot_args[0] < 64) { | 73 | if (boot_args[0] < 64) { |
73 | /* called from hpux boot loader */ | 74 | /* called from hpux boot loader */ |
74 | boot_command_line[0] = '\0'; | 75 | boot_command_line[0] = '\0'; |
75 | } else { | 76 | } else { |
76 | strcpy(boot_command_line, (char *)__va(boot_args[1])); | 77 | strcpy(boot_command_line, (char *)__va(boot_args[1])); |
77 | 78 | ||
78 | #ifdef CONFIG_BLK_DEV_INITRD | 79 | #ifdef CONFIG_BLK_DEV_INITRD |
79 | if (boot_args[2] != 0) /* did palo pass us a ramdisk? */ | 80 | if (boot_args[2] != 0) /* did palo pass us a ramdisk? */ |
80 | { | 81 | { |
81 | initrd_start = (unsigned long)__va(boot_args[2]); | 82 | initrd_start = (unsigned long)__va(boot_args[2]); |
82 | initrd_end = (unsigned long)__va(boot_args[3]); | 83 | initrd_end = (unsigned long)__va(boot_args[3]); |
83 | } | 84 | } |
84 | #endif | 85 | #endif |
85 | } | 86 | } |
86 | 87 | ||
87 | strcpy(command_line, boot_command_line); | 88 | strcpy(command_line, boot_command_line); |
88 | *cmdline_p = command_line; | 89 | *cmdline_p = command_line; |
89 | } | 90 | } |
90 | 91 | ||
91 | #ifdef CONFIG_PA11 | 92 | #ifdef CONFIG_PA11 |
92 | void __init dma_ops_init(void) | 93 | void __init dma_ops_init(void) |
93 | { | 94 | { |
94 | switch (boot_cpu_data.cpu_type) { | 95 | switch (boot_cpu_data.cpu_type) { |
95 | case pcx: | 96 | case pcx: |
96 | /* | 97 | /* |
97 | * We've got way too many dependencies on 1.1 semantics | 98 | * We've got way too many dependencies on 1.1 semantics |
98 | * to support 1.0 boxes at this point. | 99 | * to support 1.0 boxes at this point. |
99 | */ | 100 | */ |
100 | panic( "PA-RISC Linux currently only supports machines that conform to\n" | 101 | panic( "PA-RISC Linux currently only supports machines that conform to\n" |
101 | "the PA-RISC 1.1 or 2.0 architecture specification.\n"); | 102 | "the PA-RISC 1.1 or 2.0 architecture specification.\n"); |
102 | 103 | ||
103 | case pcxs: | 104 | case pcxs: |
104 | case pcxt: | 105 | case pcxt: |
105 | hppa_dma_ops = &pcx_dma_ops; | 106 | hppa_dma_ops = &pcx_dma_ops; |
106 | break; | 107 | break; |
107 | case pcxl2: | 108 | case pcxl2: |
108 | pa7300lc_init(); | 109 | pa7300lc_init(); |
109 | case pcxl: /* falls through */ | 110 | case pcxl: /* falls through */ |
110 | hppa_dma_ops = &pcxl_dma_ops; | 111 | hppa_dma_ops = &pcxl_dma_ops; |
111 | break; | 112 | break; |
112 | default: | 113 | default: |
113 | break; | 114 | break; |
114 | } | 115 | } |
115 | } | 116 | } |
116 | #endif | 117 | #endif |
117 | 118 | ||
118 | extern int init_per_cpu(int cpuid); | 119 | extern int init_per_cpu(int cpuid); |
119 | extern void collect_boot_cpu_data(void); | 120 | extern void collect_boot_cpu_data(void); |
120 | 121 | ||
121 | void __init setup_arch(char **cmdline_p) | 122 | void __init setup_arch(char **cmdline_p) |
122 | { | 123 | { |
123 | #ifdef CONFIG_64BIT | 124 | #ifdef CONFIG_64BIT |
124 | extern int parisc_narrow_firmware; | 125 | extern int parisc_narrow_firmware; |
125 | #endif | 126 | #endif |
127 | unwind_init(); | ||
126 | 128 | ||
127 | init_per_cpu(smp_processor_id()); /* Set Modes & Enable FP */ | 129 | init_per_cpu(smp_processor_id()); /* Set Modes & Enable FP */ |
128 | 130 | ||
129 | #ifdef CONFIG_64BIT | 131 | #ifdef CONFIG_64BIT |
130 | printk(KERN_INFO "The 64-bit Kernel has started...\n"); | 132 | printk(KERN_INFO "The 64-bit Kernel has started...\n"); |
131 | #else | 133 | #else |
132 | printk(KERN_INFO "The 32-bit Kernel has started...\n"); | 134 | printk(KERN_INFO "The 32-bit Kernel has started...\n"); |
133 | #endif | 135 | #endif |
134 | 136 | ||
135 | pdc_console_init(); | 137 | pdc_console_init(); |
136 | 138 | ||
137 | #ifdef CONFIG_64BIT | 139 | #ifdef CONFIG_64BIT |
138 | if(parisc_narrow_firmware) { | 140 | if(parisc_narrow_firmware) { |
139 | printk(KERN_INFO "Kernel is using PDC in 32-bit mode.\n"); | 141 | printk(KERN_INFO "Kernel is using PDC in 32-bit mode.\n"); |
140 | } | 142 | } |
141 | #endif | 143 | #endif |
142 | setup_pdc(); | 144 | setup_pdc(); |
143 | setup_cmdline(cmdline_p); | 145 | setup_cmdline(cmdline_p); |
144 | collect_boot_cpu_data(); | 146 | collect_boot_cpu_data(); |
145 | do_memory_inventory(); /* probe for physical memory */ | 147 | do_memory_inventory(); /* probe for physical memory */ |
146 | parisc_cache_init(); | 148 | parisc_cache_init(); |
147 | paging_init(); | 149 | paging_init(); |
148 | 150 | ||
149 | #ifdef CONFIG_CHASSIS_LCD_LED | 151 | #ifdef CONFIG_CHASSIS_LCD_LED |
150 | /* initialize the LCD/LED after boot_cpu_data is available ! */ | 152 | /* initialize the LCD/LED after boot_cpu_data is available ! */ |
151 | led_init(); /* LCD/LED initialization */ | 153 | led_init(); /* LCD/LED initialization */ |
152 | #endif | 154 | #endif |
153 | 155 | ||
154 | #ifdef CONFIG_PA11 | 156 | #ifdef CONFIG_PA11 |
155 | dma_ops_init(); | 157 | dma_ops_init(); |
156 | #endif | 158 | #endif |
157 | 159 | ||
158 | #if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE) | 160 | #if defined(CONFIG_VT) && defined(CONFIG_DUMMY_CONSOLE) |
159 | conswitchp = &dummy_con; /* we use take_over_console() later ! */ | 161 | conswitchp = &dummy_con; /* we use take_over_console() later ! */ |
160 | #endif | 162 | #endif |
161 | 163 | ||
162 | } | 164 | } |
163 | 165 | ||
164 | /* | 166 | /* |
165 | * Display CPU info for all CPUs. | 167 | * Display CPU info for all CPUs. |
166 | * for parisc this is in processor.c | 168 | * for parisc this is in processor.c |
167 | */ | 169 | */ |
168 | extern int show_cpuinfo (struct seq_file *m, void *v); | 170 | extern int show_cpuinfo (struct seq_file *m, void *v); |
169 | 171 | ||
170 | static void * | 172 | static void * |
171 | c_start (struct seq_file *m, loff_t *pos) | 173 | c_start (struct seq_file *m, loff_t *pos) |
172 | { | 174 | { |
173 | /* Looks like the caller will call repeatedly until we return | 175 | /* Looks like the caller will call repeatedly until we return |
174 | * 0, signaling EOF perhaps. This could be used to sequence | 176 | * 0, signaling EOF perhaps. This could be used to sequence |
175 | * through CPUs for example. Since we print all cpu info in our | 177 | * through CPUs for example. Since we print all cpu info in our |
176 | * show_cpuinfo() disregarding 'pos' (which I assume is 'v' above) | 178 | * show_cpuinfo() disregarding 'pos' (which I assume is 'v' above) |
177 | * we only allow for one "position". */ | 179 | * we only allow for one "position". */ |
178 | return ((long)*pos < 1) ? (void *)1 : NULL; | 180 | return ((long)*pos < 1) ? (void *)1 : NULL; |
179 | } | 181 | } |
180 | 182 | ||
181 | static void * | 183 | static void * |
182 | c_next (struct seq_file *m, void *v, loff_t *pos) | 184 | c_next (struct seq_file *m, void *v, loff_t *pos) |
183 | { | 185 | { |
184 | ++*pos; | 186 | ++*pos; |
185 | return c_start(m, pos); | 187 | return c_start(m, pos); |
186 | } | 188 | } |
187 | 189 | ||
188 | static void | 190 | static void |
189 | c_stop (struct seq_file *m, void *v) | 191 | c_stop (struct seq_file *m, void *v) |
190 | { | 192 | { |
191 | } | 193 | } |
192 | 194 | ||
193 | const struct seq_operations cpuinfo_op = { | 195 | const struct seq_operations cpuinfo_op = { |
194 | .start = c_start, | 196 | .start = c_start, |
195 | .next = c_next, | 197 | .next = c_next, |
196 | .stop = c_stop, | 198 | .stop = c_stop, |
197 | .show = show_cpuinfo | 199 | .show = show_cpuinfo |
198 | }; | 200 | }; |
199 | 201 | ||
200 | static void __init parisc_proc_mkdir(void) | 202 | static void __init parisc_proc_mkdir(void) |
201 | { | 203 | { |
202 | /* | 204 | /* |
203 | ** Can't call proc_mkdir() until after proc_root_init() has been | 205 | ** Can't call proc_mkdir() until after proc_root_init() has been |
204 | ** called by start_kernel(). In other words, this code can't | 206 | ** called by start_kernel(). In other words, this code can't |
205 | ** live in arch/.../setup.c because start_parisc() calls | 207 | ** live in arch/.../setup.c because start_parisc() calls |
206 | ** start_kernel(). | 208 | ** start_kernel(). |
207 | */ | 209 | */ |
208 | switch (boot_cpu_data.cpu_type) { | 210 | switch (boot_cpu_data.cpu_type) { |
209 | case pcxl: | 211 | case pcxl: |
210 | case pcxl2: | 212 | case pcxl2: |
211 | if (NULL == proc_gsc_root) | 213 | if (NULL == proc_gsc_root) |
212 | { | 214 | { |
213 | proc_gsc_root = proc_mkdir("bus/gsc", NULL); | 215 | proc_gsc_root = proc_mkdir("bus/gsc", NULL); |
214 | } | 216 | } |
215 | break; | 217 | break; |
216 | case pcxt_: | 218 | case pcxt_: |
217 | case pcxu: | 219 | case pcxu: |
218 | case pcxu_: | 220 | case pcxu_: |
219 | case pcxw: | 221 | case pcxw: |
220 | case pcxw_: | 222 | case pcxw_: |
221 | case pcxw2: | 223 | case pcxw2: |
222 | if (NULL == proc_runway_root) | 224 | if (NULL == proc_runway_root) |
223 | { | 225 | { |
224 | proc_runway_root = proc_mkdir("bus/runway", NULL); | 226 | proc_runway_root = proc_mkdir("bus/runway", NULL); |
225 | } | 227 | } |
226 | break; | 228 | break; |
227 | case mako: | 229 | case mako: |
228 | case mako2: | 230 | case mako2: |
229 | if (NULL == proc_mckinley_root) | 231 | if (NULL == proc_mckinley_root) |
230 | { | 232 | { |
231 | proc_mckinley_root = proc_mkdir("bus/mckinley", NULL); | 233 | proc_mckinley_root = proc_mkdir("bus/mckinley", NULL); |
232 | } | 234 | } |
233 | break; | 235 | break; |
234 | default: | 236 | default: |
235 | /* FIXME: this was added to prevent the compiler | 237 | /* FIXME: this was added to prevent the compiler |
236 | * complaining about missing pcx, pcxs and pcxt | 238 | * complaining about missing pcx, pcxs and pcxt |
237 | * I'm assuming they have neither gsc nor runway */ | 239 | * I'm assuming they have neither gsc nor runway */ |
238 | break; | 240 | break; |
239 | } | 241 | } |
240 | } | 242 | } |
241 | 243 | ||
242 | static struct resource central_bus = { | 244 | static struct resource central_bus = { |
243 | .name = "Central Bus", | 245 | .name = "Central Bus", |
244 | .start = F_EXTEND(0xfff80000), | 246 | .start = F_EXTEND(0xfff80000), |
245 | .end = F_EXTEND(0xfffaffff), | 247 | .end = F_EXTEND(0xfffaffff), |
246 | .flags = IORESOURCE_MEM, | 248 | .flags = IORESOURCE_MEM, |
247 | }; | 249 | }; |
248 | 250 | ||
249 | static struct resource local_broadcast = { | 251 | static struct resource local_broadcast = { |
250 | .name = "Local Broadcast", | 252 | .name = "Local Broadcast", |
251 | .start = F_EXTEND(0xfffb0000), | 253 | .start = F_EXTEND(0xfffb0000), |
252 | .end = F_EXTEND(0xfffdffff), | 254 | .end = F_EXTEND(0xfffdffff), |
253 | .flags = IORESOURCE_MEM, | 255 | .flags = IORESOURCE_MEM, |
254 | }; | 256 | }; |
255 | 257 | ||
256 | static struct resource global_broadcast = { | 258 | static struct resource global_broadcast = { |
257 | .name = "Global Broadcast", | 259 | .name = "Global Broadcast", |
258 | .start = F_EXTEND(0xfffe0000), | 260 | .start = F_EXTEND(0xfffe0000), |
259 | .end = F_EXTEND(0xffffffff), | 261 | .end = F_EXTEND(0xffffffff), |
260 | .flags = IORESOURCE_MEM, | 262 | .flags = IORESOURCE_MEM, |
261 | }; | 263 | }; |
262 | 264 | ||
263 | static int __init parisc_init_resources(void) | 265 | static int __init parisc_init_resources(void) |
264 | { | 266 | { |
265 | int result; | 267 | int result; |
266 | 268 | ||
267 | result = request_resource(&iomem_resource, ¢ral_bus); | 269 | result = request_resource(&iomem_resource, ¢ral_bus); |
268 | if (result < 0) { | 270 | if (result < 0) { |
269 | printk(KERN_ERR | 271 | printk(KERN_ERR |
270 | "%s: failed to claim %s address space!\n", | 272 | "%s: failed to claim %s address space!\n", |
271 | __FILE__, central_bus.name); | 273 | __FILE__, central_bus.name); |
272 | return result; | 274 | return result; |
273 | } | 275 | } |
274 | 276 | ||
275 | result = request_resource(&iomem_resource, &local_broadcast); | 277 | result = request_resource(&iomem_resource, &local_broadcast); |
276 | if (result < 0) { | 278 | if (result < 0) { |
277 | printk(KERN_ERR | 279 | printk(KERN_ERR |
278 | "%s: failed to claim %saddress space!\n", | 280 | "%s: failed to claim %saddress space!\n", |
279 | __FILE__, local_broadcast.name); | 281 | __FILE__, local_broadcast.name); |
280 | return result; | 282 | return result; |
281 | } | 283 | } |
282 | 284 | ||
283 | result = request_resource(&iomem_resource, &global_broadcast); | 285 | result = request_resource(&iomem_resource, &global_broadcast); |
284 | if (result < 0) { | 286 | if (result < 0) { |
285 | printk(KERN_ERR | 287 | printk(KERN_ERR |
286 | "%s: failed to claim %s address space!\n", | 288 | "%s: failed to claim %s address space!\n", |
287 | __FILE__, global_broadcast.name); | 289 | __FILE__, global_broadcast.name); |
288 | return result; | 290 | return result; |
289 | } | 291 | } |
290 | 292 | ||
291 | return 0; | 293 | return 0; |
292 | } | 294 | } |
293 | 295 | ||
294 | extern void gsc_init(void); | 296 | extern void gsc_init(void); |
295 | extern void processor_init(void); | 297 | extern void processor_init(void); |
296 | extern void ccio_init(void); | 298 | extern void ccio_init(void); |
297 | extern void hppb_init(void); | 299 | extern void hppb_init(void); |
298 | extern void dino_init(void); | 300 | extern void dino_init(void); |
299 | extern void iosapic_init(void); | 301 | extern void iosapic_init(void); |
300 | extern void lba_init(void); | 302 | extern void lba_init(void); |
301 | extern void sba_init(void); | 303 | extern void sba_init(void); |
302 | extern void eisa_init(void); | 304 | extern void eisa_init(void); |
303 | 305 | ||
304 | static int __init parisc_init(void) | 306 | static int __init parisc_init(void) |
305 | { | 307 | { |
306 | u32 osid = (OS_ID_LINUX << 16); | 308 | u32 osid = (OS_ID_LINUX << 16); |
307 | 309 | ||
308 | parisc_proc_mkdir(); | 310 | parisc_proc_mkdir(); |
309 | parisc_init_resources(); | 311 | parisc_init_resources(); |
310 | do_device_inventory(); /* probe for hardware */ | 312 | do_device_inventory(); /* probe for hardware */ |
311 | 313 | ||
312 | parisc_pdc_chassis_init(); | 314 | parisc_pdc_chassis_init(); |
313 | 315 | ||
314 | /* set up a new led state on systems shipped LED State panel */ | 316 | /* set up a new led state on systems shipped LED State panel */ |
315 | pdc_chassis_send_status(PDC_CHASSIS_DIRECT_BSTART); | 317 | pdc_chassis_send_status(PDC_CHASSIS_DIRECT_BSTART); |
316 | 318 | ||
317 | /* tell PDC we're Linux. Nevermind failure. */ | 319 | /* tell PDC we're Linux. Nevermind failure. */ |
318 | pdc_stable_write(0x40, &osid, sizeof(osid)); | 320 | pdc_stable_write(0x40, &osid, sizeof(osid)); |
319 | 321 | ||
320 | processor_init(); | 322 | processor_init(); |
321 | printk(KERN_INFO "CPU(s): %d x %s at %d.%06d MHz\n", | 323 | printk(KERN_INFO "CPU(s): %d x %s at %d.%06d MHz\n", |
322 | boot_cpu_data.cpu_count, | 324 | boot_cpu_data.cpu_count, |
323 | boot_cpu_data.cpu_name, | 325 | boot_cpu_data.cpu_name, |
324 | boot_cpu_data.cpu_hz / 1000000, | 326 | boot_cpu_data.cpu_hz / 1000000, |
325 | boot_cpu_data.cpu_hz % 1000000 ); | 327 | boot_cpu_data.cpu_hz % 1000000 ); |
326 | 328 | ||
327 | parisc_setup_cache_timing(); | 329 | parisc_setup_cache_timing(); |
328 | 330 | ||
329 | /* These are in a non-obvious order, will fix when we have an iotree */ | 331 | /* These are in a non-obvious order, will fix when we have an iotree */ |
330 | #if defined(CONFIG_IOSAPIC) | 332 | #if defined(CONFIG_IOSAPIC) |
331 | iosapic_init(); | 333 | iosapic_init(); |
332 | #endif | 334 | #endif |
333 | #if defined(CONFIG_IOMMU_SBA) | 335 | #if defined(CONFIG_IOMMU_SBA) |
334 | sba_init(); | 336 | sba_init(); |
335 | #endif | 337 | #endif |
336 | #if defined(CONFIG_PCI_LBA) | 338 | #if defined(CONFIG_PCI_LBA) |
337 | lba_init(); | 339 | lba_init(); |
338 | #endif | 340 | #endif |
339 | 341 | ||
340 | /* CCIO before any potential subdevices */ | 342 | /* CCIO before any potential subdevices */ |
341 | #if defined(CONFIG_IOMMU_CCIO) | 343 | #if defined(CONFIG_IOMMU_CCIO) |
342 | ccio_init(); | 344 | ccio_init(); |
343 | #endif | 345 | #endif |
344 | 346 | ||
345 | /* | 347 | /* |
346 | * Need to register Asp & Wax before the EISA adapters for the IRQ | 348 | * Need to register Asp & Wax before the EISA adapters for the IRQ |
347 | * regions. EISA must come before PCI to be sure it gets IRQ region | 349 | * regions. EISA must come before PCI to be sure it gets IRQ region |
348 | * 0. | 350 | * 0. |
349 | */ | 351 | */ |
350 | #if defined(CONFIG_GSC_LASI) || defined(CONFIG_GSC_WAX) | 352 | #if defined(CONFIG_GSC_LASI) || defined(CONFIG_GSC_WAX) |
351 | gsc_init(); | 353 | gsc_init(); |
352 | #endif | 354 | #endif |
353 | #ifdef CONFIG_EISA | 355 | #ifdef CONFIG_EISA |
354 | eisa_init(); | 356 | eisa_init(); |
355 | #endif | 357 | #endif |
356 | 358 | ||
357 | #if defined(CONFIG_HPPB) | 359 | #if defined(CONFIG_HPPB) |
358 | hppb_init(); | 360 | hppb_init(); |
359 | #endif | 361 | #endif |
360 | 362 | ||
361 | #if defined(CONFIG_GSC_DINO) | 363 | #if defined(CONFIG_GSC_DINO) |
362 | dino_init(); | 364 | dino_init(); |
363 | #endif | 365 | #endif |
364 | 366 | ||
365 | #ifdef CONFIG_CHASSIS_LCD_LED | 367 | #ifdef CONFIG_CHASSIS_LCD_LED |
366 | register_led_regions(); /* register LED port info in procfs */ | 368 | register_led_regions(); /* register LED port info in procfs */ |
367 | #endif | 369 | #endif |
368 | 370 | ||
369 | return 0; | 371 | return 0; |
370 | } | 372 | } |
371 | arch_initcall(parisc_init); | 373 | arch_initcall(parisc_init); |
372 | 374 | ||
373 | void start_parisc(void) | 375 | void start_parisc(void) |
374 | { | 376 | { |
375 | extern void start_kernel(void); | 377 | extern void start_kernel(void); |
376 | 378 | ||
377 | int ret, cpunum; | 379 | int ret, cpunum; |
378 | struct pdc_coproc_cfg coproc_cfg; | 380 | struct pdc_coproc_cfg coproc_cfg; |
379 | 381 | ||
380 | cpunum = smp_processor_id(); | 382 | cpunum = smp_processor_id(); |
381 | 383 | ||
382 | set_firmware_width_unlocked(); | 384 | set_firmware_width_unlocked(); |
383 | 385 | ||
384 | ret = pdc_coproc_cfg_unlocked(&coproc_cfg); | 386 | ret = pdc_coproc_cfg_unlocked(&coproc_cfg); |
385 | if (ret >= 0 && coproc_cfg.ccr_functional) { | 387 | if (ret >= 0 && coproc_cfg.ccr_functional) { |
386 | mtctl(coproc_cfg.ccr_functional, 10); | 388 | mtctl(coproc_cfg.ccr_functional, 10); |
387 | 389 | ||
388 | cpu_data[cpunum].fp_rev = coproc_cfg.revision; | 390 | cpu_data[cpunum].fp_rev = coproc_cfg.revision; |
389 | cpu_data[cpunum].fp_model = coproc_cfg.model; | 391 | cpu_data[cpunum].fp_model = coproc_cfg.model; |
390 | 392 | ||
391 | asm volatile ("fstd %fr0,8(%sp)"); | 393 | asm volatile ("fstd %fr0,8(%sp)"); |
392 | } else { | 394 | } else { |
393 | panic("must have an fpu to boot linux"); | 395 | panic("must have an fpu to boot linux"); |
394 | } | 396 | } |
395 | 397 | ||
396 | start_kernel(); | 398 | start_kernel(); |
397 | // not reached | 399 | // not reached |
398 | } | 400 | } |
399 | 401 |
arch/parisc/kernel/unwind.c
1 | /* | 1 | /* |
2 | * Kernel unwinding support | 2 | * Kernel unwinding support |
3 | * | 3 | * |
4 | * (c) 2002-2004 Randolph Chung <tausq@debian.org> | 4 | * (c) 2002-2004 Randolph Chung <tausq@debian.org> |
5 | * | 5 | * |
6 | * Derived partially from the IA64 implementation. The PA-RISC | 6 | * Derived partially from the IA64 implementation. The PA-RISC |
7 | * Runtime Architecture Document is also a useful reference to | 7 | * Runtime Architecture Document is also a useful reference to |
8 | * understand what is happening here | 8 | * understand what is happening here |
9 | */ | 9 | */ |
10 | 10 | ||
11 | #include <linux/kernel.h> | 11 | #include <linux/kernel.h> |
12 | #include <linux/init.h> | 12 | #include <linux/init.h> |
13 | #include <linux/sched.h> | 13 | #include <linux/sched.h> |
14 | #include <linux/slab.h> | 14 | #include <linux/slab.h> |
15 | #include <linux/kallsyms.h> | 15 | #include <linux/kallsyms.h> |
16 | 16 | ||
17 | #include <asm/uaccess.h> | 17 | #include <asm/uaccess.h> |
18 | #include <asm/assembly.h> | 18 | #include <asm/assembly.h> |
19 | #include <asm/asm-offsets.h> | 19 | #include <asm/asm-offsets.h> |
20 | #include <asm/ptrace.h> | 20 | #include <asm/ptrace.h> |
21 | 21 | ||
22 | #include <asm/unwind.h> | 22 | #include <asm/unwind.h> |
23 | 23 | ||
24 | /* #define DEBUG 1 */ | 24 | /* #define DEBUG 1 */ |
25 | #ifdef DEBUG | 25 | #ifdef DEBUG |
26 | #define dbg(x...) printk(x) | 26 | #define dbg(x...) printk(x) |
27 | #else | 27 | #else |
28 | #define dbg(x...) | 28 | #define dbg(x...) |
29 | #endif | 29 | #endif |
30 | 30 | ||
31 | #define KERNEL_START (KERNEL_BINARY_TEXT_START - 0x1000) | 31 | #define KERNEL_START (KERNEL_BINARY_TEXT_START - 0x1000) |
32 | 32 | ||
33 | extern struct unwind_table_entry __start___unwind[]; | 33 | extern struct unwind_table_entry __start___unwind[]; |
34 | extern struct unwind_table_entry __stop___unwind[]; | 34 | extern struct unwind_table_entry __stop___unwind[]; |
35 | 35 | ||
36 | static spinlock_t unwind_lock; | 36 | static spinlock_t unwind_lock; |
37 | /* | 37 | /* |
38 | * the kernel unwind block is not dynamically allocated so that | 38 | * the kernel unwind block is not dynamically allocated so that |
39 | * we can call unwind_init as early in the bootup process as | 39 | * we can call unwind_init as early in the bootup process as |
40 | * possible (before the slab allocator is initialized) | 40 | * possible (before the slab allocator is initialized) |
41 | */ | 41 | */ |
42 | static struct unwind_table kernel_unwind_table __read_mostly; | 42 | static struct unwind_table kernel_unwind_table __read_mostly; |
43 | static LIST_HEAD(unwind_tables); | 43 | static LIST_HEAD(unwind_tables); |
44 | 44 | ||
45 | static inline const struct unwind_table_entry * | 45 | static inline const struct unwind_table_entry * |
46 | find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr) | 46 | find_unwind_entry_in_table(const struct unwind_table *table, unsigned long addr) |
47 | { | 47 | { |
48 | const struct unwind_table_entry *e = NULL; | 48 | const struct unwind_table_entry *e = NULL; |
49 | unsigned long lo, hi, mid; | 49 | unsigned long lo, hi, mid; |
50 | 50 | ||
51 | lo = 0; | 51 | lo = 0; |
52 | hi = table->length - 1; | 52 | hi = table->length - 1; |
53 | 53 | ||
54 | while (lo <= hi) { | 54 | while (lo <= hi) { |
55 | mid = (hi - lo) / 2 + lo; | 55 | mid = (hi - lo) / 2 + lo; |
56 | e = &table->table[mid]; | 56 | e = &table->table[mid]; |
57 | if (addr < e->region_start) | 57 | if (addr < e->region_start) |
58 | hi = mid - 1; | 58 | hi = mid - 1; |
59 | else if (addr > e->region_end) | 59 | else if (addr > e->region_end) |
60 | lo = mid + 1; | 60 | lo = mid + 1; |
61 | else | 61 | else |
62 | return e; | 62 | return e; |
63 | } | 63 | } |
64 | 64 | ||
65 | return NULL; | 65 | return NULL; |
66 | } | 66 | } |
67 | 67 | ||
68 | static const struct unwind_table_entry * | 68 | static const struct unwind_table_entry * |
69 | find_unwind_entry(unsigned long addr) | 69 | find_unwind_entry(unsigned long addr) |
70 | { | 70 | { |
71 | struct unwind_table *table; | 71 | struct unwind_table *table; |
72 | const struct unwind_table_entry *e = NULL; | 72 | const struct unwind_table_entry *e = NULL; |
73 | 73 | ||
74 | if (addr >= kernel_unwind_table.start && | 74 | if (addr >= kernel_unwind_table.start && |
75 | addr <= kernel_unwind_table.end) | 75 | addr <= kernel_unwind_table.end) |
76 | e = find_unwind_entry_in_table(&kernel_unwind_table, addr); | 76 | e = find_unwind_entry_in_table(&kernel_unwind_table, addr); |
77 | else | 77 | else |
78 | list_for_each_entry(table, &unwind_tables, list) { | 78 | list_for_each_entry(table, &unwind_tables, list) { |
79 | if (addr >= table->start && | 79 | if (addr >= table->start && |
80 | addr <= table->end) | 80 | addr <= table->end) |
81 | e = find_unwind_entry_in_table(table, addr); | 81 | e = find_unwind_entry_in_table(table, addr); |
82 | if (e) | 82 | if (e) |
83 | break; | 83 | break; |
84 | } | 84 | } |
85 | 85 | ||
86 | return e; | 86 | return e; |
87 | } | 87 | } |
88 | 88 | ||
89 | static void | 89 | static void |
90 | unwind_table_init(struct unwind_table *table, const char *name, | 90 | unwind_table_init(struct unwind_table *table, const char *name, |
91 | unsigned long base_addr, unsigned long gp, | 91 | unsigned long base_addr, unsigned long gp, |
92 | void *table_start, void *table_end) | 92 | void *table_start, void *table_end) |
93 | { | 93 | { |
94 | struct unwind_table_entry *start = table_start; | 94 | struct unwind_table_entry *start = table_start; |
95 | struct unwind_table_entry *end = | 95 | struct unwind_table_entry *end = |
96 | (struct unwind_table_entry *)table_end - 1; | 96 | (struct unwind_table_entry *)table_end - 1; |
97 | 97 | ||
98 | table->name = name; | 98 | table->name = name; |
99 | table->base_addr = base_addr; | 99 | table->base_addr = base_addr; |
100 | table->gp = gp; | 100 | table->gp = gp; |
101 | table->start = base_addr + start->region_start; | 101 | table->start = base_addr + start->region_start; |
102 | table->end = base_addr + end->region_end; | 102 | table->end = base_addr + end->region_end; |
103 | table->table = (struct unwind_table_entry *)table_start; | 103 | table->table = (struct unwind_table_entry *)table_start; |
104 | table->length = end - start + 1; | 104 | table->length = end - start + 1; |
105 | INIT_LIST_HEAD(&table->list); | 105 | INIT_LIST_HEAD(&table->list); |
106 | 106 | ||
107 | for (; start <= end; start++) { | 107 | for (; start <= end; start++) { |
108 | if (start < end && | 108 | if (start < end && |
109 | start->region_end > (start+1)->region_start) { | 109 | start->region_end > (start+1)->region_start) { |
110 | printk("WARNING: Out of order unwind entry! %p and %p\n", start, start+1); | 110 | printk("WARNING: Out of order unwind entry! %p and %p\n", start, start+1); |
111 | } | 111 | } |
112 | 112 | ||
113 | start->region_start += base_addr; | 113 | start->region_start += base_addr; |
114 | start->region_end += base_addr; | 114 | start->region_end += base_addr; |
115 | } | 115 | } |
116 | } | 116 | } |
117 | 117 | ||
118 | static void | 118 | static void |
119 | unwind_table_sort(struct unwind_table_entry *start, | 119 | unwind_table_sort(struct unwind_table_entry *start, |
120 | struct unwind_table_entry *finish) | 120 | struct unwind_table_entry *finish) |
121 | { | 121 | { |
122 | struct unwind_table_entry el, *p, *q; | 122 | struct unwind_table_entry el, *p, *q; |
123 | 123 | ||
124 | for (p = start + 1; p < finish; ++p) { | 124 | for (p = start + 1; p < finish; ++p) { |
125 | if (p[0].region_start < p[-1].region_start) { | 125 | if (p[0].region_start < p[-1].region_start) { |
126 | el = *p; | 126 | el = *p; |
127 | q = p; | 127 | q = p; |
128 | do { | 128 | do { |
129 | q[0] = q[-1]; | 129 | q[0] = q[-1]; |
130 | --q; | 130 | --q; |
131 | } while (q > start && | 131 | } while (q > start && |
132 | el.region_start < q[-1].region_start); | 132 | el.region_start < q[-1].region_start); |
133 | *q = el; | 133 | *q = el; |
134 | } | 134 | } |
135 | } | 135 | } |
136 | } | 136 | } |
137 | 137 | ||
138 | struct unwind_table * | 138 | struct unwind_table * |
139 | unwind_table_add(const char *name, unsigned long base_addr, | 139 | unwind_table_add(const char *name, unsigned long base_addr, |
140 | unsigned long gp, | 140 | unsigned long gp, |
141 | void *start, void *end) | 141 | void *start, void *end) |
142 | { | 142 | { |
143 | struct unwind_table *table; | 143 | struct unwind_table *table; |
144 | unsigned long flags; | 144 | unsigned long flags; |
145 | struct unwind_table_entry *s = (struct unwind_table_entry *)start; | 145 | struct unwind_table_entry *s = (struct unwind_table_entry *)start; |
146 | struct unwind_table_entry *e = (struct unwind_table_entry *)end; | 146 | struct unwind_table_entry *e = (struct unwind_table_entry *)end; |
147 | 147 | ||
148 | unwind_table_sort(s, e); | 148 | unwind_table_sort(s, e); |
149 | 149 | ||
150 | table = kmalloc(sizeof(struct unwind_table), GFP_USER); | 150 | table = kmalloc(sizeof(struct unwind_table), GFP_USER); |
151 | if (table == NULL) | 151 | if (table == NULL) |
152 | return NULL; | 152 | return NULL; |
153 | unwind_table_init(table, name, base_addr, gp, start, end); | 153 | unwind_table_init(table, name, base_addr, gp, start, end); |
154 | spin_lock_irqsave(&unwind_lock, flags); | 154 | spin_lock_irqsave(&unwind_lock, flags); |
155 | list_add_tail(&table->list, &unwind_tables); | 155 | list_add_tail(&table->list, &unwind_tables); |
156 | spin_unlock_irqrestore(&unwind_lock, flags); | 156 | spin_unlock_irqrestore(&unwind_lock, flags); |
157 | 157 | ||
158 | return table; | 158 | return table; |
159 | } | 159 | } |
160 | 160 | ||
161 | void unwind_table_remove(struct unwind_table *table) | 161 | void unwind_table_remove(struct unwind_table *table) |
162 | { | 162 | { |
163 | unsigned long flags; | 163 | unsigned long flags; |
164 | 164 | ||
165 | spin_lock_irqsave(&unwind_lock, flags); | 165 | spin_lock_irqsave(&unwind_lock, flags); |
166 | list_del(&table->list); | 166 | list_del(&table->list); |
167 | spin_unlock_irqrestore(&unwind_lock, flags); | 167 | spin_unlock_irqrestore(&unwind_lock, flags); |
168 | 168 | ||
169 | kfree(table); | 169 | kfree(table); |
170 | } | 170 | } |
171 | 171 | ||
172 | /* Called from setup_arch to import the kernel unwind info */ | 172 | /* Called from setup_arch to import the kernel unwind info */ |
173 | static int unwind_init(void) | 173 | int unwind_init(void) |
174 | { | 174 | { |
175 | long start, stop; | 175 | long start, stop; |
176 | register unsigned long gp __asm__ ("r27"); | 176 | register unsigned long gp __asm__ ("r27"); |
177 | 177 | ||
178 | start = (long)&__start___unwind[0]; | 178 | start = (long)&__start___unwind[0]; |
179 | stop = (long)&__stop___unwind[0]; | 179 | stop = (long)&__stop___unwind[0]; |
180 | 180 | ||
181 | spin_lock_init(&unwind_lock); | 181 | spin_lock_init(&unwind_lock); |
182 | 182 | ||
183 | printk("unwind_init: start = 0x%lx, end = 0x%lx, entries = %lu\n", | 183 | printk("unwind_init: start = 0x%lx, end = 0x%lx, entries = %lu\n", |
184 | start, stop, | 184 | start, stop, |
185 | (stop - start) / sizeof(struct unwind_table_entry)); | 185 | (stop - start) / sizeof(struct unwind_table_entry)); |
186 | 186 | ||
187 | unwind_table_init(&kernel_unwind_table, "kernel", KERNEL_START, | 187 | unwind_table_init(&kernel_unwind_table, "kernel", KERNEL_START, |
188 | gp, | 188 | gp, |
189 | &__start___unwind[0], &__stop___unwind[0]); | 189 | &__start___unwind[0], &__stop___unwind[0]); |
190 | #if 0 | 190 | #if 0 |
191 | { | 191 | { |
192 | int i; | 192 | int i; |
193 | for (i = 0; i < 10; i++) | 193 | for (i = 0; i < 10; i++) |
194 | { | 194 | { |
195 | printk("region 0x%x-0x%x\n", | 195 | printk("region 0x%x-0x%x\n", |
196 | __start___unwind[i].region_start, | 196 | __start___unwind[i].region_start, |
197 | __start___unwind[i].region_end); | 197 | __start___unwind[i].region_end); |
198 | } | 198 | } |
199 | } | 199 | } |
200 | #endif | 200 | #endif |
201 | return 0; | 201 | return 0; |
202 | } | 202 | } |
203 | 203 | ||
204 | #ifdef CONFIG_64BIT | 204 | #ifdef CONFIG_64BIT |
205 | #define get_func_addr(fptr) fptr[2] | 205 | #define get_func_addr(fptr) fptr[2] |
206 | #else | 206 | #else |
207 | #define get_func_addr(fptr) fptr[0] | 207 | #define get_func_addr(fptr) fptr[0] |
208 | #endif | 208 | #endif |
209 | 209 | ||
210 | static int unwind_special(struct unwind_frame_info *info, unsigned long pc, int frame_size) | 210 | static int unwind_special(struct unwind_frame_info *info, unsigned long pc, int frame_size) |
211 | { | 211 | { |
212 | extern void handle_interruption(int, struct pt_regs *); | 212 | extern void handle_interruption(int, struct pt_regs *); |
213 | static unsigned long *hi = (unsigned long *)&handle_interruption; | 213 | static unsigned long *hi = (unsigned long *)&handle_interruption; |
214 | 214 | ||
215 | if (pc == get_func_addr(hi)) { | 215 | if (pc == get_func_addr(hi)) { |
216 | struct pt_regs *regs = (struct pt_regs *)(info->sp - frame_size - PT_SZ_ALGN); | 216 | struct pt_regs *regs = (struct pt_regs *)(info->sp - frame_size - PT_SZ_ALGN); |
217 | dbg("Unwinding through handle_interruption()\n"); | 217 | dbg("Unwinding through handle_interruption()\n"); |
218 | info->prev_sp = regs->gr[30]; | 218 | info->prev_sp = regs->gr[30]; |
219 | info->prev_ip = regs->iaoq[0]; | 219 | info->prev_ip = regs->iaoq[0]; |
220 | 220 | ||
221 | return 1; | 221 | return 1; |
222 | } | 222 | } |
223 | 223 | ||
224 | return 0; | 224 | return 0; |
225 | } | 225 | } |
226 | 226 | ||
227 | static void unwind_frame_regs(struct unwind_frame_info *info) | 227 | static void unwind_frame_regs(struct unwind_frame_info *info) |
228 | { | 228 | { |
229 | const struct unwind_table_entry *e; | 229 | const struct unwind_table_entry *e; |
230 | unsigned long npc; | 230 | unsigned long npc; |
231 | unsigned int insn; | 231 | unsigned int insn; |
232 | long frame_size = 0; | 232 | long frame_size = 0; |
233 | int looking_for_rp, rpoffset = 0; | 233 | int looking_for_rp, rpoffset = 0; |
234 | 234 | ||
235 | e = find_unwind_entry(info->ip); | 235 | e = find_unwind_entry(info->ip); |
236 | if (e == NULL) { | 236 | if (e == NULL) { |
237 | unsigned long sp; | 237 | unsigned long sp; |
238 | extern char _stext[], _etext[]; | 238 | extern char _stext[], _etext[]; |
239 | 239 | ||
240 | dbg("Cannot find unwind entry for 0x%lx; forced unwinding\n", info->ip); | 240 | dbg("Cannot find unwind entry for 0x%lx; forced unwinding\n", info->ip); |
241 | 241 | ||
242 | #ifdef CONFIG_KALLSYMS | 242 | #ifdef CONFIG_KALLSYMS |
243 | /* Handle some frequent special cases.... */ | 243 | /* Handle some frequent special cases.... */ |
244 | { | 244 | { |
245 | char symname[KSYM_NAME_LEN]; | 245 | char symname[KSYM_NAME_LEN]; |
246 | char *modname; | 246 | char *modname; |
247 | 247 | ||
248 | kallsyms_lookup(info->ip, NULL, NULL, &modname, | 248 | kallsyms_lookup(info->ip, NULL, NULL, &modname, |
249 | symname); | 249 | symname); |
250 | 250 | ||
251 | dbg("info->ip = 0x%lx, name = %s\n", info->ip, symname); | 251 | dbg("info->ip = 0x%lx, name = %s\n", info->ip, symname); |
252 | 252 | ||
253 | if (strcmp(symname, "_switch_to_ret") == 0) { | 253 | if (strcmp(symname, "_switch_to_ret") == 0) { |
254 | info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE; | 254 | info->prev_sp = info->sp - CALLEE_SAVE_FRAME_SIZE; |
255 | info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET); | 255 | info->prev_ip = *(unsigned long *)(info->prev_sp - RP_OFFSET); |
256 | dbg("_switch_to_ret @ %lx - setting " | 256 | dbg("_switch_to_ret @ %lx - setting " |
257 | "prev_sp=%lx prev_ip=%lx\n", | 257 | "prev_sp=%lx prev_ip=%lx\n", |
258 | info->ip, info->prev_sp, | 258 | info->ip, info->prev_sp, |
259 | info->prev_ip); | 259 | info->prev_ip); |
260 | return; | 260 | return; |
261 | } else if (strcmp(symname, "ret_from_kernel_thread") == 0 || | 261 | } else if (strcmp(symname, "ret_from_kernel_thread") == 0 || |
262 | strcmp(symname, "syscall_exit") == 0) { | 262 | strcmp(symname, "syscall_exit") == 0) { |
263 | info->prev_ip = info->prev_sp = 0; | 263 | info->prev_ip = info->prev_sp = 0; |
264 | return; | 264 | return; |
265 | } | 265 | } |
266 | } | 266 | } |
267 | #endif | 267 | #endif |
268 | 268 | ||
269 | /* Since we are doing the unwinding blind, we don't know if | 269 | /* Since we are doing the unwinding blind, we don't know if |
270 | we are adjusting the stack correctly or extracting the rp | 270 | we are adjusting the stack correctly or extracting the rp |
271 | correctly. The rp is checked to see if it belongs to the | 271 | correctly. The rp is checked to see if it belongs to the |
272 | kernel text section, if not we assume we don't have a | 272 | kernel text section, if not we assume we don't have a |
273 | correct stack frame and we continue to unwind the stack. | 273 | correct stack frame and we continue to unwind the stack. |
274 | This is not quite correct, and will fail for loadable | 274 | This is not quite correct, and will fail for loadable |
275 | modules. */ | 275 | modules. */ |
276 | sp = info->sp & ~63; | 276 | sp = info->sp & ~63; |
277 | do { | 277 | do { |
278 | unsigned long tmp; | 278 | unsigned long tmp; |
279 | 279 | ||
280 | info->prev_sp = sp - 64; | 280 | info->prev_sp = sp - 64; |
281 | info->prev_ip = 0; | 281 | info->prev_ip = 0; |
282 | if (get_user(tmp, (unsigned long *)(info->prev_sp - RP_OFFSET))) | 282 | if (get_user(tmp, (unsigned long *)(info->prev_sp - RP_OFFSET))) |
283 | break; | 283 | break; |
284 | info->prev_ip = tmp; | 284 | info->prev_ip = tmp; |
285 | sp = info->prev_sp; | 285 | sp = info->prev_sp; |
286 | } while (info->prev_ip < (unsigned long)_stext || | 286 | } while (info->prev_ip < (unsigned long)_stext || |
287 | info->prev_ip > (unsigned long)_etext); | 287 | info->prev_ip > (unsigned long)_etext); |
288 | 288 | ||
289 | info->rp = 0; | 289 | info->rp = 0; |
290 | 290 | ||
291 | dbg("analyzing func @ %lx with no unwind info, setting " | 291 | dbg("analyzing func @ %lx with no unwind info, setting " |
292 | "prev_sp=%lx prev_ip=%lx\n", info->ip, | 292 | "prev_sp=%lx prev_ip=%lx\n", info->ip, |
293 | info->prev_sp, info->prev_ip); | 293 | info->prev_sp, info->prev_ip); |
294 | } else { | 294 | } else { |
295 | dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, " | 295 | dbg("e->start = 0x%x, e->end = 0x%x, Save_SP = %d, " |
296 | "Save_RP = %d, Millicode = %d size = %u\n", | 296 | "Save_RP = %d, Millicode = %d size = %u\n", |
297 | e->region_start, e->region_end, e->Save_SP, e->Save_RP, | 297 | e->region_start, e->region_end, e->Save_SP, e->Save_RP, |
298 | e->Millicode, e->Total_frame_size); | 298 | e->Millicode, e->Total_frame_size); |
299 | 299 | ||
300 | looking_for_rp = e->Save_RP; | 300 | looking_for_rp = e->Save_RP; |
301 | 301 | ||
302 | for (npc = e->region_start; | 302 | for (npc = e->region_start; |
303 | (frame_size < (e->Total_frame_size << 3) || | 303 | (frame_size < (e->Total_frame_size << 3) || |
304 | looking_for_rp) && | 304 | looking_for_rp) && |
305 | npc < info->ip; | 305 | npc < info->ip; |
306 | npc += 4) { | 306 | npc += 4) { |
307 | 307 | ||
308 | insn = *(unsigned int *)npc; | 308 | insn = *(unsigned int *)npc; |
309 | 309 | ||
310 | if ((insn & 0xffffc000) == 0x37de0000 || | 310 | if ((insn & 0xffffc000) == 0x37de0000 || |
311 | (insn & 0xffe00000) == 0x6fc00000) { | 311 | (insn & 0xffe00000) == 0x6fc00000) { |
312 | /* ldo X(sp), sp, or stwm X,D(sp) */ | 312 | /* ldo X(sp), sp, or stwm X,D(sp) */ |
313 | frame_size += (insn & 0x1 ? -1 << 13 : 0) | | 313 | frame_size += (insn & 0x1 ? -1 << 13 : 0) | |
314 | ((insn & 0x3fff) >> 1); | 314 | ((insn & 0x3fff) >> 1); |
315 | dbg("analyzing func @ %lx, insn=%08x @ " | 315 | dbg("analyzing func @ %lx, insn=%08x @ " |
316 | "%lx, frame_size = %ld\n", info->ip, | 316 | "%lx, frame_size = %ld\n", info->ip, |
317 | insn, npc, frame_size); | 317 | insn, npc, frame_size); |
318 | } else if ((insn & 0xffe00008) == 0x73c00008) { | 318 | } else if ((insn & 0xffe00008) == 0x73c00008) { |
319 | /* std,ma X,D(sp) */ | 319 | /* std,ma X,D(sp) */ |
320 | frame_size += (insn & 0x1 ? -1 << 13 : 0) | | 320 | frame_size += (insn & 0x1 ? -1 << 13 : 0) | |
321 | (((insn >> 4) & 0x3ff) << 3); | 321 | (((insn >> 4) & 0x3ff) << 3); |
322 | dbg("analyzing func @ %lx, insn=%08x @ " | 322 | dbg("analyzing func @ %lx, insn=%08x @ " |
323 | "%lx, frame_size = %ld\n", info->ip, | 323 | "%lx, frame_size = %ld\n", info->ip, |
324 | insn, npc, frame_size); | 324 | insn, npc, frame_size); |
325 | } else if (insn == 0x6bc23fd9) { | 325 | } else if (insn == 0x6bc23fd9) { |
326 | /* stw rp,-20(sp) */ | 326 | /* stw rp,-20(sp) */ |
327 | rpoffset = 20; | 327 | rpoffset = 20; |
328 | looking_for_rp = 0; | 328 | looking_for_rp = 0; |
329 | dbg("analyzing func @ %lx, insn=stw rp," | 329 | dbg("analyzing func @ %lx, insn=stw rp," |
330 | "-20(sp) @ %lx\n", info->ip, npc); | 330 | "-20(sp) @ %lx\n", info->ip, npc); |
331 | } else if (insn == 0x0fc212c1) { | 331 | } else if (insn == 0x0fc212c1) { |
332 | /* std rp,-16(sr0,sp) */ | 332 | /* std rp,-16(sr0,sp) */ |
333 | rpoffset = 16; | 333 | rpoffset = 16; |
334 | looking_for_rp = 0; | 334 | looking_for_rp = 0; |
335 | dbg("analyzing func @ %lx, insn=std rp," | 335 | dbg("analyzing func @ %lx, insn=std rp," |
336 | "-16(sp) @ %lx\n", info->ip, npc); | 336 | "-16(sp) @ %lx\n", info->ip, npc); |
337 | } | 337 | } |
338 | } | 338 | } |
339 | 339 | ||
340 | if (!unwind_special(info, e->region_start, frame_size)) { | 340 | if (!unwind_special(info, e->region_start, frame_size)) { |
341 | info->prev_sp = info->sp - frame_size; | 341 | info->prev_sp = info->sp - frame_size; |
342 | if (e->Millicode) | 342 | if (e->Millicode) |
343 | info->rp = info->r31; | 343 | info->rp = info->r31; |
344 | else if (rpoffset) | 344 | else if (rpoffset) |
345 | info->rp = *(unsigned long *)(info->prev_sp - rpoffset); | 345 | info->rp = *(unsigned long *)(info->prev_sp - rpoffset); |
346 | info->prev_ip = info->rp; | 346 | info->prev_ip = info->rp; |
347 | info->rp = 0; | 347 | info->rp = 0; |
348 | } | 348 | } |
349 | 349 | ||
350 | dbg("analyzing func @ %lx, setting prev_sp=%lx " | 350 | dbg("analyzing func @ %lx, setting prev_sp=%lx " |
351 | "prev_ip=%lx npc=%lx\n", info->ip, info->prev_sp, | 351 | "prev_ip=%lx npc=%lx\n", info->ip, info->prev_sp, |
352 | info->prev_ip, npc); | 352 | info->prev_ip, npc); |
353 | } | 353 | } |
354 | } | 354 | } |
355 | 355 | ||
356 | void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t, | 356 | void unwind_frame_init(struct unwind_frame_info *info, struct task_struct *t, |
357 | struct pt_regs *regs) | 357 | struct pt_regs *regs) |
358 | { | 358 | { |
359 | memset(info, 0, sizeof(struct unwind_frame_info)); | 359 | memset(info, 0, sizeof(struct unwind_frame_info)); |
360 | info->t = t; | 360 | info->t = t; |
361 | info->sp = regs->gr[30]; | 361 | info->sp = regs->gr[30]; |
362 | info->ip = regs->iaoq[0]; | 362 | info->ip = regs->iaoq[0]; |
363 | info->rp = regs->gr[2]; | 363 | info->rp = regs->gr[2]; |
364 | info->r31 = regs->gr[31]; | 364 | info->r31 = regs->gr[31]; |
365 | 365 | ||
366 | dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n", | 366 | dbg("(%d) Start unwind from sp=%08lx ip=%08lx\n", |
367 | t ? (int)t->pid : -1, info->sp, info->ip); | 367 | t ? (int)t->pid : -1, info->sp, info->ip); |
368 | } | 368 | } |
369 | 369 | ||
370 | void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t) | 370 | void unwind_frame_init_from_blocked_task(struct unwind_frame_info *info, struct task_struct *t) |
371 | { | 371 | { |
372 | struct pt_regs *r = &t->thread.regs; | 372 | struct pt_regs *r = &t->thread.regs; |
373 | struct pt_regs *r2; | 373 | struct pt_regs *r2; |
374 | 374 | ||
375 | r2 = kmalloc(sizeof(struct pt_regs), GFP_KERNEL); | 375 | r2 = kmalloc(sizeof(struct pt_regs), GFP_KERNEL); |
376 | if (!r2) | 376 | if (!r2) |
377 | return; | 377 | return; |
378 | *r2 = *r; | 378 | *r2 = *r; |
379 | r2->gr[30] = r->ksp; | 379 | r2->gr[30] = r->ksp; |
380 | r2->iaoq[0] = r->kpc; | 380 | r2->iaoq[0] = r->kpc; |
381 | unwind_frame_init(info, t, r2); | 381 | unwind_frame_init(info, t, r2); |
382 | kfree(r2); | 382 | kfree(r2); |
383 | } | 383 | } |
384 | 384 | ||
385 | void unwind_frame_init_running(struct unwind_frame_info *info, struct pt_regs *regs) | 385 | void unwind_frame_init_running(struct unwind_frame_info *info, struct pt_regs *regs) |
386 | { | 386 | { |
387 | unwind_frame_init(info, current, regs); | 387 | unwind_frame_init(info, current, regs); |
388 | } | 388 | } |
389 | 389 | ||
390 | int unwind_once(struct unwind_frame_info *next_frame) | 390 | int unwind_once(struct unwind_frame_info *next_frame) |
391 | { | 391 | { |
392 | unwind_frame_regs(next_frame); | 392 | unwind_frame_regs(next_frame); |
393 | 393 | ||
394 | if (next_frame->prev_sp == 0 || | 394 | if (next_frame->prev_sp == 0 || |
395 | next_frame->prev_ip == 0) | 395 | next_frame->prev_ip == 0) |
396 | return -1; | 396 | return -1; |
397 | 397 | ||
398 | next_frame->sp = next_frame->prev_sp; | 398 | next_frame->sp = next_frame->prev_sp; |
399 | next_frame->ip = next_frame->prev_ip; | 399 | next_frame->ip = next_frame->prev_ip; |
400 | next_frame->prev_sp = 0; | 400 | next_frame->prev_sp = 0; |
401 | next_frame->prev_ip = 0; | 401 | next_frame->prev_ip = 0; |
402 | 402 | ||
403 | dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n", | 403 | dbg("(%d) Continue unwind to sp=%08lx ip=%08lx\n", |
404 | next_frame->t ? (int)next_frame->t->pid : -1, | 404 | next_frame->t ? (int)next_frame->t->pid : -1, |
405 | next_frame->sp, next_frame->ip); | 405 | next_frame->sp, next_frame->ip); |
406 | 406 | ||
407 | return 0; | 407 | return 0; |
408 | } | 408 | } |
409 | 409 | ||
410 | int unwind_to_user(struct unwind_frame_info *info) | 410 | int unwind_to_user(struct unwind_frame_info *info) |
411 | { | 411 | { |
412 | int ret; | 412 | int ret; |
413 | 413 | ||
414 | do { | 414 | do { |
415 | ret = unwind_once(info); | 415 | ret = unwind_once(info); |
416 | } while (!ret && !(info->ip & 3)); | 416 | } while (!ret && !(info->ip & 3)); |
417 | 417 | ||
418 | return ret; | 418 | return ret; |
419 | } | 419 | } |
420 | |||
421 | module_init(unwind_init); | ||
422 | 420 |