Merge branch 'x86-alternatives-for-linus' of git://git.kernel.org/pub/scm/linux/kerne...
[sfrench/cifs-2.6.git] / arch / s390 / boot / ipl_parm.c
1 // SPDX-License-Identifier: GPL-2.0
2 #include <linux/kernel.h>
3 #include <linux/init.h>
4 #include <linux/ctype.h>
5 #include <asm/ebcdic.h>
6 #include <asm/sclp.h>
7 #include <asm/sections.h>
8 #include <asm/boot_data.h>
9 #include <asm/facility.h>
10 #include "boot.h"
11
12 char __bootdata(early_command_line)[COMMAND_LINE_SIZE];
13 struct ipl_parameter_block __bootdata(early_ipl_block);
14 int __bootdata(early_ipl_block_valid);
15
16 unsigned long __bootdata(memory_end);
17 int __bootdata(memory_end_set);
18 int __bootdata(noexec_disabled);
19
20 static inline int __diag308(unsigned long subcode, void *addr)
21 {
22         register unsigned long _addr asm("0") = (unsigned long)addr;
23         register unsigned long _rc asm("1") = 0;
24         unsigned long reg1, reg2;
25         psw_t old = S390_lowcore.program_new_psw;
26
27         asm volatile(
28                 "       epsw    %0,%1\n"
29                 "       st      %0,%[psw_pgm]\n"
30                 "       st      %1,%[psw_pgm]+4\n"
31                 "       larl    %0,1f\n"
32                 "       stg     %0,%[psw_pgm]+8\n"
33                 "       diag    %[addr],%[subcode],0x308\n"
34                 "1:     nopr    %%r7\n"
35                 : "=&d" (reg1), "=&a" (reg2),
36                   [psw_pgm] "=Q" (S390_lowcore.program_new_psw),
37                   [addr] "+d" (_addr), "+d" (_rc)
38                 : [subcode] "d" (subcode)
39                 : "cc", "memory");
40         S390_lowcore.program_new_psw = old;
41         return _rc;
42 }
43
44 void store_ipl_parmblock(void)
45 {
46         int rc;
47
48         rc = __diag308(DIAG308_STORE, &early_ipl_block);
49         if (rc == DIAG308_RC_OK &&
50             early_ipl_block.hdr.version <= IPL_MAX_SUPPORTED_VERSION)
51                 early_ipl_block_valid = 1;
52 }
53
54 static size_t scpdata_length(const char *buf, size_t count)
55 {
56         while (count) {
57                 if (buf[count - 1] != '\0' && buf[count - 1] != ' ')
58                         break;
59                 count--;
60         }
61         return count;
62 }
63
64 static size_t ipl_block_get_ascii_scpdata(char *dest, size_t size,
65                                           const struct ipl_parameter_block *ipb)
66 {
67         size_t count;
68         size_t i;
69         int has_lowercase;
70
71         count = min(size - 1, scpdata_length(ipb->ipl_info.fcp.scp_data,
72                                              ipb->ipl_info.fcp.scp_data_len));
73         if (!count)
74                 goto out;
75
76         has_lowercase = 0;
77         for (i = 0; i < count; i++) {
78                 if (!isascii(ipb->ipl_info.fcp.scp_data[i])) {
79                         count = 0;
80                         goto out;
81                 }
82                 if (!has_lowercase && islower(ipb->ipl_info.fcp.scp_data[i]))
83                         has_lowercase = 1;
84         }
85
86         if (has_lowercase)
87                 memcpy(dest, ipb->ipl_info.fcp.scp_data, count);
88         else
89                 for (i = 0; i < count; i++)
90                         dest[i] = tolower(ipb->ipl_info.fcp.scp_data[i]);
91 out:
92         dest[count] = '\0';
93         return count;
94 }
95
96 static void append_ipl_block_parm(void)
97 {
98         char *parm, *delim;
99         size_t len, rc = 0;
100
101         len = strlen(early_command_line);
102
103         delim = early_command_line + len;    /* '\0' character position */
104         parm = early_command_line + len + 1; /* append right after '\0' */
105
106         switch (early_ipl_block.hdr.pbt) {
107         case DIAG308_IPL_TYPE_CCW:
108                 rc = ipl_block_get_ascii_vmparm(
109                         parm, COMMAND_LINE_SIZE - len - 1, &early_ipl_block);
110                 break;
111         case DIAG308_IPL_TYPE_FCP:
112                 rc = ipl_block_get_ascii_scpdata(
113                         parm, COMMAND_LINE_SIZE - len - 1, &early_ipl_block);
114                 break;
115         }
116         if (rc) {
117                 if (*parm == '=')
118                         memmove(early_command_line, parm + 1, rc);
119                 else
120                         *delim = ' '; /* replace '\0' with space */
121         }
122 }
123
124 static inline int has_ebcdic_char(const char *str)
125 {
126         int i;
127
128         for (i = 0; str[i]; i++)
129                 if (str[i] & 0x80)
130                         return 1;
131         return 0;
132 }
133
134 void setup_boot_command_line(void)
135 {
136         COMMAND_LINE[ARCH_COMMAND_LINE_SIZE - 1] = 0;
137         /* convert arch command line to ascii if necessary */
138         if (has_ebcdic_char(COMMAND_LINE))
139                 EBCASC(COMMAND_LINE, ARCH_COMMAND_LINE_SIZE);
140         /* copy arch command line */
141         strcpy(early_command_line, strim(COMMAND_LINE));
142
143         /* append IPL PARM data to the boot command line */
144         if (early_ipl_block_valid)
145                 append_ipl_block_parm();
146 }
147
148 static void modify_facility(unsigned long nr, bool clear)
149 {
150         if (clear)
151                 __clear_facility(nr, S390_lowcore.stfle_fac_list);
152         else
153                 __set_facility(nr, S390_lowcore.stfle_fac_list);
154 }
155
156 static void check_cleared_facilities(void)
157 {
158         unsigned long als[] = { FACILITIES_ALS };
159         int i;
160
161         for (i = 0; i < ARRAY_SIZE(als); i++) {
162                 if ((S390_lowcore.stfle_fac_list[i] & als[i]) != als[i]) {
163                         sclp_early_printk("Warning: The Linux kernel requires facilities cleared via command line option\n");
164                         print_missing_facilities();
165                         break;
166                 }
167         }
168 }
169
170 static void modify_fac_list(char *str)
171 {
172         unsigned long val, endval;
173         char *endp;
174         bool clear;
175
176         while (*str) {
177                 clear = false;
178                 if (*str == '!') {
179                         clear = true;
180                         str++;
181                 }
182                 val = simple_strtoull(str, &endp, 0);
183                 if (str == endp)
184                         break;
185                 str = endp;
186                 if (*str == '-') {
187                         str++;
188                         endval = simple_strtoull(str, &endp, 0);
189                         if (str == endp)
190                                 break;
191                         str = endp;
192                         while (val <= endval) {
193                                 modify_facility(val, clear);
194                                 val++;
195                         }
196                 } else {
197                         modify_facility(val, clear);
198                 }
199                 if (*str != ',')
200                         break;
201                 str++;
202         }
203         check_cleared_facilities();
204 }
205
206 static char command_line_buf[COMMAND_LINE_SIZE] __section(.data);
207 void parse_boot_command_line(void)
208 {
209         char *param, *val;
210         bool enabled;
211         char *args;
212         int rc;
213
214         args = strcpy(command_line_buf, early_command_line);
215         while (*args) {
216                 args = next_arg(args, &param, &val);
217
218                 if (!strcmp(param, "mem")) {
219                         memory_end = memparse(val, NULL);
220                         memory_end_set = 1;
221                 }
222
223                 if (!strcmp(param, "noexec")) {
224                         rc = kstrtobool(val, &enabled);
225                         if (!rc && !enabled)
226                                 noexec_disabled = 1;
227                 }
228
229                 if (!strcmp(param, "facilities"))
230                         modify_fac_list(val);
231         }
232 }
233
234 void setup_memory_end(void)
235 {
236 #ifdef CONFIG_CRASH_DUMP
237         if (!OLDMEM_BASE && early_ipl_block_valid &&
238             early_ipl_block.hdr.pbt == DIAG308_IPL_TYPE_FCP &&
239             early_ipl_block.ipl_info.fcp.opt == DIAG308_IPL_OPT_DUMP) {
240                 if (!sclp_early_get_hsa_size(&memory_end) && memory_end)
241                         memory_end_set = 1;
242         }
243 #endif
244 }