/*
* Copyright (c) 2015, 2016 Oracle and/or its affiliates. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*
* strlen(), strncmp(), strchr(), strspn() and strcspn() were copied from
* Linux kernel source (linux/lib/string.c).
*/
/*
* This entry point is entered from xen/arch/x86/boot/head.S with:
* - 0x4(%esp) = &cmdline,
* - 0x8(%esp) = &early_boot_opts.
*/
asm (
" .text \n"
" .globl _start \n"
"_start: \n"
" jmp cmdline_parse_early \n"
);
#include
#include "defs.h"
#include "video.h"
/* Keep in sync with trampoline.S:early_boot_opts label! */
typedef struct __packed {
u8 skip_realmode;
u8 opt_edd;
u8 opt_edid;
u8 padding;
#ifdef CONFIG_VIDEO
u16 boot_vid_mode;
u16 vesa_width;
u16 vesa_height;
u16 vesa_depth;
#endif
} early_boot_opts_t;
/*
* Space and TAB are obvious delimiters. However, I am
* adding "\n" and "\r" here too. Just in case when
* crazy bootloader/user puts them somewhere.
*/
static const char delim_chars_comma[] = ", \n\r\t";
#define delim_chars (delim_chars_comma + 1)
static size_t strlen(const char *s)
{
const char *sc;
for ( sc = s; *sc != '\0'; ++sc )
/* nothing */;
return sc - s;
}
static int strncmp(const char *cs, const char *ct, size_t count)
{
unsigned char c1, c2;
while ( count )
{
c1 = *cs++;
c2 = *ct++;
if ( c1 != c2 )
return c1 < c2 ? -1 : 1;
if ( !c1 )
break;
count--;
}
return 0;
}
static char *strchr(const char *s, int c)
{
for ( ; *s != (char)c; ++s )
if ( *s == '\0' )
return NULL;
return (char *)s;
}
static size_t strspn(const char *s, const char *accept)
{
const char *p;
const char *a;
size_t count = 0;
for ( p = s; *p != '\0'; ++p )
{
for ( a = accept; *a != '\0'; ++a )
{
if ( *p == *a )
break;
}
if ( *a == '\0' )
return count;
++count;
}
return count;
}
static size_t strcspn(const char *s, const char *reject)
{
const char *p;
const char *r;
size_t count = 0;
for ( p = s; *p != '\0'; ++p )
{
for ( r = reject; *r != '\0'; ++r )
{
if ( *p == *r )
return count;
}
++count;
}
return count;
}
static unsigned int __maybe_unused strtoui(
const char *s, const char *stop, const char **next)
{
char base = 10, l;
unsigned long long res = 0;
if ( *s == '0' )
base = (tolower(*++s) == 'x') ? (++s, 16) : 8;
for ( ; *s != '\0'; ++s )
{
if ( stop && strchr(stop, *s) )
goto out;
if ( *s < '0' || (*s > '7' && base == 8) )
{
res = UINT_MAX;
goto out;
}
l = tolower(*s);
if ( *s > '9' && (base != 16 || l < 'a' || l > 'f') )
{
res = UINT_MAX;
goto out;
}
res *= base;
res += (l >= 'a') ? (l - 'a' + 10) : (*s - '0');
if ( res >= UINT_MAX )
{
res = UINT_MAX;
goto out;
}
}
out:
if ( next )
*next = s;
return res;
}
static int strmaxcmp(const char *cs, const char *ct, const char *_delim_chars)
{
return strncmp(cs, ct, max(strcspn(cs, _delim_chars), strlen(ct)));
}
static int __maybe_unused strsubcmp(const char *cs, const char *ct)
{
return strncmp(cs, ct, strlen(ct));
}
static const char *find_opt(const char *cmdline, const char *opt, bool arg)
{
size_t lc, lo;
lo = strlen(opt);
for ( ; ; )
{
cmdline += strspn(cmdline, delim_chars);
if ( *cmdline == '\0' )
return NULL;
if ( !strmaxcmp(cmdline, "--", delim_chars) )
return NULL;
lc = strcspn(cmdline, delim_chars);
if ( !strncmp(cmdline, opt, arg ? lo : max(lc, lo)) )
return cmdline + lo;
cmdline += lc;
}
}
static bool skip_realmode(const char *cmdline)
{
return find_opt(cmdline, "no-real-mode", false) || find_opt(cmdline, "tboot=", true);
}
static u8 edd_parse(const char *cmdline)
{
const char *c;
c = find_opt(cmdline, "edd=", true);
if ( !c )
return 0;
if ( !strmaxcmp(c, "off", delim_chars) )
return 2;
return !strmaxcmp(c, "skipmbr", delim_chars);
}
static u8 edid_parse(const char *cmdline)
{
const char *c;
c = find_opt(cmdline, "edid=", true);
if ( !c )
return 0;
if ( !strmaxcmp(c, "force", delim_chars) )
return 2;
return !strmaxcmp(c, "no", delim_chars);
}
#ifdef CONFIG_VIDEO
static u16 rows2vmode(unsigned int rows)
{
switch ( rows )
{
case 25:
return VIDEO_80x25;
case 28:
return VIDEO_80x28;
case 30:
return VIDEO_80x30;
case 34:
return VIDEO_80x34;
case 43:
return VIDEO_80x43;
case 50:
return VIDEO_80x50;
case 60:
return VIDEO_80x60;
default:
return ASK_VGA;
}
}
static void vga_parse(const char *cmdline, early_boot_opts_t *ebo)
{
const char *c;
unsigned int tmp, vesa_depth, vesa_height, vesa_width;
c = find_opt(cmdline, "vga=", true);
if ( !c )
return;
ebo->boot_vid_mode = ASK_VGA;
if ( !strmaxcmp(c, "current", delim_chars_comma) )
ebo->boot_vid_mode = VIDEO_CURRENT_MODE;
else if ( !strsubcmp(c, "text-80x") )
{
c += strlen("text-80x");
ebo->boot_vid_mode = rows2vmode(strtoui(c, delim_chars_comma, NULL));
}
else if ( !strsubcmp(c, "gfx-") )
{
vesa_width = strtoui(c + strlen("gfx-"), "x", &c);
if ( vesa_width > U16_MAX )
return;
/*
* Increment c outside of strtoui() because otherwise some
* compiler may complain with following message:
* warning: operation on 'c' may be undefined.
*/
++c;
vesa_height = strtoui(c, "x", &c);
if ( vesa_height > U16_MAX )
return;
vesa_depth = strtoui(++c, delim_chars_comma, NULL);
if ( vesa_depth > U16_MAX )
return;
ebo->vesa_width = vesa_width;
ebo->vesa_height = vesa_height;
ebo->vesa_depth = vesa_depth;
ebo->boot_vid_mode = VIDEO_VESA_BY_SIZE;
}
else if ( !strsubcmp(c, "mode-") )
{
tmp = strtoui(c + strlen("mode-"), delim_chars_comma, NULL);
if ( tmp > U16_MAX )
return;
ebo->boot_vid_mode = tmp;
}
}
#endif
void __stdcall cmdline_parse_early(const char *cmdline, early_boot_opts_t *ebo)
{
if ( !cmdline )
return;
ebo->skip_realmode = skip_realmode(cmdline);
ebo->opt_edd = edd_parse(cmdline);
ebo->opt_edid = edid_parse(cmdline);
#ifdef CONFIG_VIDEO
vga_parse(cmdline, ebo);
#endif
}