1 /*
2 * Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved.
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 * strlen(), strncmp(), strchr(), strspn() and strcspn() were copied from
18 * Linux kernel source (linux/lib/string.c).
19 */
20
21 /*
22 * This entry point is entered from xen/arch/x86/boot/head.S with:
23 * - 0x4(%esp) = &cmdline,
24 * - 0x8(%esp) = &early_boot_opts.
25 */
26 asm (
27 " .text \n"
28 " .globl _start \n"
29 "_start: \n"
30 " jmp cmdline_parse_early \n"
31 );
32
33 #include <xen/kconfig.h>
34 #include "defs.h"
35 #include "video.h"
36
37 /* Keep in sync with trampoline.S:early_boot_opts label! */
38 typedef struct __packed {
39 u8 skip_realmode;
40 u8 opt_edd;
41 u8 opt_edid;
42 u8 padding;
43 #ifdef CONFIG_VIDEO
44 u16 boot_vid_mode;
45 u16 vesa_width;
46 u16 vesa_height;
47 u16 vesa_depth;
48 #endif
49 } early_boot_opts_t;
50
51 /*
52 * Space and TAB are obvious delimiters. However, I am
53 * adding "\n" and "\r" here too. Just in case when
54 * crazy bootloader/user puts them somewhere.
55 */
56 static const char delim_chars_comma[] = ", \n\r\t";
57
58 #define delim_chars (delim_chars_comma + 1)
59
strlen(const char * s)60 static size_t strlen(const char *s)
61 {
62 const char *sc;
63
64 for ( sc = s; *sc != '\0'; ++sc )
65 /* nothing */;
66 return sc - s;
67 }
68
strncmp(const char * cs,const char * ct,size_t count)69 static int strncmp(const char *cs, const char *ct, size_t count)
70 {
71 unsigned char c1, c2;
72
73 while ( count )
74 {
75 c1 = *cs++;
76 c2 = *ct++;
77 if ( c1 != c2 )
78 return c1 < c2 ? -1 : 1;
79 if ( !c1 )
80 break;
81 count--;
82 }
83 return 0;
84 }
85
strchr(const char * s,int c)86 static char *strchr(const char *s, int c)
87 {
88 for ( ; *s != (char)c; ++s )
89 if ( *s == '\0' )
90 return NULL;
91 return (char *)s;
92 }
93
strspn(const char * s,const char * accept)94 static size_t strspn(const char *s, const char *accept)
95 {
96 const char *p;
97 const char *a;
98 size_t count = 0;
99
100 for ( p = s; *p != '\0'; ++p )
101 {
102 for ( a = accept; *a != '\0'; ++a )
103 {
104 if ( *p == *a )
105 break;
106 }
107 if ( *a == '\0' )
108 return count;
109 ++count;
110 }
111 return count;
112 }
113
strcspn(const char * s,const char * reject)114 static size_t strcspn(const char *s, const char *reject)
115 {
116 const char *p;
117 const char *r;
118 size_t count = 0;
119
120 for ( p = s; *p != '\0'; ++p )
121 {
122 for ( r = reject; *r != '\0'; ++r )
123 {
124 if ( *p == *r )
125 return count;
126 }
127 ++count;
128 }
129 return count;
130 }
131
strtoui(const char * s,const char * stop,const char ** next)132 static unsigned int __maybe_unused strtoui(
133 const char *s, const char *stop, const char **next)
134 {
135 char base = 10, l;
136 unsigned long long res = 0;
137
138 if ( *s == '0' )
139 base = (tolower(*++s) == 'x') ? (++s, 16) : 8;
140
141 for ( ; *s != '\0'; ++s )
142 {
143 if ( stop && strchr(stop, *s) )
144 goto out;
145
146 if ( *s < '0' || (*s > '7' && base == 8) )
147 {
148 res = UINT_MAX;
149 goto out;
150 }
151
152 l = tolower(*s);
153
154 if ( *s > '9' && (base != 16 || l < 'a' || l > 'f') )
155 {
156 res = UINT_MAX;
157 goto out;
158 }
159
160 res *= base;
161 res += (l >= 'a') ? (l - 'a' + 10) : (*s - '0');
162
163 if ( res >= UINT_MAX )
164 {
165 res = UINT_MAX;
166 goto out;
167 }
168 }
169
170 out:
171 if ( next )
172 *next = s;
173
174 return res;
175 }
176
strmaxcmp(const char * cs,const char * ct,const char * _delim_chars)177 static int strmaxcmp(const char *cs, const char *ct, const char *_delim_chars)
178 {
179 return strncmp(cs, ct, max(strcspn(cs, _delim_chars), strlen(ct)));
180 }
181
strsubcmp(const char * cs,const char * ct)182 static int __maybe_unused strsubcmp(const char *cs, const char *ct)
183 {
184 return strncmp(cs, ct, strlen(ct));
185 }
186
find_opt(const char * cmdline,const char * opt,bool arg)187 static const char *find_opt(const char *cmdline, const char *opt, bool arg)
188 {
189 size_t lc, lo;
190
191 lo = strlen(opt);
192
193 for ( ; ; )
194 {
195 cmdline += strspn(cmdline, delim_chars);
196
197 if ( *cmdline == '\0' )
198 return NULL;
199
200 if ( !strmaxcmp(cmdline, "--", delim_chars) )
201 return NULL;
202
203 lc = strcspn(cmdline, delim_chars);
204
205 if ( !strncmp(cmdline, opt, arg ? lo : max(lc, lo)) )
206 return cmdline + lo;
207
208 cmdline += lc;
209 }
210 }
211
skip_realmode(const char * cmdline)212 static bool skip_realmode(const char *cmdline)
213 {
214 return find_opt(cmdline, "no-real-mode", false) || find_opt(cmdline, "tboot=", true);
215 }
216
edd_parse(const char * cmdline)217 static u8 edd_parse(const char *cmdline)
218 {
219 const char *c;
220
221 c = find_opt(cmdline, "edd=", true);
222
223 if ( !c )
224 return 0;
225
226 if ( !strmaxcmp(c, "off", delim_chars) )
227 return 2;
228
229 return !strmaxcmp(c, "skipmbr", delim_chars);
230 }
231
edid_parse(const char * cmdline)232 static u8 edid_parse(const char *cmdline)
233 {
234 const char *c;
235
236 c = find_opt(cmdline, "edid=", true);
237
238 if ( !c )
239 return 0;
240
241 if ( !strmaxcmp(c, "force", delim_chars) )
242 return 2;
243
244 return !strmaxcmp(c, "no", delim_chars);
245 }
246
247 #ifdef CONFIG_VIDEO
rows2vmode(unsigned int rows)248 static u16 rows2vmode(unsigned int rows)
249 {
250 switch ( rows )
251 {
252 case 25:
253 return VIDEO_80x25;
254
255 case 28:
256 return VIDEO_80x28;
257
258 case 30:
259 return VIDEO_80x30;
260
261 case 34:
262 return VIDEO_80x34;
263
264 case 43:
265 return VIDEO_80x43;
266
267 case 50:
268 return VIDEO_80x50;
269
270 case 60:
271 return VIDEO_80x60;
272
273 default:
274 return ASK_VGA;
275 }
276 }
277
vga_parse(const char * cmdline,early_boot_opts_t * ebo)278 static void vga_parse(const char *cmdline, early_boot_opts_t *ebo)
279 {
280 const char *c;
281 unsigned int tmp, vesa_depth, vesa_height, vesa_width;
282
283 c = find_opt(cmdline, "vga=", true);
284
285 if ( !c )
286 return;
287
288 ebo->boot_vid_mode = ASK_VGA;
289
290 if ( !strmaxcmp(c, "current", delim_chars_comma) )
291 ebo->boot_vid_mode = VIDEO_CURRENT_MODE;
292 else if ( !strsubcmp(c, "text-80x") )
293 {
294 c += strlen("text-80x");
295 ebo->boot_vid_mode = rows2vmode(strtoui(c, delim_chars_comma, NULL));
296 }
297 else if ( !strsubcmp(c, "gfx-") )
298 {
299 vesa_width = strtoui(c + strlen("gfx-"), "x", &c);
300
301 if ( vesa_width > U16_MAX )
302 return;
303
304 /*
305 * Increment c outside of strtoui() because otherwise some
306 * compiler may complain with following message:
307 * warning: operation on 'c' may be undefined.
308 */
309 ++c;
310 vesa_height = strtoui(c, "x", &c);
311
312 if ( vesa_height > U16_MAX )
313 return;
314
315 vesa_depth = strtoui(++c, delim_chars_comma, NULL);
316
317 if ( vesa_depth > U16_MAX )
318 return;
319
320 ebo->vesa_width = vesa_width;
321 ebo->vesa_height = vesa_height;
322 ebo->vesa_depth = vesa_depth;
323 ebo->boot_vid_mode = VIDEO_VESA_BY_SIZE;
324 }
325 else if ( !strsubcmp(c, "mode-") )
326 {
327 tmp = strtoui(c + strlen("mode-"), delim_chars_comma, NULL);
328
329 if ( tmp > U16_MAX )
330 return;
331
332 ebo->boot_vid_mode = tmp;
333 }
334 }
335 #endif
336
cmdline_parse_early(const char * cmdline,early_boot_opts_t * ebo)337 void __stdcall cmdline_parse_early(const char *cmdline, early_boot_opts_t *ebo)
338 {
339 if ( !cmdline )
340 return;
341
342 ebo->skip_realmode = skip_realmode(cmdline);
343 ebo->opt_edd = edd_parse(cmdline);
344 ebo->opt_edid = edid_parse(cmdline);
345
346 #ifdef CONFIG_VIDEO
347 vga_parse(cmdline, ebo);
348 #endif
349 }
350