Merge branch 'oprofile-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git...
[pandora-kernel.git] / arch / s390 / kernel / early.c
1 /*
2  *  arch/s390/kernel/early.c
3  *
4  *    Copyright IBM Corp. 2007, 2009
5  *    Author(s): Hongjie Yang <hongjie@us.ibm.com>,
6  *               Heiko Carstens <heiko.carstens@de.ibm.com>
7  */
8
9 #define KMSG_COMPONENT "setup"
10 #define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
11
12 #include <linux/compiler.h>
13 #include <linux/init.h>
14 #include <linux/errno.h>
15 #include <linux/string.h>
16 #include <linux/ctype.h>
17 #include <linux/ftrace.h>
18 #include <linux/lockdep.h>
19 #include <linux/module.h>
20 #include <linux/pfn.h>
21 #include <linux/uaccess.h>
22 #include <linux/kernel.h>
23 #include <asm/ebcdic.h>
24 #include <asm/ipl.h>
25 #include <asm/lowcore.h>
26 #include <asm/processor.h>
27 #include <asm/sections.h>
28 #include <asm/setup.h>
29 #include <asm/sysinfo.h>
30 #include <asm/cpcmd.h>
31 #include <asm/sclp.h>
32 #include "entry.h"
33
34 /*
35  * Create a Kernel NSS if the SAVESYS= parameter is defined
36  */
37 #define DEFSYS_CMD_SIZE         128
38 #define SAVESYS_CMD_SIZE        32
39
40 char kernel_nss_name[NSS_NAME_SIZE + 1];
41
42 static void __init setup_boot_command_line(void);
43
44 /*
45  * Get the TOD clock running.
46  */
47 static void __init reset_tod_clock(void)
48 {
49         u64 time;
50
51         if (store_clock(&time) == 0)
52                 return;
53         /* TOD clock not running. Set the clock to Unix Epoch. */
54         if (set_clock(TOD_UNIX_EPOCH) != 0 || store_clock(&time) != 0)
55                 disabled_wait(0);
56
57         sched_clock_base_cc = TOD_UNIX_EPOCH;
58 }
59
60 #ifdef CONFIG_SHARED_KERNEL
61 int __init savesys_ipl_nss(char *cmd, const int cmdlen);
62
63 asm(
64         "       .section .init.text,\"ax\",@progbits\n"
65         "       .align  4\n"
66         "       .type   savesys_ipl_nss, @function\n"
67         "savesys_ipl_nss:\n"
68 #ifdef CONFIG_64BIT
69         "       stmg    6,15,48(15)\n"
70         "       lgr     14,3\n"
71         "       sam31\n"
72         "       diag    2,14,0x8\n"
73         "       sam64\n"
74         "       lgr     2,14\n"
75         "       lmg     6,15,48(15)\n"
76 #else
77         "       stm     6,15,24(15)\n"
78         "       lr      14,3\n"
79         "       diag    2,14,0x8\n"
80         "       lr      2,14\n"
81         "       lm      6,15,24(15)\n"
82 #endif
83         "       br      14\n"
84         "       .size   savesys_ipl_nss, .-savesys_ipl_nss\n");
85
86 static __initdata char upper_command_line[COMMAND_LINE_SIZE];
87
88 static noinline __init void create_kernel_nss(void)
89 {
90         unsigned int i, stext_pfn, eshared_pfn, end_pfn, min_size;
91 #ifdef CONFIG_BLK_DEV_INITRD
92         unsigned int sinitrd_pfn, einitrd_pfn;
93 #endif
94         int response;
95         size_t len;
96         char *savesys_ptr;
97         char defsys_cmd[DEFSYS_CMD_SIZE];
98         char savesys_cmd[SAVESYS_CMD_SIZE];
99
100         /* Do nothing if we are not running under VM */
101         if (!MACHINE_IS_VM)
102                 return;
103
104         /* Convert COMMAND_LINE to upper case */
105         for (i = 0; i < strlen(boot_command_line); i++)
106                 upper_command_line[i] = toupper(boot_command_line[i]);
107
108         savesys_ptr = strstr(upper_command_line, "SAVESYS=");
109
110         if (!savesys_ptr)
111                 return;
112
113         savesys_ptr += 8;    /* Point to the beginning of the NSS name */
114         for (i = 0; i < NSS_NAME_SIZE; i++) {
115                 if (savesys_ptr[i] == ' ' || savesys_ptr[i] == '\0')
116                         break;
117                 kernel_nss_name[i] = savesys_ptr[i];
118         }
119
120         stext_pfn = PFN_DOWN(__pa(&_stext));
121         eshared_pfn = PFN_DOWN(__pa(&_eshared));
122         end_pfn = PFN_UP(__pa(&_end));
123         min_size = end_pfn << 2;
124
125         sprintf(defsys_cmd, "DEFSYS %s 00000-%.5X EW %.5X-%.5X SR %.5X-%.5X",
126                 kernel_nss_name, stext_pfn - 1, stext_pfn, eshared_pfn - 1,
127                 eshared_pfn, end_pfn);
128
129 #ifdef CONFIG_BLK_DEV_INITRD
130         if (INITRD_START && INITRD_SIZE) {
131                 sinitrd_pfn = PFN_DOWN(__pa(INITRD_START));
132                 einitrd_pfn = PFN_UP(__pa(INITRD_START + INITRD_SIZE));
133                 min_size = einitrd_pfn << 2;
134                 sprintf(defsys_cmd, "%s EW %.5X-%.5X", defsys_cmd,
135                 sinitrd_pfn, einitrd_pfn);
136         }
137 #endif
138
139         sprintf(defsys_cmd, "%s EW MINSIZE=%.7iK PARMREGS=0-13",
140                 defsys_cmd, min_size);
141         sprintf(savesys_cmd, "SAVESYS %s \n IPL %s",
142                 kernel_nss_name, kernel_nss_name);
143
144         __cpcmd(defsys_cmd, NULL, 0, &response);
145
146         if (response != 0) {
147                 pr_err("Defining the Linux kernel NSS failed with rc=%d\n",
148                         response);
149                 kernel_nss_name[0] = '\0';
150                 return;
151         }
152
153         len = strlen(savesys_cmd);
154         ASCEBC(savesys_cmd, len);
155         response = savesys_ipl_nss(savesys_cmd, len);
156
157         /* On success: response is equal to the command size,
158          *             max SAVESYS_CMD_SIZE
159          * On error: response contains the numeric portion of cp error message.
160          *           for SAVESYS it will be >= 263
161          *           for missing privilege class, it will be 1
162          */
163         if (response > SAVESYS_CMD_SIZE || response == 1) {
164                 pr_err("Saving the Linux kernel NSS failed with rc=%d\n",
165                         response);
166                 kernel_nss_name[0] = '\0';
167                 return;
168         }
169
170         /* re-setup boot command line with new ipl vm parms */
171         ipl_update_parameters();
172         setup_boot_command_line();
173
174         ipl_flags = IPL_NSS_VALID;
175 }
176
177 #else /* CONFIG_SHARED_KERNEL */
178
179 static inline void create_kernel_nss(void) { }
180
181 #endif /* CONFIG_SHARED_KERNEL */
182
183 /*
184  * Clear bss memory
185  */
186 static noinline __init void clear_bss_section(void)
187 {
188         memset(__bss_start, 0, __bss_stop - __bss_start);
189 }
190
191 /*
192  * Initialize storage key for kernel pages
193  */
194 static noinline __init void init_kernel_storage_key(void)
195 {
196         unsigned long end_pfn, init_pfn;
197
198         end_pfn = PFN_UP(__pa(&_end));
199
200         for (init_pfn = 0 ; init_pfn < end_pfn; init_pfn++)
201                 page_set_storage_key(init_pfn << PAGE_SHIFT, PAGE_DEFAULT_KEY);
202 }
203
204 static __initdata struct sysinfo_3_2_2 vmms __aligned(PAGE_SIZE);
205
206 static noinline __init void detect_machine_type(void)
207 {
208         /* No VM information? Looks like LPAR */
209         if (stsi(&vmms, 3, 2, 2) == -ENOSYS)
210                 return;
211         if (!vmms.count)
212                 return;
213
214         /* Running under KVM? If not we assume z/VM */
215         if (!memcmp(vmms.vm[0].cpi, "\xd2\xe5\xd4", 3))
216                 S390_lowcore.machine_flags |= MACHINE_FLAG_KVM;
217         else
218                 S390_lowcore.machine_flags |= MACHINE_FLAG_VM;
219 }
220
221 static __init void early_pgm_check_handler(void)
222 {
223         unsigned long addr;
224         const struct exception_table_entry *fixup;
225
226         addr = S390_lowcore.program_old_psw.addr;
227         fixup = search_exception_tables(addr & PSW_ADDR_INSN);
228         if (!fixup)
229                 disabled_wait(0);
230         S390_lowcore.program_old_psw.addr = fixup->fixup | PSW_ADDR_AMODE;
231 }
232
233 static noinline __init void setup_lowcore_early(void)
234 {
235         psw_t psw;
236
237         psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
238         psw.addr = PSW_ADDR_AMODE | (unsigned long) s390_base_ext_handler;
239         S390_lowcore.external_new_psw = psw;
240         psw.addr = PSW_ADDR_AMODE | (unsigned long) s390_base_pgm_handler;
241         S390_lowcore.program_new_psw = psw;
242         s390_base_pgm_handler_fn = early_pgm_check_handler;
243 }
244
245 static noinline __init void setup_hpage(void)
246 {
247 #ifndef CONFIG_DEBUG_PAGEALLOC
248         unsigned int facilities;
249
250         facilities = stfl();
251         if (!(facilities & (1UL << 23)) || !(facilities & (1UL << 29)))
252                 return;
253         S390_lowcore.machine_flags |= MACHINE_FLAG_HPAGE;
254         __ctl_set_bit(0, 23);
255 #endif
256 }
257
258 static __init void detect_mvpg(void)
259 {
260 #ifndef CONFIG_64BIT
261         int rc;
262
263         asm volatile(
264                 "       la      0,0\n"
265                 "       mvpg    %2,%2\n"
266                 "0:     la      %0,0\n"
267                 "1:\n"
268                 EX_TABLE(0b,1b)
269                 : "=d" (rc) : "0" (-EOPNOTSUPP), "a" (0) : "memory", "cc", "0");
270         if (!rc)
271                 S390_lowcore.machine_flags |= MACHINE_FLAG_MVPG;
272 #endif
273 }
274
275 static __init void detect_ieee(void)
276 {
277 #ifndef CONFIG_64BIT
278         int rc, tmp;
279
280         asm volatile(
281                 "       efpc    %1,0\n"
282                 "0:     la      %0,0\n"
283                 "1:\n"
284                 EX_TABLE(0b,1b)
285                 : "=d" (rc), "=d" (tmp): "0" (-EOPNOTSUPP) : "cc");
286         if (!rc)
287                 S390_lowcore.machine_flags |= MACHINE_FLAG_IEEE;
288 #endif
289 }
290
291 static __init void detect_csp(void)
292 {
293 #ifndef CONFIG_64BIT
294         int rc;
295
296         asm volatile(
297                 "       la      0,0\n"
298                 "       la      1,0\n"
299                 "       la      2,4\n"
300                 "       csp     0,2\n"
301                 "0:     la      %0,0\n"
302                 "1:\n"
303                 EX_TABLE(0b,1b)
304                 : "=d" (rc) : "0" (-EOPNOTSUPP) : "cc", "0", "1", "2");
305         if (!rc)
306                 S390_lowcore.machine_flags |= MACHINE_FLAG_CSP;
307 #endif
308 }
309
310 static __init void detect_diag9c(void)
311 {
312         unsigned int cpu_address;
313         int rc;
314
315         cpu_address = stap();
316         asm volatile(
317                 "       diag    %2,0,0x9c\n"
318                 "0:     la      %0,0\n"
319                 "1:\n"
320                 EX_TABLE(0b,1b)
321                 : "=d" (rc) : "0" (-EOPNOTSUPP), "d" (cpu_address) : "cc");
322         if (!rc)
323                 S390_lowcore.machine_flags |= MACHINE_FLAG_DIAG9C;
324 }
325
326 static __init void detect_diag44(void)
327 {
328 #ifdef CONFIG_64BIT
329         int rc;
330
331         asm volatile(
332                 "       diag    0,0,0x44\n"
333                 "0:     la      %0,0\n"
334                 "1:\n"
335                 EX_TABLE(0b,1b)
336                 : "=d" (rc) : "0" (-EOPNOTSUPP) : "cc");
337         if (!rc)
338                 S390_lowcore.machine_flags |= MACHINE_FLAG_DIAG44;
339 #endif
340 }
341
342 static __init void detect_machine_facilities(void)
343 {
344 #ifdef CONFIG_64BIT
345         unsigned int facilities;
346
347         facilities = stfl();
348         if (facilities & (1 << 28))
349                 S390_lowcore.machine_flags |= MACHINE_FLAG_IDTE;
350         if (facilities & (1 << 23))
351                 S390_lowcore.machine_flags |= MACHINE_FLAG_PFMF;
352         if (facilities & (1 << 4))
353                 S390_lowcore.machine_flags |= MACHINE_FLAG_MVCOS;
354 #endif
355 }
356
357 static __init void rescue_initrd(void)
358 {
359 #ifdef CONFIG_BLK_DEV_INITRD
360         /*
361          * Move the initrd right behind the bss section in case it starts
362          * within the bss section. So we don't overwrite it when the bss
363          * section gets cleared.
364          */
365         if (!INITRD_START || !INITRD_SIZE)
366                 return;
367         if (INITRD_START >= (unsigned long) __bss_stop)
368                 return;
369         memmove(__bss_stop, (void *) INITRD_START, INITRD_SIZE);
370         INITRD_START = (unsigned long) __bss_stop;
371 #endif
372 }
373
374 /* Set up boot command line */
375 static void __init append_to_cmdline(size_t (*ipl_data)(char *, size_t))
376 {
377         char *parm, *delim;
378         size_t rc, len;
379
380         len = strlen(boot_command_line);
381
382         delim = boot_command_line + len;        /* '\0' character position */
383         parm  = boot_command_line + len + 1;    /* append right after '\0' */
384
385         rc = ipl_data(parm, COMMAND_LINE_SIZE - len - 1);
386         if (rc) {
387                 if (*parm == '=')
388                         memmove(boot_command_line, parm + 1, rc);
389                 else
390                         *delim = ' ';           /* replace '\0' with space */
391         }
392 }
393
394 static void __init setup_boot_command_line(void)
395 {
396         /* copy arch command line */
397         strlcpy(boot_command_line, COMMAND_LINE, ARCH_COMMAND_LINE_SIZE);
398
399         /* append IPL PARM data to the boot command line */
400         if (MACHINE_IS_VM)
401                 append_to_cmdline(append_ipl_vmparm);
402
403         append_to_cmdline(append_ipl_scpdata);
404 }
405
406
407 /*
408  * Save ipl parameters, clear bss memory, initialize storage keys
409  * and create a kernel NSS at startup if the SAVESYS= parm is defined
410  */
411 void __init startup_init(void)
412 {
413         reset_tod_clock();
414         ipl_save_parameters();
415         rescue_initrd();
416         clear_bss_section();
417         init_kernel_storage_key();
418         lockdep_init();
419         lockdep_off();
420         sort_main_extable();
421         setup_lowcore_early();
422         detect_machine_type();
423         ipl_update_parameters();
424         setup_boot_command_line();
425         create_kernel_nss();
426         detect_mvpg();
427         detect_ieee();
428         detect_csp();
429         detect_diag9c();
430         detect_diag44();
431         detect_machine_facilities();
432         setup_hpage();
433         sclp_facilities_detect();
434         detect_memory_layout(memory_chunk);
435 #ifdef CONFIG_DYNAMIC_FTRACE
436         S390_lowcore.ftrace_func = (unsigned long)ftrace_caller;
437 #endif
438         lockdep_on();
439 }