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 <linux/seq_file.h> 38#include <asm/uaccess.h> 39#include <asm/system.h> 40#include <asm/smp.h> 41#include <asm/mmu_context.h> 42#include <asm/cpcmd.h> 43 44/* 45 * Machine setup.. 46 */ 47unsigned int console_mode = 0; 48unsigned int console_device = -1; 49unsigned long memory_size = 0; 50unsigned long machine_flags = 0; 51struct { unsigned long addr, size, type; } memory_chunk[16] = { { 0 } }; 52#define CHUNK_READ_WRITE 0 53#define CHUNK_READ_ONLY 1 54__u16 boot_cpu_addr; 55int cpus_initialized = 0; 56unsigned long cpu_initialized = 0; 57volatile int __cpu_logical_map[NR_CPUS]; /* logical cpu to cpu address */ 58 59/* 60 * Setup options 61 */ 62extern int _text,_etext, _edata, _end; 63 64/* 65 * This is set up by the setup-routine at boot-time 66 * for S390 need to find out, what we have to setup 67 * using address 0x10400 ... 68 */ 69 70#include <asm/setup.h> 71 72static char command_line[COMMAND_LINE_SIZE] = { 0, }; 73 char saved_command_line[COMMAND_LINE_SIZE]; 74 75static struct resource code_resource = { "Kernel code", 0x100000, 0 }; 76static struct resource data_resource = { "Kernel data", 0, 0 }; 77 78/* 79 * cpu_init() initializes state that is per-CPU. 80 */ 81void __init cpu_init (void) 82{ 83 int nr = smp_processor_id(); 84 int addr = hard_smp_processor_id(); 85 86 if (test_and_set_bit(nr,&cpu_initialized)) { 87 printk("CPU#%d ALREADY INITIALIZED!!!!!!!!!\n", nr); 88 for (;;) __sti(); 89 } 90 cpus_initialized++; 91 92 /* 93 * Store processor id in lowcore (used e.g. in timer_interrupt) 94 */ 95 asm volatile ("stidp %0": "=m" (S390_lowcore.cpu_data.cpu_id)); 96 S390_lowcore.cpu_data.cpu_addr = addr; 97 S390_lowcore.cpu_data.cpu_nr = nr; 98 99 /* 100 * Force FPU initialization: 101 */ 102 current->flags &= ~PF_USEDFPU; 103 current->used_math = 0; 104 105 /* Setup active_mm for idle_task */ 106 atomic_inc(&init_mm.mm_count); 107 current->active_mm = &init_mm; 108 if (current->mm) 109 BUG(); 110 enter_lazy_tlb(&init_mm, current, nr); 111} 112 113/* 114 * VM halt and poweroff setup routines 115 */ 116char vmhalt_cmd[128] = ""; 117char vmpoff_cmd[128] = ""; 118 119static inline void strncpy_skip_quote(char *dst, char *src, int n) 120{ 121 int sx, dx; 122 123 dx = 0; 124 for (sx = 0; src[sx] != 0; sx++) { 125 if (src[sx] == '"') continue; 126 dst[dx++] = src[sx]; 127 if (dx >= n) break; 128 } 129} 130 131static int __init vmhalt_setup(char *str) 132{ 133 strncpy_skip_quote(vmhalt_cmd, str, 127); 134 vmhalt_cmd[127] = 0; 135 return 1; 136} 137 138__setup("vmhalt=", vmhalt_setup); 139 140static int __init vmpoff_setup(char *str) 141{ 142 strncpy_skip_quote(vmpoff_cmd, str, 127); 143 vmpoff_cmd[127] = 0; 144 return 1; 145} 146 147__setup("vmpoff=", vmpoff_setup); 148 149/* 150 * condev= and conmode= setup parameter. 151 */ 152 153static int __init condev_setup(char *str) 154{ 155 int vdev; 156 157 vdev = simple_strtoul(str, &str, 0); 158 if (vdev >= 0 && vdev < 65536) 159 console_device = vdev; 160 return 1; 161} 162 163__setup("condev=", condev_setup); 164 165static int __init conmode_setup(char *str) 166{ 167#if defined(CONFIG_HWC_CONSOLE) 168 if (strncmp(str, "hwc", 4) == 0) 169 SET_CONSOLE_HWC; 170#endif 171#if defined(CONFIG_TN3215_CONSOLE) 172 if (strncmp(str, "3215", 5) == 0) 173 SET_CONSOLE_3215; 174#endif 175#if defined(CONFIG_TN3270_CONSOLE) 176 if (strncmp(str, "3270", 5) == 0) 177 SET_CONSOLE_3270; 178#endif 179 return 1; 180} 181 182__setup("conmode=", conmode_setup); 183 184static void __init conmode_default(void) 185{ 186 char query_buffer[1024]; 187 char *ptr; 188 189 if (MACHINE_IS_VM) { 190 cpcmd("QUERY TERM", query_buffer, 1024); 191 ptr = strstr(query_buffer, "CONMODE"); 192 /* 193 * Set the conmode to 3215 so that the device recognition 194 * will set the cu_type of the console to 3215. If the 195 * conmode is 3270 and we don't set it back then both 196 * 3215 and the 3270 driver will try to access the console 197 * device (3215 as console and 3270 as normal tty). 198 */ 199 cpcmd("TERM CONMODE 3215", NULL, 0); 200 if (ptr == NULL) { 201#if defined(CONFIG_HWC_CONSOLE) 202 SET_CONSOLE_HWC; 203#endif 204 return; 205 } 206 if (strncmp(ptr + 8, "3270", 4) == 0) { 207#if defined(CONFIG_TN3270_CONSOLE) 208 SET_CONSOLE_3270; 209#elif defined(CONFIG_TN3215_CONSOLE) 210 SET_CONSOLE_3215; 211#elif defined(CONFIG_HWC_CONSOLE) 212 SET_CONSOLE_HWC; 213#endif 214 } else if (strncmp(ptr + 8, "3215", 4) == 0) { 215#if defined(CONFIG_TN3215_CONSOLE) 216 SET_CONSOLE_3215; 217#elif defined(CONFIG_TN3270_CONSOLE) 218 SET_CONSOLE_3270; 219#elif defined(CONFIG_HWC_CONSOLE) 220 SET_CONSOLE_HWC; 221#endif 222 } 223 } else if (MACHINE_IS_P390) { 224#if defined(CONFIG_TN3215_CONSOLE) 225 SET_CONSOLE_3215; 226#elif defined(CONFIG_TN3270_CONSOLE) 227 SET_CONSOLE_3270; 228#endif 229 } else { 230#if defined(CONFIG_HWC_CONSOLE) 231 SET_CONSOLE_HWC; 232#endif 233 } 234} 235 236#ifdef CONFIG_SMP 237extern void machine_restart_smp(char *); 238extern void machine_halt_smp(void); 239extern void machine_power_off_smp(void); 240 241void (*_machine_restart)(char *command) = machine_restart_smp; 242void (*_machine_halt)(void) = machine_halt_smp; 243void (*_machine_power_off)(void) = machine_power_off_smp; 244#else 245/* 246 * Reboot, halt and power_off routines for non SMP. 247 */ 248static void do_machine_restart_nonsmp(char * __unused) 249{ 250 reipl(S390_lowcore.ipl_device); 251} 252 253static void do_machine_halt_nonsmp(void) 254{ 255 if (MACHINE_IS_VM && strlen(vmhalt_cmd) > 0) 256 cpcmd(vmhalt_cmd, NULL, 0); 257 signal_processor(smp_processor_id(), sigp_stop_and_store_status); 258} 259 260static void do_machine_power_off_nonsmp(void) 261{ 262 if (MACHINE_IS_VM && strlen(vmpoff_cmd) > 0) 263 cpcmd(vmpoff_cmd, NULL, 0); 264 signal_processor(smp_processor_id(), sigp_stop_and_store_status); 265} 266 267void (*_machine_restart)(char *command) = do_machine_restart_nonsmp; 268void (*_machine_halt)(void) = do_machine_halt_nonsmp; 269void (*_machine_power_off)(void) = do_machine_power_off_nonsmp; 270#endif 271 272/* 273 * Reboot, halt and power_off stubs. They just call _machine_restart, 274 * _machine_halt or _machine_power_off. 275 */ 276 277void machine_restart(char *command) 278{ 279 _machine_restart(command); 280} 281 282void machine_halt(void) 283{ 284 _machine_halt(); 285} 286 287void machine_power_off(void) 288{ 289 _machine_power_off(); 290} 291 292/* 293 * Setup function called from init/main.c just after the banner 294 * was printed. 295 */ 296extern char _pstart, _pend, _stext; 297 298void __init setup_arch(char **cmdline_p) 299{ 300 unsigned long bootmap_size; 301 unsigned long memory_start, memory_end; 302 char c = ' ', cn, *to = command_line, *from = COMMAND_LINE; 303 struct resource *res; 304 unsigned long start_pfn, end_pfn; 305 static unsigned int smptrap=0; 306 unsigned long delay = 0; 307 struct _lowcore *lowcore; 308 int i; 309 310 if (smptrap) 311 return; 312 smptrap=1; 313 314 /* 315 * print what head.S has found out about the machine 316 */ 317 printk((MACHINE_IS_VM) ? 318 "We are running under VM (64 bit mode)\n" : 319 "We are running native (64 bit mode)\n"); 320 321 ROOT_DEV = to_kdev_t(0x0100); 322 memory_start = (unsigned long) &_end; /* fixit if use $CODELO etc*/ 323 memory_end = memory_size & ~0x200000UL; /* detected in head.s */ 324 init_mm.start_code = PAGE_OFFSET; 325 init_mm.end_code = (unsigned long) &_etext; 326 init_mm.end_data = (unsigned long) &_edata; 327 init_mm.brk = (unsigned long) &_end; 328 329 code_resource.start = (unsigned long) &_text; 330 code_resource.end = (unsigned long) &_etext - 1; 331 data_resource.start = (unsigned long) &_etext; 332 data_resource.end = (unsigned long) &_edata - 1; 333 334 /* Save unparsed command line copy for /proc/cmdline */ 335 memcpy(saved_command_line, COMMAND_LINE, COMMAND_LINE_SIZE); 336 saved_command_line[COMMAND_LINE_SIZE-1] = '\0'; 337 338 for (;;) { 339 if (c == ' ' && strncmp(from, "mem=", 4) == 0) { 340 memory_end = simple_strtoul(from+4, &from, 0); 341 if ( *from == 'K' || *from == 'k' ) { 342 memory_end = memory_end << 10; 343 from++; 344 } else if ( *from == 'M' || *from == 'm' ) { 345 memory_end = memory_end << 20; 346 from++; 347 } 348 } 349 if (c == ' ' && strncmp(from, "ipldelay=", 9) == 0) { 350 delay = simple_strtoul(from+9, &from, 0); 351 if (*from == 's' || *from == 'S') { 352 delay = delay*1000000; 353 from++; 354 } else if (*from == 'm' || *from == 'M') { 355 delay = delay*60*1000000; 356 from++; 357 } 358 /* now wait for the requested amount of time */ 359 udelay(delay); 360 } 361 cn = *(from++); 362 if (!cn) 363 break; 364 if (cn == '\n') 365 cn = ' '; /* replace newlines with space */ 366 if (cn == 0x0d) 367 cn = ' '; /* replace 0x0d with space */ 368 if (cn == ' ' && c == ' ') 369 continue; /* remove additional spaces */ 370 c = cn; 371 if (to - command_line >= COMMAND_LINE_SIZE) 372 break; 373 *(to++) = c; 374 } 375 if (c == ' ' && to > command_line) to--; 376 *to = '\0'; 377 *cmdline_p = command_line; 378 379 /* 380 * partially used pages are not usable - thus 381 * we are rounding upwards: 382 */ 383 start_pfn = (__pa(&_end) + PAGE_SIZE - 1) >> PAGE_SHIFT; 384 end_pfn = memory_end >> PAGE_SHIFT; 385 386 /* 387 * Initialize the boot-time allocator 388 */ 389 bootmap_size = init_bootmem(start_pfn, end_pfn); 390 391 /* 392 * Register RAM areas with the bootmem allocator. 393 */ 394 for (i = 0; i < 16 && memory_chunk[i].size > 0; i++) { 395 unsigned long start_chunk, end_chunk; 396 397 if (memory_chunk[i].type != CHUNK_READ_WRITE) 398 continue; 399 start_chunk = (memory_chunk[i].addr + PAGE_SIZE - 1); 400 start_chunk >>= PAGE_SHIFT; 401 end_chunk = (memory_chunk[i].addr + memory_chunk[i].size); 402 end_chunk >>= PAGE_SHIFT; 403 if (start_chunk < start_pfn) 404 start_chunk = start_pfn; 405 if (end_chunk > end_pfn) 406 end_chunk = end_pfn; 407 if (start_chunk < end_chunk) 408 free_bootmem(start_chunk << PAGE_SHIFT, 409 (end_chunk - start_chunk) << PAGE_SHIFT); 410 } 411 412 /* 413 * Reserve the bootmem bitmap itself as well. We do this in two 414 * steps (first step was init_bootmem()) because this catches 415 * the (very unlikely) case of us accidentally initializing the 416 * bootmem allocator with an invalid RAM area. 417 */ 418 reserve_bootmem(start_pfn << PAGE_SHIFT, bootmap_size); 419 420#ifdef CONFIG_BLK_DEV_INITRD 421 if (INITRD_START) { 422 if (INITRD_START + INITRD_SIZE <= memory_end) { 423 reserve_bootmem(INITRD_START, INITRD_SIZE); 424 initrd_start = INITRD_START; 425 initrd_end = initrd_start + INITRD_SIZE; 426 } else { 427 printk("initrd extends beyond end of memory " 428 "(0x%08lx > 0x%08lx)\ndisabling initrd\n", 429 initrd_start + INITRD_SIZE, memory_end); 430 initrd_start = initrd_end = 0; 431 } 432 } 433#endif 434 435 /* 436 * Setup lowcore for boot cpu 437 */ 438 lowcore = (struct _lowcore *) 439 __alloc_bootmem(2*PAGE_SIZE, 2*PAGE_SIZE, 0); 440 memset(lowcore, 0, 2*PAGE_SIZE); 441 lowcore->restart_psw.mask = _RESTART_PSW_MASK; 442 lowcore->restart_psw.addr = (addr_t) &restart_int_handler; 443 lowcore->external_new_psw.mask = _EXT_PSW_MASK; 444 lowcore->external_new_psw.addr = (addr_t) &ext_int_handler; 445 lowcore->svc_new_psw.mask = _SVC_PSW_MASK; 446 lowcore->svc_new_psw.addr = (addr_t) &system_call; 447 lowcore->program_new_psw.mask = _PGM_PSW_MASK; 448 lowcore->program_new_psw.addr = (addr_t) &pgm_check_handler; 449 lowcore->mcck_new_psw.mask = _MCCK_PSW_MASK; 450 lowcore->mcck_new_psw.addr = (addr_t) &mcck_int_handler; 451 lowcore->io_new_psw.mask = _IO_PSW_MASK; 452 lowcore->io_new_psw.addr = (addr_t) &io_int_handler; 453 lowcore->ipl_device = S390_lowcore.ipl_device; 454 lowcore->kernel_stack = ((__u64) &init_task_union) + 16384; 455 lowcore->async_stack = (__u64) 456 __alloc_bootmem(4*PAGE_SIZE, 4*PAGE_SIZE, 0) + 16384; 457 lowcore->jiffy_timer = -1LL; 458 if (MACHINE_HAS_DIAG44) 459 lowcore->diag44_opcode = 0x83000044; 460 else 461 lowcore->diag44_opcode = 0x07000700; 462 set_prefix((__u32)(__u64) lowcore); 463 cpu_init(); 464 boot_cpu_addr = S390_lowcore.cpu_data.cpu_addr; 465 __cpu_logical_map[0] = boot_cpu_addr; 466 467 /* 468 * Create kernel page tables and switch to virtual addressing. 469 */ 470 paging_init(); 471 472 res = alloc_bootmem_low(sizeof(struct resource)); 473 res->start = 0; 474 res->end = memory_end; 475 res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; 476 request_resource(&iomem_resource, res); 477 request_resource(res, &code_resource); 478 request_resource(res, &data_resource); 479 480 /* Setup default console */ 481 conmode_default(); 482} 483 484void print_cpu_info(struct cpuinfo_S390 *cpuinfo) 485{ 486 printk("cpu %d " 487#ifdef CONFIG_SMP 488 "phys_idx=%d " 489#endif 490 "vers=%02X ident=%06X machine=%04X unused=%04X\n", 491 cpuinfo->cpu_nr, 492#ifdef CONFIG_SMP 493 cpuinfo->cpu_addr, 494#endif 495 cpuinfo->cpu_id.version, 496 cpuinfo->cpu_id.ident, 497 cpuinfo->cpu_id.machine, 498 cpuinfo->cpu_id.unused); 499} 500 501/* 502 * show_cpuinfo - Get information on one CPU for use by procfs. 503 */ 504 505static int show_cpuinfo(struct seq_file *m, void *v) 506{ 507 struct cpuinfo_S390 *cpuinfo; 508 unsigned long n = (unsigned long) v - 1; 509 510 if (!n) { 511 seq_printf(m, "vendor_id : IBM/S390\n" 512 "# processors : %i\n" 513 "bogomips per cpu: %lu.%02lu\n", 514 smp_num_cpus, loops_per_jiffy/(500000/HZ), 515 (loops_per_jiffy/(5000/HZ))%100); 516 } 517 if (cpu_online_map & (1 << n)) { 518 cpuinfo = &safe_get_cpu_lowcore(n)->cpu_data; 519 seq_printf(m, "processor %li: " 520 "version = %02X, " 521 "identification = %06X, " 522 "machine = %04X\n", 523 n, cpuinfo->cpu_id.version, 524 cpuinfo->cpu_id.ident, 525 cpuinfo->cpu_id.machine); 526 } 527 return 0; 528} 529 530static void *c_start(struct seq_file *m, loff_t *pos) 531{ 532 return *pos <= NR_CPUS ? (void *)((unsigned long) *pos + 1) : NULL; 533} 534static void *c_next(struct seq_file *m, void *v, loff_t *pos) 535{ 536 ++*pos; 537 return c_start(m, pos); 538} 539static void c_stop(struct seq_file *m, void *v) 540{ 541} 542struct seq_operations cpuinfo_op = { 543 start: c_start, 544 next: c_next, 545 stop: c_stop, 546 show: show_cpuinfo, 547}; 548