patch-2_6_7-vs1_9_1_12
[linux-2.6.git] / arch / s390 / kernel / setup.c
1 /*
2  *  arch/s390/kernel/setup.c
3  *
4  *  S390 version
5  *    Copyright (C) 1999,2000 IBM Deutschland Entwicklung GmbH, IBM Corporation
6  *    Author(s): Hartmut Penner (hp@de.ibm.com),
7  *               Martin Schwidefsky (schwidefsky@de.ibm.com)
8  *
9  *  Derived from "arch/i386/kernel/setup.c"
10  *    Copyright (C) 1995, Linus Torvalds
11  */
12
13 /*
14  * This file handles the architecture-dependent parts of initialization
15  */
16
17 #include <linux/errno.h>
18 #include <linux/module.h>
19 #include <linux/sched.h>
20 #include <linux/kernel.h>
21 #include <linux/mm.h>
22 #include <linux/stddef.h>
23 #include <linux/unistd.h>
24 #include <linux/ptrace.h>
25 #include <linux/slab.h>
26 #include <linux/user.h>
27 #include <linux/a.out.h>
28 #include <linux/tty.h>
29 #include <linux/ioport.h>
30 #include <linux/delay.h>
31 #include <linux/config.h>
32 #include <linux/init.h>
33 #include <linux/initrd.h>
34 #include <linux/bootmem.h>
35 #include <linux/root_dev.h>
36 #include <linux/console.h>
37 #include <linux/seq_file.h>
38 #include <linux/kernel_stat.h>
39
40 #include <asm/uaccess.h>
41 #include <asm/system.h>
42 #include <asm/smp.h>
43 #include <asm/mmu_context.h>
44 #include <asm/cpcmd.h>
45 #include <asm/lowcore.h>
46 #include <asm/irq.h>
47
48 /*
49  * Machine setup..
50  */
51 unsigned int console_mode = 0;
52 unsigned int console_device = -1;
53 unsigned int console_irq = -1;
54 unsigned long memory_size = 0;
55 unsigned long machine_flags = 0;
56 struct {
57         unsigned long addr, size, type;
58 } memory_chunk[MEMORY_CHUNKS] = { { 0 } };
59 #define CHUNK_READ_WRITE 0
60 #define CHUNK_READ_ONLY 1
61 int cpus_initialized = 0;
62 static cpumask_t cpu_initialized;
63 volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */
64
65 /*
66  * Setup options
67  */
68 extern int _text,_etext, _edata, _end;
69
70 /*
71  * This is set up by the setup-routine at boot-time
72  * for S390 need to find out, what we have to setup
73  * using address 0x10400 ...
74  */
75
76 #include <asm/setup.h>
77
78 static char command_line[COMMAND_LINE_SIZE] = { 0, };
79        char saved_command_line[COMMAND_LINE_SIZE];
80
81 static struct resource code_resource = { "Kernel code", 0x100000, 0 };
82 static struct resource data_resource = { "Kernel data", 0, 0 };
83
84 /*
85  * cpu_init() initializes state that is per-CPU.
86  */
87 void __devinit cpu_init (void)
88 {
89         int nr = smp_processor_id();
90         int addr = hard_smp_processor_id();
91
92         if (cpu_test_and_set(nr,cpu_initialized)) {
93                 printk("CPU#%d ALREADY INITIALIZED!!!!!!!!!\n", nr);
94                 for (;;) local_irq_enable();
95         }
96         cpus_initialized++;
97
98         /*
99          * Store processor id in lowcore (used e.g. in timer_interrupt)
100          */
101         asm volatile ("stidp %0": "=m" (S390_lowcore.cpu_data.cpu_id));
102         S390_lowcore.cpu_data.cpu_addr = addr;
103
104         /*
105          * Force FPU initialization:
106          */
107         clear_thread_flag(TIF_USEDFPU);
108         current->used_math = 0;
109
110         /* Setup active_mm for idle_task  */
111         atomic_inc(&init_mm.mm_count);
112         current->active_mm = &init_mm;
113         if (current->mm)
114                 BUG();
115         enter_lazy_tlb(&init_mm, current);
116 }
117
118 /*
119  * VM halt and poweroff setup routines
120  */
121 char vmhalt_cmd[128] = "";
122 char vmpoff_cmd[128] = "";
123
124 static inline void strncpy_skip_quote(char *dst, char *src, int n)
125 {
126         int sx, dx;
127
128         dx = 0;
129         for (sx = 0; src[sx] != 0; sx++) {
130                 if (src[sx] == '"') continue;
131                 dst[dx++] = src[sx];
132                 if (dx >= n) break;
133         }
134 }
135
136 static int __init vmhalt_setup(char *str)
137 {
138         strncpy_skip_quote(vmhalt_cmd, str, 127);
139         vmhalt_cmd[127] = 0;
140         return 1;
141 }
142
143 __setup("vmhalt=", vmhalt_setup);
144
145 static int __init vmpoff_setup(char *str)
146 {
147         strncpy_skip_quote(vmpoff_cmd, str, 127);
148         vmpoff_cmd[127] = 0;
149         return 1;
150 }
151
152 __setup("vmpoff=", vmpoff_setup);
153
154 /*
155  * condev= and conmode= setup parameter.
156  */
157
158 static int __init condev_setup(char *str)
159 {
160         int vdev;
161
162         vdev = simple_strtoul(str, &str, 0);
163         if (vdev >= 0 && vdev < 65536) {
164                 console_device = vdev;
165                 console_irq = -1;
166         }
167         return 1;
168 }
169
170 __setup("condev=", condev_setup);
171
172 static int __init conmode_setup(char *str)
173 {
174 #if defined(CONFIG_SCLP_CONSOLE)
175         if (strncmp(str, "hwc", 4) == 0 || strncmp(str, "sclp", 5) == 0)
176                 SET_CONSOLE_SCLP;
177 #endif
178 #if defined(CONFIG_TN3215_CONSOLE)
179         if (strncmp(str, "3215", 5) == 0)
180                 SET_CONSOLE_3215;
181 #endif
182 #if defined(CONFIG_TN3270_CONSOLE)
183         if (strncmp(str, "3270", 5) == 0)
184                 SET_CONSOLE_3270;
185 #endif
186         return 1;
187 }
188
189 __setup("conmode=", conmode_setup);
190
191 static void __init conmode_default(void)
192 {
193         char query_buffer[1024];
194         char *ptr;
195
196         if (MACHINE_IS_VM) {
197                 cpcmd("QUERY CONSOLE", query_buffer, 1024);
198                 console_device = simple_strtoul(query_buffer + 5, NULL, 16);
199                 ptr = strstr(query_buffer, "SUBCHANNEL =");
200                 console_irq = simple_strtoul(ptr + 13, NULL, 16);
201                 cpcmd("QUERY TERM", query_buffer, 1024);
202                 ptr = strstr(query_buffer, "CONMODE");
203                 /*
204                  * Set the conmode to 3215 so that the device recognition 
205                  * will set the cu_type of the console to 3215. If the
206                  * conmode is 3270 and we don't set it back then both
207                  * 3215 and the 3270 driver will try to access the console
208                  * device (3215 as console and 3270 as normal tty).
209                  */
210                 cpcmd("TERM CONMODE 3215", NULL, 0);
211                 if (ptr == NULL) {
212 #if defined(CONFIG_SCLP_CONSOLE)
213                         SET_CONSOLE_SCLP;
214 #endif
215                         return;
216                 }
217                 if (strncmp(ptr + 8, "3270", 4) == 0) {
218 #if defined(CONFIG_TN3270_CONSOLE)
219                         SET_CONSOLE_3270;
220 #elif defined(CONFIG_TN3215_CONSOLE)
221                         SET_CONSOLE_3215;
222 #elif defined(CONFIG_SCLP_CONSOLE)
223                         SET_CONSOLE_SCLP;
224 #endif
225                 } else if (strncmp(ptr + 8, "3215", 4) == 0) {
226 #if defined(CONFIG_TN3215_CONSOLE)
227                         SET_CONSOLE_3215;
228 #elif defined(CONFIG_TN3270_CONSOLE)
229                         SET_CONSOLE_3270;
230 #elif defined(CONFIG_SCLP_CONSOLE)
231                         SET_CONSOLE_SCLP;
232 #endif
233                 }
234         } else if (MACHINE_IS_P390) {
235 #if defined(CONFIG_TN3215_CONSOLE)
236                 SET_CONSOLE_3215;
237 #elif defined(CONFIG_TN3270_CONSOLE)
238                 SET_CONSOLE_3270;
239 #endif
240         } else {
241 #if defined(CONFIG_SCLP_CONSOLE)
242                 SET_CONSOLE_SCLP;
243 #endif
244         }
245 }
246
247 #ifdef CONFIG_SMP
248 extern void machine_restart_smp(char *);
249 extern void machine_halt_smp(void);
250 extern void machine_power_off_smp(void);
251
252 void (*_machine_restart)(char *command) = machine_restart_smp;
253 void (*_machine_halt)(void) = machine_halt_smp;
254 void (*_machine_power_off)(void) = machine_power_off_smp;
255 #else
256 /*
257  * Reboot, halt and power_off routines for non SMP.
258  */
259 extern void reipl(unsigned long devno);
260 static void do_machine_restart_nonsmp(char * __unused)
261 {
262         if (MACHINE_IS_VM)
263                 cpcmd ("IPL", NULL, 0);
264         else
265                 reipl (0x10000 | S390_lowcore.ipl_device);
266 }
267
268 static void do_machine_halt_nonsmp(void)
269 {
270         if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0)
271                 cpcmd(vmhalt_cmd, NULL, 0);
272         signal_processor(smp_processor_id(), sigp_stop_and_store_status);
273 }
274
275 static void do_machine_power_off_nonsmp(void)
276 {
277         if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0)
278                 cpcmd(vmpoff_cmd, NULL, 0);
279         signal_processor(smp_processor_id(), sigp_stop_and_store_status);
280 }
281
282 void (*_machine_restart)(char *command) = do_machine_restart_nonsmp;
283 void (*_machine_halt)(void) = do_machine_halt_nonsmp;
284 void (*_machine_power_off)(void) = do_machine_power_off_nonsmp;
285 #endif
286
287  /*
288  * Reboot, halt and power_off stubs. They just call _machine_restart,
289  * _machine_halt or _machine_power_off. 
290  */
291
292 void machine_restart(char *command)
293 {
294         console_unblank();
295         _machine_restart(command);
296 }
297
298 EXPORT_SYMBOL(machine_restart);
299
300 void machine_halt(void)
301 {
302         console_unblank();
303         _machine_halt();
304 }
305
306 EXPORT_SYMBOL(machine_halt);
307
308 void machine_power_off(void)
309 {
310         console_unblank();
311         _machine_power_off();
312 }
313
314 EXPORT_SYMBOL(machine_power_off);
315
316 /*
317  * Setup function called from init/main.c just after the banner
318  * was printed.
319  */
320 extern char _pstart, _pend, _stext;
321
322 void __init setup_arch(char **cmdline_p)
323 {
324         unsigned long bootmap_size;
325         unsigned long memory_start, memory_end;
326         char c = ' ', cn, *to = command_line, *from = COMMAND_LINE;
327         struct resource *res;
328         unsigned long start_pfn, end_pfn;
329         static unsigned int smptrap=0;
330         unsigned long delay = 0;
331         struct _lowcore *lc;
332         int i;
333
334         if (smptrap)
335                 return;
336         smptrap=1;
337
338         /*
339          * print what head.S has found out about the machine 
340          */
341 #ifndef CONFIG_ARCH_S390X
342         printk((MACHINE_IS_VM) ?
343                "We are running under VM (31 bit mode)\n" :
344                "We are running native (31 bit mode)\n");
345         printk((MACHINE_HAS_IEEE) ?
346                "This machine has an IEEE fpu\n" :
347                "This machine has no IEEE fpu\n");
348 #else /* CONFIG_ARCH_S390X */
349         printk((MACHINE_IS_VM) ?
350                "We are running under VM (64 bit mode)\n" :
351                "We are running native (64 bit mode)\n");
352 #endif /* CONFIG_ARCH_S390X */
353
354         ROOT_DEV = Root_RAM0;
355         memory_start = (unsigned long) &_end;    /* fixit if use $CODELO etc*/
356 #ifndef CONFIG_ARCH_S390X
357         memory_end = memory_size & ~0x400000UL;  /* align memory end to 4MB */
358         /*
359          * We need some free virtual space to be able to do vmalloc.
360          * On a machine with 2GB memory we make sure that we have at
361          * least 128 MB free space for vmalloc.
362          */
363         if (memory_end > 1920*1024*1024)
364                 memory_end = 1920*1024*1024;
365 #else /* CONFIG_ARCH_S390X */
366         memory_end = memory_size & ~0x200000UL;  /* detected in head.s */
367 #endif /* CONFIG_ARCH_S390X */
368         init_mm.start_code = PAGE_OFFSET;
369         init_mm.end_code = (unsigned long) &_etext;
370         init_mm.end_data = (unsigned long) &_edata;
371         init_mm.brk = (unsigned long) &_end;
372
373         code_resource.start = (unsigned long) &_text;
374         code_resource.end = (unsigned long) &_etext - 1;
375         data_resource.start = (unsigned long) &_etext;
376         data_resource.end = (unsigned long) &_edata - 1;
377
378         /* Save unparsed command line copy for /proc/cmdline */
379         memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE);
380         saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
381
382         for (;;) {
383                 /*
384                  * "mem=XXX[kKmM]" sets memsize 
385                  */
386                 if (c == ' ' && strncmp(from, "mem=", 4) == 0) {
387                         memory_end = simple_strtoul(from+4, &from, 0);
388                         if ( *from == 'K' || *from == 'k' ) {
389                                 memory_end = memory_end << 10;
390                                 from++;
391                         } else if ( *from == 'M' || *from == 'm' ) {
392                                 memory_end = memory_end << 20;
393                                 from++;
394                         }
395                 }
396                 /*
397                  * "ipldelay=XXX[sm]" sets ipl delay in seconds or minutes
398                  */
399                 if (c == ' ' && strncmp(from, "ipldelay=", 9) == 0) {
400                         delay = simple_strtoul(from+9, &from, 0);
401                         if (*from == 's' || *from == 'S') {
402                                 delay = delay*1000000;
403                                 from++;
404                         } else if (*from == 'm' || *from == 'M') {
405                                 delay = delay*60*1000000;
406                                 from++;
407                         }
408                         /* now wait for the requested amount of time */
409                         udelay(delay);
410                 }
411                 cn = *(from++);
412                 if (!cn)
413                         break;
414                 if (cn == '\n')
415                         cn = ' ';  /* replace newlines with space */
416                 if (cn == 0x0d)
417                         cn = ' ';  /* replace 0x0d with space */
418                 if (cn == ' ' && c == ' ')
419                         continue;  /* remove additional spaces */
420                 c = cn;
421                 if (to - command_line >= COMMAND_LINE_SIZE)
422                         break;
423                 *(to++) = c;
424         }
425         if (c == ' ' && to > command_line) to--;
426         *to = '\0';
427         *cmdline_p = command_line;
428
429         /*
430          * partially used pages are not usable - thus
431          * we are rounding upwards:
432          */
433         start_pfn = (__pa(&_end) + PAGE_SIZE - 1) >> PAGE_SHIFT;
434         end_pfn = max_pfn = memory_end >> PAGE_SHIFT;
435
436         /*
437          * Initialize the boot-time allocator (with low memory only):
438          */
439         bootmap_size = init_bootmem(start_pfn, end_pfn);
440
441         /*
442          * Register RAM areas with the bootmem allocator.
443          */
444         for (i = 0; i < 16 && memory_chunk[i].size > 0; i++) {
445                 unsigned long start_chunk, end_chunk;
446
447                 if (memory_chunk[i].type != CHUNK_READ_WRITE)
448                         continue;
449                 start_chunk = (memory_chunk[i].addr + PAGE_SIZE - 1);
450                 start_chunk >>= PAGE_SHIFT;
451                 end_chunk = (memory_chunk[i].addr + memory_chunk[i].size);
452                 end_chunk >>= PAGE_SHIFT;
453                 if (start_chunk < start_pfn)
454                         start_chunk = start_pfn;
455                 if (end_chunk > end_pfn)
456                         end_chunk = end_pfn;
457                 if (start_chunk < end_chunk)
458                         free_bootmem(start_chunk << PAGE_SHIFT,
459                                      (end_chunk - start_chunk) << PAGE_SHIFT);
460         }
461
462         /*
463          * Reserve the bootmem bitmap itself as well. We do this in two
464          * steps (first step was init_bootmem()) because this catches
465          * the (very unlikely) case of us accidentally initializing the
466          * bootmem allocator with an invalid RAM area.
467          */
468         reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size);
469
470 #ifdef CONFIG_BLK_DEV_INITRD
471         if (INITRD_START) {
472                 if (INITRD_START + INITRD_SIZE <= memory_end) {
473                         reserve_bootmem(INITRD_START, INITRD_SIZE);
474                         initrd_start = INITRD_START;
475                         initrd_end = initrd_start + INITRD_SIZE;
476                 } else {
477                         printk("initrd extends beyond end of memory "
478                                "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
479                                initrd_start + INITRD_SIZE, memory_end);
480                         initrd_start = initrd_end = 0;
481                 }
482         }
483 #endif
484
485         /*
486          * Setup lowcore for boot cpu
487          */
488 #ifndef CONFIG_ARCH_S390X
489         lc = (struct _lowcore *) __alloc_bootmem(PAGE_SIZE, PAGE_SIZE, 0);
490         memset(lc, 0, PAGE_SIZE);
491 #else /* CONFIG_ARCH_S390X */
492         lc = (struct _lowcore *) __alloc_bootmem(2*PAGE_SIZE, 2*PAGE_SIZE, 0);
493         memset(lc, 0, 2*PAGE_SIZE);
494 #endif /* CONFIG_ARCH_S390X */
495         lc->restart_psw.mask = PSW_BASE_BITS;
496         lc->restart_psw.addr =
497                 PSW_ADDR_AMODE | (unsigned long) restart_int_handler;
498         lc->external_new_psw.mask = PSW_KERNEL_BITS;
499         lc->external_new_psw.addr =
500                 PSW_ADDR_AMODE | (unsigned long) ext_int_handler;
501         lc->svc_new_psw.mask = PSW_KERNEL_BITS | PSW_MASK_IO | PSW_MASK_EXT;
502         lc->svc_new_psw.addr = PSW_ADDR_AMODE | (unsigned long) system_call;
503         lc->program_new_psw.mask = PSW_KERNEL_BITS;
504         lc->program_new_psw.addr =
505                 PSW_ADDR_AMODE | (unsigned long)pgm_check_handler;
506         lc->mcck_new_psw.mask = PSW_KERNEL_BITS;
507         lc->mcck_new_psw.addr =
508                 PSW_ADDR_AMODE | (unsigned long) mcck_int_handler;
509         lc->io_new_psw.mask = PSW_KERNEL_BITS;
510         lc->io_new_psw.addr = PSW_ADDR_AMODE | (unsigned long) io_int_handler;
511         lc->ipl_device = S390_lowcore.ipl_device;
512         lc->jiffy_timer = -1LL;
513         lc->kernel_stack = ((unsigned long) &init_thread_union) + THREAD_SIZE;
514         lc->async_stack = (unsigned long)
515                 __alloc_bootmem(ASYNC_SIZE, ASYNC_SIZE, 0) + ASYNC_SIZE;
516         lc->current_task = (unsigned long) init_thread_union.thread_info.task;
517         lc->thread_info = (unsigned long) &init_thread_union;
518 #ifdef CONFIG_ARCH_S390X
519         if (MACHINE_HAS_DIAG44)
520                 lc->diag44_opcode = 0x83000044;
521         else
522                 lc->diag44_opcode = 0x07000700;
523 #endif /* CONFIG_ARCH_S390X */
524         set_prefix((u32)(unsigned long) lc);
525         cpu_init();
526         __cpu_logical_map[0] = S390_lowcore.cpu_data.cpu_addr;
527
528         /*
529          * Create kernel page tables and switch to virtual addressing.
530          */
531         paging_init();
532
533         res = alloc_bootmem_low(sizeof(struct resource));
534         res->start = 0;
535         res->end = memory_end;
536         res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
537         request_resource(&iomem_resource, res);
538         request_resource(res, &code_resource);
539         request_resource(res, &data_resource);
540
541         /* Setup default console */
542         conmode_default();
543 }
544
545 void print_cpu_info(struct cpuinfo_S390 *cpuinfo)
546 {
547    printk("cpu %d "
548 #ifdef CONFIG_SMP
549            "phys_idx=%d "
550 #endif
551            "vers=%02X ident=%06X machine=%04X unused=%04X\n",
552            cpuinfo->cpu_nr,
553 #ifdef CONFIG_SMP
554            cpuinfo->cpu_addr,
555 #endif
556            cpuinfo->cpu_id.version,
557            cpuinfo->cpu_id.ident,
558            cpuinfo->cpu_id.machine,
559            cpuinfo->cpu_id.unused);
560 }
561
562 /*
563  * show_cpuinfo - Get information on one CPU for use by procfs.
564  */
565
566 static int show_cpuinfo(struct seq_file *m, void *v)
567 {
568         struct cpuinfo_S390 *cpuinfo;
569         unsigned long n = (unsigned long) v - 1;
570
571         if (!n) {
572                 seq_printf(m, "vendor_id       : IBM/S390\n"
573                                "# processors    : %i\n"
574                                "bogomips per cpu: %lu.%02lu\n",
575                                num_online_cpus(), loops_per_jiffy/(500000/HZ),
576                                (loops_per_jiffy/(5000/HZ))%100);
577         }
578         if (cpu_online(n)) {
579 #ifdef CONFIG_SMP
580                 if (smp_processor_id() == n)
581                         cpuinfo = &S390_lowcore.cpu_data;
582                 else
583                         cpuinfo = &lowcore_ptr[n]->cpu_data;
584 #else
585                 cpuinfo = &S390_lowcore.cpu_data;
586 #endif
587                 seq_printf(m, "processor %li: "
588                                "version = %02X,  "
589                                "identification = %06X,  "
590                                "machine = %04X\n",
591                                n, cpuinfo->cpu_id.version,
592                                cpuinfo->cpu_id.ident,
593                                cpuinfo->cpu_id.machine);
594         }
595         return 0;
596 }
597
598 static void *c_start(struct seq_file *m, loff_t *pos)
599 {
600         return *pos < NR_CPUS ? (void *)((unsigned long) *pos + 1) : NULL;
601 }
602 static void *c_next(struct seq_file *m, void *v, loff_t *pos)
603 {
604         ++*pos;
605         return c_start(m, pos);
606 }
607 static void c_stop(struct seq_file *m, void *v)
608 {
609 }
610 struct seq_operations cpuinfo_op = {
611         .start  = c_start,
612         .next   = c_next,
613         .stop   = c_stop,
614         .show   = show_cpuinfo,
615 };
616
617 /*
618  * show_interrupts is needed by /proc/interrupts.
619  */
620
621 static const char *intrclass_names[] = {
622         "EXT",
623         "I/O",
624 };
625
626 int show_interrupts(struct seq_file *p, void *v)
627 {
628         int i = *(loff_t *) v, j;
629         
630         if (i == 0) {
631                 seq_puts(p, "           ");
632                 for (j=0; j<NR_CPUS; j++)
633                         if (cpu_online(j))
634                                 seq_printf(p, "CPU%d       ",j);
635                 seq_putc(p, '\n');
636         }
637         
638         if (i < NR_IRQS) {
639                 seq_printf(p, "%s: ", intrclass_names[i]);
640 #ifndef CONFIG_SMP
641                 seq_printf(p, "%10u ", kstat_irqs(i));
642 #else
643                 for (j = 0; j < NR_CPUS; j++)
644                         if (cpu_online(j))
645                                 seq_printf(p, "%10u ", kstat_cpu(j).irqs[i]);
646 #endif
647                 seq_putc(p, '\n');
648                 
649         }
650         
651         return 0;
652 }