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