File: /usr/src/linux/arch/s390x/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/sched.h>
19     #include <linux/kernel.h>
20     #include <linux/mm.h>
21     #include <linux/stddef.h>
22     #include <linux/unistd.h>
23     #include <linux/ptrace.h>
24     #include <linux/slab.h>
25     #include <linux/user.h>
26     #include <linux/a.out.h>
27     #include <linux/tty.h>
28     #include <linux/ioport.h>
29     #include <linux/delay.h>
30     #include <linux/config.h>
31     #include <linux/init.h>
32     #ifdef CONFIG_BLK_DEV_RAM
33     #include <linux/blk.h>
34     #endif
35     #include <linux/bootmem.h>
36     #include <linux/console.h>
37     #include <asm/uaccess.h>
38     #include <asm/system.h>
39     #include <asm/smp.h>
40     #include <asm/mmu_context.h>
41     #include <asm/cpcmd.h>
42     
43     /*
44      * Machine setup..
45      */
46     unsigned int console_mode = 0;
47     unsigned int console_device = -1;
48     unsigned long memory_size = 0;
49     unsigned long machine_flags = 0;
50     __u16 boot_cpu_addr;
51     int cpus_initialized = 0;
52     unsigned long cpu_initialized = 0;
53     volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */
54     
55     /*
56      * Setup options
57      */
58     extern int _text,_etext, _edata, _end;
59     
60     /*
61      * This is set up by the setup-routine at boot-time
62      * for S390 need to find out, what we have to setup
63      * using address 0x10400 ...
64      */
65     
66     #include <asm/setup.h>
67     
68     static char command_line[COMMAND_LINE_SIZE] = { 0, };
69            char saved_command_line[COMMAND_LINE_SIZE];
70     
71     static struct resource code_resource = { "Kernel code", 0x100000, 0 };
72     static struct resource data_resource = { "Kernel data", 0, 0 };
73     
74     /*
75      * cpu_init() initializes state that is per-CPU.
76      */
77     void __init cpu_init (void)
78     {
79             int nr = smp_processor_id();
80             int addr = hard_smp_processor_id();
81     
82             if (test_and_set_bit(nr,&cpu_initialized)) {
83                     printk("CPU#%d ALREADY INITIALIZED!!!!!!!!!\n", nr);
84                     for (;;) __sti();
85             }
86             cpus_initialized++;
87     
88             /*
89              * Store processor id in lowcore (used e.g. in timer_interrupt)
90              */
91             asm volatile ("stidp %0": "=m" (S390_lowcore.cpu_data.cpu_id));
92             S390_lowcore.cpu_data.cpu_addr = addr;
93             S390_lowcore.cpu_data.cpu_nr = nr;
94     
95             /*
96              * Force FPU initialization:
97              */
98             current->flags &= ~PF_USEDFPU;
99             current->used_math = 0;
100     
101             /* Setup active_mm for idle_task  */
102             atomic_inc(&init_mm.mm_count);
103             current->active_mm = &init_mm;
104             if (current->mm)
105                     BUG();
106             enter_lazy_tlb(&init_mm, current, nr);
107     }
108     
109     /*
110      * VM halt and poweroff setup routines
111      */
112     char vmhalt_cmd[128] = "";
113     char vmpoff_cmd[128] = "";
114     
115     static inline void strncpy_skip_quote(char *dst, char *src, int n)
116     {
117             int sx, dx;
118     
119             dx = 0;
120             for (sx = 0; src[sx] != 0; sx++) {
121                     if (src[sx] == '"') continue;
122                     dst[dx++] = src[sx];
123                     if (dx >= n) break;
124             }
125     }
126     
127     static int __init vmhalt_setup(char *str)
128     {
129             strncpy_skip_quote(vmhalt_cmd, str, 127);
130             vmhalt_cmd[127] = 0;
131             return 1;
132     }
133     
134     __setup("vmhalt=", vmhalt_setup);
135     
136     static int __init vmpoff_setup(char *str)
137     {
138             strncpy_skip_quote(vmpoff_cmd, str, 127);
139             vmpoff_cmd[127] = 0;
140             return 1;
141     }
142     
143     __setup("vmpoff=", vmpoff_setup);
144     
145     /*
146      * condev= and conmode= setup parameter.
147      */
148     
149     static int __init condev_setup(char *str)
150     {
151     	int vdev;
152     
153     	vdev = simple_strtoul(str, &str, 0);
154     	if (vdev >= 0 && vdev < 65536)
155     		console_device = vdev;
156     	return 1;
157     }
158     
159     __setup("condev=", condev_setup);
160     
161     static int __init conmode_setup(char *str)
162     {
163     #if defined(CONFIG_HWC_CONSOLE)
164     	if (strncmp(str, "hwc", 4) == 0 && !MACHINE_IS_P390)
165                     SET_CONSOLE_HWC;
166     #endif
167     #if defined(CONFIG_TN3215_CONSOLE)
168     	if (strncmp(str, "3215", 5) == 0 && (MACHINE_IS_VM || MACHINE_IS_P390))
169     		SET_CONSOLE_3215;
170     #endif
171     #if defined(CONFIG_TN3270_CONSOLE)
172     	if (strncmp(str, "3270", 5) == 0 && (MACHINE_IS_VM || MACHINE_IS_P390))
173     		SET_CONSOLE_3270;
174     #endif
175             return 1;
176     }
177     
178     __setup("conmode=", conmode_setup);
179     
180     static void __init conmode_default(void)
181     {
182     	char query_buffer[1024];
183     	char *ptr;
184     
185             if (MACHINE_IS_VM) {
186     		cpcmd("QUERY TERM", query_buffer, 1024);
187     		ptr = strstr(query_buffer, "CONMODE");
188     		/*
189     		 * Set the conmode to 3215 so that the device recognition 
190     		 * will set the cu_type of the console to 3215. If the
191     		 * conmode is 3270 and we don't set it back then both
192     		 * 3215 and the 3270 driver will try to access the console
193     		 * device (3215 as console and 3270 as normal tty).
194     		 */
195     		cpcmd("TERM CONMODE 3215", NULL, 0);
196     		if (ptr == NULL) {
197     #if defined(CONFIG_HWC_CONSOLE)
198     			SET_CONSOLE_HWC;
199     #endif
200     			return;
201     		}
202     		if (strncmp(ptr + 8, "3270", 4) == 0) {
203     #if defined(CONFIG_TN3270_CONSOLE)
204     			SET_CONSOLE_3270;
205     #elif defined(CONFIG_TN3215_CONSOLE)
206     			SET_CONSOLE_3215;
207     #elif defined(CONFIG_HWC_CONSOLE)
208     			SET_CONSOLE_HWC;
209     #endif
210     		} else if (strncmp(ptr + 8, "3215", 4) == 0) {
211     #if defined(CONFIG_TN3215_CONSOLE)
212     			SET_CONSOLE_3215;
213     #elif defined(CONFIG_TN3270_CONSOLE)
214     			SET_CONSOLE_3270;
215     #elif defined(CONFIG_HWC_CONSOLE)
216     			SET_CONSOLE_HWC;
217     #endif
218     		}
219             } else if (MACHINE_IS_P390) {
220     #if defined(CONFIG_TN3215_CONSOLE)
221     		SET_CONSOLE_3215;
222     #elif defined(CONFIG_TN3270_CONSOLE)
223     		SET_CONSOLE_3270;
224     #endif
225     	} else {
226     #if defined(CONFIG_HWC_CONSOLE)
227     		SET_CONSOLE_HWC;
228     #endif
229     	}
230     }
231     
232     /*
233      * Reboot, halt and power_off routines for non SMP.
234      */
235     #ifndef CONFIG_SMP
236     void machine_restart(char * __unused)
237     {
238     	reipl(S390_lowcore.ipl_device);
239     }
240     
241     void machine_halt(void)
242     {
243             if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0)
244                     cpcmd(vmhalt_cmd, NULL, 0);
245             signal_processor(smp_processor_id(), sigp_stop_and_store_status);
246     }
247     
248     void machine_power_off(void)
249     {
250             if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0)
251                     cpcmd(vmpoff_cmd, NULL, 0);
252             signal_processor(smp_processor_id(), sigp_stop_and_store_status);
253     }
254     #endif
255     
256     /*
257      * Setup function called from init/main.c just after the banner
258      * was printed.
259      */
260     void __init setup_arch(char **cmdline_p)
261     {
262             unsigned long bootmap_size;
263             unsigned long memory_start, memory_end;
264             char c = ' ', cn, *to = command_line, *from = COMMAND_LINE;
265     	struct resource *res;
266     	unsigned long start_pfn, end_pfn;
267             static unsigned int smptrap=0;
268             unsigned long delay = 0;
269     
270             if (smptrap)
271                     return;
272             smptrap=1;
273     
274             /*
275              * Setup lowcore information for boot cpu
276              */
277             cpu_init();
278             boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr;
279             __cpu_logical_map[0] = boot_cpu_addr;
280     
281             /*
282              * print what head.S has found out about the machine 
283              */
284     	printk((MACHINE_IS_VM) ?
285     	       "We are running under VM\n" :
286     	       "We are running native\n");
287     
288             ROOT_DEV = to_kdev_t(0x0100);
289             memory_start = (unsigned long) &_end;    /* fixit if use $CODELO etc*/
290     	memory_end = memory_size;                /* detected in head.s */
291             init_mm.start_code = PAGE_OFFSET;
292             init_mm.end_code = (unsigned long) &_etext;
293             init_mm.end_data = (unsigned long) &_edata;
294             init_mm.brk = (unsigned long) &_end;
295     
296     	code_resource.start = (unsigned long) &_text;
297     	code_resource.end = (unsigned long) &_etext - 1;
298     	data_resource.start = (unsigned long) &_etext;
299     	data_resource.end = (unsigned long) &_edata - 1;
300     
301             /* Save unparsed command line copy for /proc/cmdline */
302             memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE);
303             saved_command_line[COMMAND_LINE_SIZE-1] = '\0';
304     
305             for (;;) {
306                     /*
307                      * "mem=XXX[kKmM]" sets memsize 
308                      */
309                     if (c == ' ' && strncmp(from, "mem=", 4) == 0) {
310                             memory_end = simple_strtoul(from+4, &from, 0);
311                             if ( *from == 'K' || *from == 'k' ) {
312                                     memory_end = memory_end << 10;
313                                     from++;
314                             } else if ( *from == 'M' || *from == 'm' ) {
315                                     memory_end = memory_end << 20;
316                                     from++;
317                             }
318                     }
319                     /*
320                      * "ipldelay=XXX[sm]" sets ipl delay in seconds or minutes
321                      */
322                     if (c == ' ' && strncmp(from, "ipldelay=", 9) == 0) {
323                             delay = simple_strtoul(from+9, &from, 0);
324     			if (*from == 's' || *from == 'S') {
325     				delay = delay*1000000;
326     				from++;
327     			} else if (*from == 'm' || *from == 'M') {
328     				delay = delay*60*1000000;
329     				from++;
330     			}
331     			/* now wait for the requested amount of time */
332     			udelay(delay);
333                     }
334                     cn = *(from++);
335                     if (!cn)
336                             break;
337                     if (cn == '\n')
338                             cn = ' ';  /* replace newlines with space */
339     		if (cn == 0x0d)
340     			cn = ' ';  /* replace 0x0d with space */
341                     if (cn == ' ' && c == ' ')
342                             continue;  /* remove additional spaces */
343                     c = cn;
344                     if (to - command_line >= COMMAND_LINE_SIZE)
345                             break;
346                     *(to++) = c;
347             }
348             if (c == ' ' && to > command_line) to--;
349             *to = '\0';
350             *cmdline_p = command_line;
351     
352     	/*
353     	 * partially used pages are not usable - thus
354     	 * we are rounding upwards:
355     	 */
356     	start_pfn = (__pa(&_end) + PAGE_SIZE - 1) >> PAGE_SHIFT;
357     	end_pfn = memory_end >> PAGE_SHIFT;
358     
359     	/*
360     	 * Initialize the boot-time allocator
361     	 */
362     	bootmap_size = init_bootmem(start_pfn, end_pfn);
363     
364     	/*
365     	 * Register RAM pages with the bootmem allocator.
366     	 */
367     	free_bootmem(start_pfn << PAGE_SHIFT, 
368     		     (end_pfn - start_pfn) << PAGE_SHIFT);
369     
370             /*
371              * Reserve the bootmem bitmap itself as well. We do this in two
372              * steps (first step was init_bootmem()) because this catches
373              * the (very unlikely) case of us accidentally initializing the
374              * bootmem allocator with an invalid RAM area.
375              */
376             reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size);
377     
378     #ifdef CONFIG_BLK_DEV_INITRD
379             if (INITRD_START) {
380     		if (INITRD_START + INITRD_SIZE <= memory_end) {
381     			reserve_bootmem(INITRD_START, INITRD_SIZE);
382     			initrd_start = INITRD_START;
383     			initrd_end = initrd_start + INITRD_SIZE;
384     		} else {
385                             printk("initrd extends beyond end of memory "
386                                    "(0x%08lx > 0x%08lx)\ndisabling initrd\n",
387                                    initrd_start + INITRD_SIZE, memory_end);
388                             initrd_start = initrd_end = 0;
389     		}
390             }
391     #endif
392     
393             paging_init();
394     
395     	res = alloc_bootmem_low(sizeof(struct resource));
396     	res->start = 0;
397     	res->end = memory_end;
398     	res->flags = IORESOURCE_MEM | IORESOURCE_BUSY;
399     	request_resource(&iomem_resource, res);
400     	request_resource(res, &code_resource);
401     	request_resource(res, &data_resource);
402     
403             /* Setup default console */
404     	conmode_default();
405     }
406     
407     void print_cpu_info(struct cpuinfo_S390 *cpuinfo)
408     {
409        printk("cpu %d "
410     #ifdef CONFIG_SMP
411                "phys_idx=%d "
412     #endif
413                "vers=%02X ident=%06X machine=%04X unused=%04X\n",
414                cpuinfo->cpu_nr,
415     #ifdef CONFIG_SMP
416                cpuinfo->cpu_addr,
417     #endif
418                cpuinfo->cpu_id.version,
419                cpuinfo->cpu_id.ident,
420                cpuinfo->cpu_id.machine,
421                cpuinfo->cpu_id.unused);
422     }
423     
424     /*
425      *	Get CPU information for use by the procfs.
426      */
427     
428     int get_cpuinfo(char * buffer)
429     {
430             struct cpuinfo_S390 *cpuinfo;
431             char *p = buffer;
432             int i;
433     
434             p += sprintf(p,"vendor_id       : IBM/S390\n"
435                            "# processors    : %i\n"
436                            "bogomips per cpu: %lu.%02lu\n",
437                            smp_num_cpus, loops_per_jiffy/(500000/HZ),
438                            (loops_per_jiffy/(5000/HZ))%100);
439             for (i = 0; i < smp_num_cpus; i++) {
440                     cpuinfo = &safe_get_cpu_lowcore(i).cpu_data;
441                     p += sprintf(p,"processor %i: "
442                                    "version = %02X,  "
443                                    "identification = %06X,  "
444                                    "machine = %04X\n",
445                                    i, cpuinfo->cpu_id.version,
446                                    cpuinfo->cpu_id.ident,
447                                    cpuinfo->cpu_id.machine);
448             }
449             return p - buffer;
450     }
451     
452