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 u16 boot_vid_mode;
44 u16 vesa_width;
45 u16 vesa_height;
46 u16 vesa_depth;
47 } early_boot_opts_t;
48
49 /*
50 * Space and TAB are obvious delimiters. However, I am
51 * adding "\n" and "\r" here too. Just in case when
52 * crazy bootloader/user puts them somewhere.
53 */
54 static const char delim_chars_comma[] = ", \n\r\t";
55
56 #define delim_chars (delim_chars_comma + 1)
57
strlen(const char * s)58 static size_t strlen(const char *s)
59 {
60 const char *sc;
61
62 for ( sc = s; *sc != '\0'; ++sc )
63 /* nothing */;
64 return sc - s;
65 }
66
strncmp(const char * cs,const char * ct,size_t count)67 static int strncmp(const char *cs, const char *ct, size_t count)
68 {
69 unsigned char c1, c2;
70
71 while ( count )
72 {
73 c1 = *cs++;
74 c2 = *ct++;
75 if ( c1 != c2 )
76 return c1 < c2 ? -1 : 1;
77 if ( !c1 )
78 break;
79 count--;
80 }
81 return 0;
82 }
83
strchr(const char * s,int c)84 static char *strchr(const char *s, int c)
85 {
86 for ( ; *s != (char)c; ++s )
87 if ( *s == '\0' )
88 return NULL;
89 return (char *)s;
90 }
91
strspn(const char * s,const char * accept)92 static size_t strspn(const char *s, const char *accept)
93 {
94 const char *p;
95 const char *a;
96 size_t count = 0;
97
98 for ( p = s; *p != '\0'; ++p )
99 {
100 for ( a = accept; *a != '\0'; ++a )
101 {
102 if ( *p == *a )
103 break;
104 }
105 if ( *a == '\0' )
106 return count;
107 ++count;
108 }
109 return count;
110 }
111
strcspn(const char * s,const char * reject)112 static size_t strcspn(const char *s, const char *reject)
113 {
114 const char *p;
115 const char *r;
116 size_t count = 0;
117
118 for ( p = s; *p != '\0'; ++p )
119 {
120 for ( r = reject; *r != '\0'; ++r )
121 {
122 if ( *p == *r )
123 return count;
124 }
125 ++count;
126 }
127 return count;
128 }
129
strtoui(const char * s,const char * stop,const char ** next)130 static unsigned int strtoui(const char *s, const char *stop, const char **next)
131 {
132 char base = 10, l;
133 unsigned long long res = 0;
134
135 if ( *s == '0' )
136 base = (tolower(*++s) == 'x') ? (++s, 16) : 8;
137
138 for ( ; *s != '\0'; ++s )
139 {
140 if ( stop && strchr(stop, *s) )
141 goto out;
142
143 if ( *s < '0' || (*s > '7' && base == 8) )
144 {
145 res = UINT_MAX;
146 goto out;
147 }
148
149 l = tolower(*s);
150
151 if ( *s > '9' && (base != 16 || l < 'a' || l > 'f') )
152 {
153 res = UINT_MAX;
154 goto out;
155 }
156
157 res *= base;
158 res += (l >= 'a') ? (l - 'a' + 10) : (*s - '0');
159
160 if ( res >= UINT_MAX )
161 {
162 res = UINT_MAX;
163 goto out;
164 }
165 }
166
167 out:
168 if ( next )
169 *next = s;
170
171 return res;
172 }
173
strmaxcmp(const char * cs,const char * ct,const char * _delim_chars)174 static int strmaxcmp(const char *cs, const char *ct, const char *_delim_chars)
175 {
176 return strncmp(cs, ct, max(strcspn(cs, _delim_chars), strlen(ct)));
177 }
178
strsubcmp(const char * cs,const char * ct)179 static int strsubcmp(const char *cs, const char *ct)
180 {
181 return strncmp(cs, ct, strlen(ct));
182 }
183
find_opt(const char * cmdline,const char * opt,bool arg)184 static const char *find_opt(const char *cmdline, const char *opt, bool arg)
185 {
186 size_t lc, lo;
187
188 lo = strlen(opt);
189
190 for ( ; ; )
191 {
192 cmdline += strspn(cmdline, delim_chars);
193
194 if ( *cmdline == '\0' )
195 return NULL;
196
197 if ( !strmaxcmp(cmdline, "--", delim_chars) )
198 return NULL;
199
200 lc = strcspn(cmdline, delim_chars);
201
202 if ( !strncmp(cmdline, opt, arg ? lo : max(lc, lo)) )
203 return cmdline + lo;
204
205 cmdline += lc;
206 }
207 }
208
skip_realmode(const char * cmdline)209 static bool skip_realmode(const char *cmdline)
210 {
211 return find_opt(cmdline, "no-real-mode", false) || find_opt(cmdline, "tboot=", true);
212 }
213
edd_parse(const char * cmdline)214 static u8 edd_parse(const char *cmdline)
215 {
216 const char *c;
217
218 c = find_opt(cmdline, "edd=", true);
219
220 if ( !c )
221 return 0;
222
223 if ( !strmaxcmp(c, "off", delim_chars) )
224 return 2;
225
226 return !strmaxcmp(c, "skipmbr", delim_chars);
227 }
228
edid_parse(const char * cmdline)229 static u8 edid_parse(const char *cmdline)
230 {
231 const char *c;
232
233 c = find_opt(cmdline, "edid=", true);
234
235 if ( !c )
236 return 0;
237
238 if ( !strmaxcmp(c, "force", delim_chars) )
239 return 2;
240
241 return !strmaxcmp(c, "no", delim_chars);
242 }
243
rows2vmode(unsigned int rows)244 static u16 rows2vmode(unsigned int rows)
245 {
246 switch ( rows )
247 {
248 case 25:
249 return VIDEO_80x25;
250
251 case 28:
252 return VIDEO_80x28;
253
254 case 30:
255 return VIDEO_80x30;
256
257 case 34:
258 return VIDEO_80x34;
259
260 case 43:
261 return VIDEO_80x43;
262
263 case 50:
264 return VIDEO_80x50;
265
266 case 60:
267 return VIDEO_80x60;
268
269 default:
270 return ASK_VGA;
271 }
272 }
273
vga_parse(const char * cmdline,early_boot_opts_t * ebo)274 static void vga_parse(const char *cmdline, early_boot_opts_t *ebo)
275 {
276 const char *c;
277 unsigned int tmp, vesa_depth, vesa_height, vesa_width;
278
279 c = find_opt(cmdline, "vga=", true);
280
281 if ( !c )
282 return;
283
284 ebo->boot_vid_mode = ASK_VGA;
285
286 if ( !strmaxcmp(c, "current", delim_chars_comma) )
287 ebo->boot_vid_mode = VIDEO_CURRENT_MODE;
288 else if ( !strsubcmp(c, "text-80x") )
289 {
290 c += strlen("text-80x");
291 ebo->boot_vid_mode = rows2vmode(strtoui(c, delim_chars_comma, NULL));
292 }
293 else if ( !strsubcmp(c, "gfx-") )
294 {
295 vesa_width = strtoui(c + strlen("gfx-"), "x", &c);
296
297 if ( vesa_width > U16_MAX )
298 return;
299
300 /*
301 * Increment c outside of strtoui() because otherwise some
302 * compiler may complain with following message:
303 * warning: operation on 'c' may be undefined.
304 */
305 ++c;
306 vesa_height = strtoui(c, "x", &c);
307
308 if ( vesa_height > U16_MAX )
309 return;
310
311 vesa_depth = strtoui(++c, delim_chars_comma, NULL);
312
313 if ( vesa_depth > U16_MAX )
314 return;
315
316 ebo->vesa_width = vesa_width;
317 ebo->vesa_height = vesa_height;
318 ebo->vesa_depth = vesa_depth;
319 ebo->boot_vid_mode = VIDEO_VESA_BY_SIZE;
320 }
321 else if ( !strsubcmp(c, "mode-") )
322 {
323 tmp = strtoui(c + strlen("mode-"), delim_chars_comma, NULL);
324
325 if ( tmp > U16_MAX )
326 return;
327
328 ebo->boot_vid_mode = tmp;
329 }
330 }
331
cmdline_parse_early(const char * cmdline,early_boot_opts_t * ebo)332 void __stdcall cmdline_parse_early(const char *cmdline, early_boot_opts_t *ebo)
333 {
334 if ( !cmdline )
335 return;
336
337 ebo->skip_realmode = skip_realmode(cmdline);
338 ebo->opt_edd = edd_parse(cmdline);
339 ebo->opt_edid = edid_parse(cmdline);
340
341 if ( IS_ENABLED(CONFIG_VIDEO) )
342 vga_parse(cmdline, ebo);
343 }
344