Root/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
40char kernel_nss_name[NSS_NAME_SIZE + 1];
41
42static void __init setup_boot_command_line(void);
43
44/*
45 * Get the TOD clock running.
46 */
47static 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    S390_lowcore.last_update_clock = sched_clock_base_cc;
59}
60
61#ifdef CONFIG_SHARED_KERNEL
62int __init savesys_ipl_nss(char *cmd, const int cmdlen);
63
64asm(
65    " .section .init.text,\"ax\",@progbits\n"
66    " .align 4\n"
67    " .type savesys_ipl_nss, @function\n"
68    "savesys_ipl_nss:\n"
69#ifdef CONFIG_64BIT
70    " stmg 6,15,48(15)\n"
71    " lgr 14,3\n"
72    " sam31\n"
73    " diag 2,14,0x8\n"
74    " sam64\n"
75    " lgr 2,14\n"
76    " lmg 6,15,48(15)\n"
77#else
78    " stm 6,15,24(15)\n"
79    " lr 14,3\n"
80    " diag 2,14,0x8\n"
81    " lr 2,14\n"
82    " lm 6,15,24(15)\n"
83#endif
84    " br 14\n"
85    " .size savesys_ipl_nss, .-savesys_ipl_nss\n"
86    " .previous\n");
87
88static __initdata char upper_command_line[COMMAND_LINE_SIZE];
89
90static noinline __init void create_kernel_nss(void)
91{
92    unsigned int i, stext_pfn, eshared_pfn, end_pfn, min_size;
93#ifdef CONFIG_BLK_DEV_INITRD
94    unsigned int sinitrd_pfn, einitrd_pfn;
95#endif
96    int response;
97    size_t len;
98    char *savesys_ptr;
99    char defsys_cmd[DEFSYS_CMD_SIZE];
100    char savesys_cmd[SAVESYS_CMD_SIZE];
101
102    /* Do nothing if we are not running under VM */
103    if (!MACHINE_IS_VM)
104        return;
105
106    /* Convert COMMAND_LINE to upper case */
107    for (i = 0; i < strlen(boot_command_line); i++)
108        upper_command_line[i] = toupper(boot_command_line[i]);
109
110    savesys_ptr = strstr(upper_command_line, "SAVESYS=");
111
112    if (!savesys_ptr)
113        return;
114
115    savesys_ptr += 8; /* Point to the beginning of the NSS name */
116    for (i = 0; i < NSS_NAME_SIZE; i++) {
117        if (savesys_ptr[i] == ' ' || savesys_ptr[i] == '\0')
118            break;
119        kernel_nss_name[i] = savesys_ptr[i];
120    }
121
122    stext_pfn = PFN_DOWN(__pa(&_stext));
123    eshared_pfn = PFN_DOWN(__pa(&_eshared));
124    end_pfn = PFN_UP(__pa(&_end));
125    min_size = end_pfn << 2;
126
127    sprintf(defsys_cmd, "DEFSYS %s 00000-%.5X EW %.5X-%.5X SR %.5X-%.5X",
128        kernel_nss_name, stext_pfn - 1, stext_pfn, eshared_pfn - 1,
129        eshared_pfn, end_pfn);
130
131#ifdef CONFIG_BLK_DEV_INITRD
132    if (INITRD_START && INITRD_SIZE) {
133        sinitrd_pfn = PFN_DOWN(__pa(INITRD_START));
134        einitrd_pfn = PFN_UP(__pa(INITRD_START + INITRD_SIZE));
135        min_size = einitrd_pfn << 2;
136        sprintf(defsys_cmd, "%s EW %.5X-%.5X", defsys_cmd,
137        sinitrd_pfn, einitrd_pfn);
138    }
139#endif
140
141    sprintf(defsys_cmd, "%s EW MINSIZE=%.7iK PARMREGS=0-13",
142        defsys_cmd, min_size);
143    sprintf(savesys_cmd, "SAVESYS %s \n IPL %s",
144        kernel_nss_name, kernel_nss_name);
145
146    __cpcmd(defsys_cmd, NULL, 0, &response);
147
148    if (response != 0) {
149        pr_err("Defining the Linux kernel NSS failed with rc=%d\n",
150            response);
151        kernel_nss_name[0] = '\0';
152        return;
153    }
154
155    len = strlen(savesys_cmd);
156    ASCEBC(savesys_cmd, len);
157    response = savesys_ipl_nss(savesys_cmd, len);
158
159    /* On success: response is equal to the command size,
160     * max SAVESYS_CMD_SIZE
161     * On error: response contains the numeric portion of cp error message.
162     * for SAVESYS it will be >= 263
163     * for missing privilege class, it will be 1
164     */
165    if (response > SAVESYS_CMD_SIZE || response == 1) {
166        pr_err("Saving the Linux kernel NSS failed with rc=%d\n",
167            response);
168        kernel_nss_name[0] = '\0';
169        return;
170    }
171
172    /* re-initialize cputime accounting. */
173    sched_clock_base_cc = get_clock();
174    S390_lowcore.last_update_clock = sched_clock_base_cc;
175    S390_lowcore.last_update_timer = 0x7fffffffffffffffULL;
176    S390_lowcore.user_timer = 0;
177    S390_lowcore.system_timer = 0;
178    asm volatile("SPT 0(%0)" : : "a" (&S390_lowcore.last_update_timer));
179
180    /* re-setup boot command line with new ipl vm parms */
181    ipl_update_parameters();
182    setup_boot_command_line();
183
184    ipl_flags = IPL_NSS_VALID;
185}
186
187#else /* CONFIG_SHARED_KERNEL */
188
189static inline void create_kernel_nss(void) { }
190
191#endif /* CONFIG_SHARED_KERNEL */
192
193/*
194 * Clear bss memory
195 */
196static noinline __init void clear_bss_section(void)
197{
198    memset(__bss_start, 0, __bss_stop - __bss_start);
199}
200
201/*
202 * Initialize storage key for kernel pages
203 */
204static noinline __init void init_kernel_storage_key(void)
205{
206    unsigned long end_pfn, init_pfn;
207
208    end_pfn = PFN_UP(__pa(&_end));
209
210    for (init_pfn = 0 ; init_pfn < end_pfn; init_pfn++)
211        page_set_storage_key(init_pfn << PAGE_SHIFT,
212                     PAGE_DEFAULT_KEY, 0);
213}
214
215static __initdata struct sysinfo_3_2_2 vmms __aligned(PAGE_SIZE);
216
217static noinline __init void detect_machine_type(void)
218{
219    /* Check current-configuration-level */
220    if ((stsi(NULL, 0, 0, 0) >> 28) <= 2) {
221        S390_lowcore.machine_flags |= MACHINE_FLAG_LPAR;
222        return;
223    }
224    /* Get virtual-machine cpu information. */
225    if (stsi(&vmms, 3, 2, 2) == -ENOSYS || !vmms.count)
226        return;
227
228    /* Running under KVM? If not we assume z/VM */
229    if (!memcmp(vmms.vm[0].cpi, "\xd2\xe5\xd4", 3))
230        S390_lowcore.machine_flags |= MACHINE_FLAG_KVM;
231    else
232        S390_lowcore.machine_flags |= MACHINE_FLAG_VM;
233}
234
235static __init void early_pgm_check_handler(void)
236{
237    unsigned long addr;
238    const struct exception_table_entry *fixup;
239
240    addr = S390_lowcore.program_old_psw.addr;
241    fixup = search_exception_tables(addr & PSW_ADDR_INSN);
242    if (!fixup)
243        disabled_wait(0);
244    S390_lowcore.program_old_psw.addr = fixup->fixup | PSW_ADDR_AMODE;
245}
246
247static noinline __init void setup_lowcore_early(void)
248{
249    psw_t psw;
250
251    psw.mask = PSW_BASE_BITS | PSW_DEFAULT_KEY;
252    psw.addr = PSW_ADDR_AMODE | (unsigned long) s390_base_ext_handler;
253    S390_lowcore.external_new_psw = psw;
254    psw.addr = PSW_ADDR_AMODE | (unsigned long) s390_base_pgm_handler;
255    S390_lowcore.program_new_psw = psw;
256    s390_base_pgm_handler_fn = early_pgm_check_handler;
257}
258
259static noinline __init void setup_facility_list(void)
260{
261    unsigned long nr;
262
263    S390_lowcore.stfl_fac_list = 0;
264    asm volatile(
265        " .insn s,0xb2b10000,0(0)\n" /* stfl */
266        "0:\n"
267        EX_TABLE(0b,0b) : "=m" (S390_lowcore.stfl_fac_list));
268    memcpy(&S390_lowcore.stfle_fac_list, &S390_lowcore.stfl_fac_list, 4);
269    nr = 4; /* # bytes stored by stfl */
270    if (test_facility(7)) {
271        /* More facility bits available with stfle */
272        register unsigned long reg0 asm("0") = MAX_FACILITY_BIT/64 - 1;
273        asm volatile(".insn s,0xb2b00000,%0" /* stfle */
274                 : "=m" (S390_lowcore.stfle_fac_list), "+d" (reg0)
275                 : : "cc");
276        nr = (reg0 + 1) * 8; /* # bytes stored by stfle */
277    }
278    memset((char *) S390_lowcore.stfle_fac_list + nr, 0,
279           MAX_FACILITY_BIT/8 - nr);
280}
281
282static noinline __init void setup_hpage(void)
283{
284#ifndef CONFIG_DEBUG_PAGEALLOC
285    if (!test_facility(2) || !test_facility(8))
286        return;
287    S390_lowcore.machine_flags |= MACHINE_FLAG_HPAGE;
288    __ctl_set_bit(0, 23);
289#endif
290}
291
292static __init void detect_mvpg(void)
293{
294#ifndef CONFIG_64BIT
295    int rc;
296
297    asm volatile(
298        " la 0,0\n"
299        " mvpg %2,%2\n"
300        "0: la %0,0\n"
301        "1:\n"
302        EX_TABLE(0b,1b)
303        : "=d" (rc) : "0" (-EOPNOTSUPP), "a" (0) : "memory", "cc", "0");
304    if (!rc)
305        S390_lowcore.machine_flags |= MACHINE_FLAG_MVPG;
306#endif
307}
308
309static __init void detect_ieee(void)
310{
311#ifndef CONFIG_64BIT
312    int rc, tmp;
313
314    asm volatile(
315        " efpc %1,0\n"
316        "0: la %0,0\n"
317        "1:\n"
318        EX_TABLE(0b,1b)
319        : "=d" (rc), "=d" (tmp): "0" (-EOPNOTSUPP) : "cc");
320    if (!rc)
321        S390_lowcore.machine_flags |= MACHINE_FLAG_IEEE;
322#endif
323}
324
325static __init void detect_csp(void)
326{
327#ifndef CONFIG_64BIT
328    int rc;
329
330    asm volatile(
331        " la 0,0\n"
332        " la 1,0\n"
333        " la 2,4\n"
334        " csp 0,2\n"
335        "0: la %0,0\n"
336        "1:\n"
337        EX_TABLE(0b,1b)
338        : "=d" (rc) : "0" (-EOPNOTSUPP) : "cc", "0", "1", "2");
339    if (!rc)
340        S390_lowcore.machine_flags |= MACHINE_FLAG_CSP;
341#endif
342}
343
344static __init void detect_diag9c(void)
345{
346    unsigned int cpu_address;
347    int rc;
348
349    cpu_address = stap();
350    asm volatile(
351        " diag %2,0,0x9c\n"
352        "0: la %0,0\n"
353        "1:\n"
354        EX_TABLE(0b,1b)
355        : "=d" (rc) : "0" (-EOPNOTSUPP), "d" (cpu_address) : "cc");
356    if (!rc)
357        S390_lowcore.machine_flags |= MACHINE_FLAG_DIAG9C;
358}
359
360static __init void detect_diag44(void)
361{
362#ifdef CONFIG_64BIT
363    int rc;
364
365    asm volatile(
366        " diag 0,0,0x44\n"
367        "0: la %0,0\n"
368        "1:\n"
369        EX_TABLE(0b,1b)
370        : "=d" (rc) : "0" (-EOPNOTSUPP) : "cc");
371    if (!rc)
372        S390_lowcore.machine_flags |= MACHINE_FLAG_DIAG44;
373#endif
374}
375
376static __init void detect_machine_facilities(void)
377{
378#ifdef CONFIG_64BIT
379    if (test_facility(3))
380        S390_lowcore.machine_flags |= MACHINE_FLAG_IDTE;
381    if (test_facility(8))
382        S390_lowcore.machine_flags |= MACHINE_FLAG_PFMF;
383    if (test_facility(11))
384        S390_lowcore.machine_flags |= MACHINE_FLAG_TOPOLOGY;
385    if (test_facility(27))
386        S390_lowcore.machine_flags |= MACHINE_FLAG_MVCOS;
387    if (test_facility(40))
388        S390_lowcore.machine_flags |= MACHINE_FLAG_SPP;
389#endif
390}
391
392static __init void rescue_initrd(void)
393{
394#ifdef CONFIG_BLK_DEV_INITRD
395    /*
396     * Move the initrd right behind the bss section in case it starts
397     * within the bss section. So we don't overwrite it when the bss
398     * section gets cleared.
399     */
400    if (!INITRD_START || !INITRD_SIZE)
401        return;
402    if (INITRD_START >= (unsigned long) __bss_stop)
403        return;
404    memmove(__bss_stop, (void *) INITRD_START, INITRD_SIZE);
405    INITRD_START = (unsigned long) __bss_stop;
406#endif
407}
408
409/* Set up boot command line */
410static void __init append_to_cmdline(size_t (*ipl_data)(char *, size_t))
411{
412    char *parm, *delim;
413    size_t rc, len;
414
415    len = strlen(boot_command_line);
416
417    delim = boot_command_line + len; /* '\0' character position */
418    parm = boot_command_line + len + 1; /* append right after '\0' */
419
420    rc = ipl_data(parm, COMMAND_LINE_SIZE - len - 1);
421    if (rc) {
422        if (*parm == '=')
423            memmove(boot_command_line, parm + 1, rc);
424        else
425            *delim = ' '; /* replace '\0' with space */
426    }
427}
428
429static void __init setup_boot_command_line(void)
430{
431    int i;
432
433    /* convert arch command line to ascii */
434    for (i = 0; i < ARCH_COMMAND_LINE_SIZE; i++)
435        if (COMMAND_LINE[i] & 0x80)
436            break;
437    if (i < ARCH_COMMAND_LINE_SIZE)
438        EBCASC(COMMAND_LINE, ARCH_COMMAND_LINE_SIZE);
439    COMMAND_LINE[ARCH_COMMAND_LINE_SIZE-1] = 0;
440
441    /* copy arch command line */
442    strlcpy(boot_command_line, strstrip(COMMAND_LINE),
443        ARCH_COMMAND_LINE_SIZE);
444
445    /* append IPL PARM data to the boot command line */
446    if (MACHINE_IS_VM)
447        append_to_cmdline(append_ipl_vmparm);
448
449    append_to_cmdline(append_ipl_scpdata);
450}
451
452
453/*
454 * Save ipl parameters, clear bss memory, initialize storage keys
455 * and create a kernel NSS at startup if the SAVESYS= parm is defined
456 */
457void __init startup_init(void)
458{
459    reset_tod_clock();
460    ipl_save_parameters();
461    rescue_initrd();
462    clear_bss_section();
463    init_kernel_storage_key();
464    lockdep_init();
465    lockdep_off();
466    sort_main_extable();
467    setup_lowcore_early();
468    setup_facility_list();
469    detect_machine_type();
470    ipl_update_parameters();
471    setup_boot_command_line();
472    create_kernel_nss();
473    detect_mvpg();
474    detect_ieee();
475    detect_csp();
476    detect_diag9c();
477    detect_diag44();
478    detect_machine_facilities();
479    setup_hpage();
480    sclp_facilities_detect();
481    detect_memory_layout(memory_chunk);
482#ifdef CONFIG_DYNAMIC_FTRACE
483    S390_lowcore.ftrace_func = (unsigned long)ftrace_caller;
484#endif
485    lockdep_on();
486}
487

Archive Download this file



interactive