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