diff --git a/.gitignore b/.gitignore
index cc64fc9..9aa47a1 100644
--- a/.gitignore
+++ b/.gitignore
@@ -247,9 +247,10 @@ xen/arch/arm/xen.lds
 xen/arch/x86/asm-offsets.s
 xen/arch/x86/boot/mkelf32
 xen/arch/x86/xen.lds
+xen/arch/x86/boot/cmdline.S
 xen/arch/x86/boot/reloc.S
-xen/arch/x86/boot/reloc.bin
-xen/arch/x86/boot/reloc.lnk
+xen/arch/x86/boot/*.bin
+xen/arch/x86/boot/*.lnk
 xen/arch/x86/efi.lds
 xen/arch/x86/efi/check.efi
 xen/arch/x86/efi/disabled
diff --git a/xen/arch/x86/Makefile b/xen/arch/x86/Makefile
index 2766bff..d8fe0f1 100644
--- a/xen/arch/x86/Makefile
+++ b/xen/arch/x86/Makefile
@@ -224,6 +224,6 @@ clean::
 	rm -f asm-offsets.s *.lds boot/*.o boot/*~ boot/core boot/mkelf32
 	rm -f $(BASEDIR)/.xen-syms.[0-9]* boot/.*.d
 	rm -f $(BASEDIR)/.xen.efi.[0-9]* efi/*.efi efi/disabled efi/mkreloc
-	rm -f boot/reloc.S boot/reloc.lnk boot/reloc.bin
+	rm -f boot/cmdline.S boot/reloc.S boot/*.lnk boot/*.bin
 	rm -f note.o
 	$(MAKE) -f $(BASEDIR)/Rules.mk -C test clean
diff --git a/xen/arch/x86/boot/Makefile b/xen/arch/x86/boot/Makefile
index 06893d8..4a1bc45 100644
--- a/xen/arch/x86/boot/Makefile
+++ b/xen/arch/x86/boot/Makefile
@@ -1,9 +1,16 @@
 obj-bin-y += head.o
 
-RELOC_DEPS = $(BASEDIR)/include/asm-x86/config.h $(BASEDIR)/include/xen/multiboot.h \
+DEFS_H_DEPS = $(BASEDIR)/include/xen/stdbool.h
+
+CMDLINE_DEPS = defs.h $(DEFS_H_DEPS) video.h
+
+RELOC_DEPS = defs.h $(DEFS_H_DEPS) $(BASEDIR)/include/xen/multiboot.h \
 	     $(BASEDIR)/include/xen/multiboot2.h
 
-head.o: reloc.S
+head.o: cmdline.S reloc.S
+
+cmdline.S: cmdline.c $(CMDLINE_DEPS)
+	$(MAKE) -f build32.mk $@ CMDLINE_DEPS="$(CMDLINE_DEPS)"
 
 reloc.S: reloc.c $(RELOC_DEPS)
 	$(MAKE) -f build32.mk $@ RELOC_DEPS="$(RELOC_DEPS)"
diff --git a/xen/arch/x86/boot/build32.mk b/xen/arch/x86/boot/build32.mk
index 39e6453..3d01698 100644
--- a/xen/arch/x86/boot/build32.mk
+++ b/xen/arch/x86/boot/build32.mk
@@ -32,6 +32,8 @@ CFLAGS := $(filter-out -flto,$(CFLAGS))
 %.o: %.c
 	$(CC) $(CFLAGS) -c -fpic $< -o $@
 
+cmdline.o: cmdline.c $(CMDLINE_DEPS)
+
 reloc.o: reloc.c $(RELOC_DEPS)
 
 .PRECIOUS: %.bin %.lnk
diff --git a/xen/arch/x86/boot/cmdline.S b/xen/arch/x86/boot/cmdline.S
deleted file mode 100644
index 00687eb..0000000
--- a/xen/arch/x86/boot/cmdline.S
+++ /dev/null
@@ -1,367 +0,0 @@
-/******************************************************************************
- * cmdline.S
- *
- * Early command-line parsing.
- */
-
-        .code32
-
-#include "video.h"
-
-# NB. String pointer on stack is modified to point past parsed digits.
-.Latoi:
-        push    %ebx
-        push    %ecx
-        push    %edx
-        push    %esi
-        xor     %ebx,%ebx       /* %ebx = accumulator */
-        mov     $10,%ecx        /* %ecx = base (default base 10) */
-        mov     16+4(%esp),%esi /* %esi = pointer into ascii string. */
-        lodsb
-        cmpb    $'0',%al
-        jne     2f
-        mov     $8,%ecx         /* Prefix '0' => octal (base 8) */
-        lodsb
-        cmpb    $'x',%al
-        jne     2f
-        mov     $16,%ecx        /* Prefix '0x' => hex (base 16) */
-1:      lodsb
-2:      sub     $'0',%al
-        jb      4f
-        cmp     $9,%al
-        jbe     3f
-        sub     $'A'-'0'-10,%al
-        jb      4f
-        cmp     $15,%al
-        jbe     3f
-        sub     $'a'-'A',%al
-        jb      4f
-3:      cmp     %cl,%al
-        jae     4f
-        movzbl  %al,%eax
-        xchg    %eax,%ebx
-        mul     %ecx
-        xchg    %eax,%ebx
-        add     %eax,%ebx
-        jmp     1b
-4:      mov     %ebx,%eax
-        dec     %esi
-        mov     %esi,16+4(%esp)
-        pop     %esi
-        pop     %edx
-        pop     %ecx
-        pop     %ebx
-        ret
-
-.Lstrstr:
-        push    %ecx
-        push    %edx
-        push    %esi
-        push    %edi
-        xor     %eax,%eax
-        xor     %ecx,%ecx
-        not     %ecx
-        mov     16+4(%esp),%esi
-        mov     16+8(%esp),%edi
-        repne   scasb
-        not     %ecx
-        dec     %ecx
-        mov     %ecx,%edx
-1:      mov     16+8(%esp),%edi
-        mov     %esi,%eax
-        mov     %edx,%ecx
-        repe    cmpsb
-        je      2f
-        xchg    %eax,%esi
-        inc     %esi
-        cmpb    $0,-1(%eax)
-        jne     1b
-        xor     %eax,%eax
-2:      pop     %edi
-        pop     %esi
-        pop     %edx
-        pop     %ecx
-        ret
-
-.Lstr_prefix:
-        push    %esi
-        push    %edi
-        mov     8+4(%esp),%esi /* 1st arg is prefix string */
-        mov     8+8(%esp),%edi /* 2nd arg is main string */
-1:      lodsb
-        test    %al,%al
-        jz      2f
-        scasb
-        je      1b
-        sbb     %eax,%eax
-        or      $1,%al
-        jmp     3f
-2:      xor     %eax,%eax
-3:      pop     %edi
-        pop     %esi
-        ret
-
-.Lstrlen:
-        push    %ecx
-        push    %esi
-        push    %edi
-        xor     %eax,%eax
-        xor     %ecx,%ecx
-        not     %ecx
-        mov     12+4(%esp),%edi
-        repne   scasb
-        not     %ecx
-        dec     %ecx
-        mov     %ecx,%eax
-        pop     %edi
-        pop     %esi
-        pop     %ecx
-        ret
-
-.Lfind_option:
-        mov     4(%esp),%eax
-        dec     %eax
-        push    %ebx
-1:      pushl   4+8(%esp)
-        inc     %eax
-        push    %eax
-        call    .Lstrstr
-        add     $8,%esp
-        test    %eax,%eax
-        jz      3f
-        cmp     %eax,4+4(%esp)
-        je      2f
-        cmpb    $' ',-1(%eax)
-        jne     1b
-2:      mov     %eax,%ebx
-        pushl   4+8(%esp)
-        call    .Lstrlen
-        add     $4,%esp
-        xadd    %eax,%ebx
-        /* NUL check (as $'\0' == 0x30 in GAS) */
-        cmpb    $0,(%ebx)
-        je      3f
-        cmpb    $' ',(%ebx)
-        je      3f
-        cmpb    $'=',(%ebx)
-        jne     1b
-3:      pop     %ebx
-        ret
-
-cmdline_parse_early:
-        pusha
-
-        /* Bail if there is no command line to parse. */
-        mov     sym_phys(multiboot_ptr),%ebx
-        mov     MB_flags(%ebx),%eax
-        test    $4,%al
-        jz      .Lcmdline_exit
-        mov     MB_cmdline(%ebx),%eax
-        test    %eax,%eax
-        jz      .Lcmdline_exit
-
-        /* Check for 'no-real-mode' command-line option. */
-        pushl   $sym_phys(.Lno_rm_opt)
-        pushl   MB_cmdline(%ebx)
-        call    .Lfind_option
-        test    %eax,%eax
-        setnz   %al
-        or      %al,sym_phys(skip_realmode)
-
-        /* Check for 'tboot=' command-line option. */
-        movl    $sym_phys(.Ltboot_opt),4(%esp)
-        call    .Lfind_option
-        test    %eax,%eax
-        setnz   %al
-        or      %al,sym_phys(skip_realmode) /* tboot= implies no-real-mode */
-
-.Lparse_edd:
-        /* Check for 'edd=' command-line option. */
-        movl    $sym_phys(.Ledd_opt),4(%esp)
-        call    .Lfind_option
-        test    %eax,%eax
-        jz      .Lparse_edid
-        cmpb    $'=',3(%eax)
-        jne     .Lparse_edid
-        add     $4,%eax
-        movb    $2,sym_phys(opt_edd)  /* opt_edd=2: edd=off */
-        cmpw    $0x666f,(%eax)            /* 0x666f == "of" */
-        je      .Lparse_edid
-        decb    sym_phys(opt_edd)     /* opt_edd=1: edd=skipmbr */
-        cmpw    $0x6b73,(%eax)            /* 0x6b73 == "sk" */
-        je      .Lparse_edid
-        decb    sym_phys(opt_edd)     /* opt_edd=0: edd=on (default) */
-
-.Lparse_edid:
-        /* Check for 'edid=' command-line option. */
-        movl    $sym_phys(.Ledid_opt),4(%esp)
-        call    .Lfind_option
-        test    %eax,%eax
-        jz      .Lparse_vga
-        cmpb    $'=',4(%eax)
-        jne     .Lparse_vga
-        add     $5,%eax
-        mov     %eax,%ebx
-        push    %ebx
-        pushl   $sym_phys(.Ledid_force)
-        call    .Lstr_prefix
-        add     $8,%esp
-        movb    $2,sym_phys(opt_edid) /* opt_edid=2: edid=force */
-        test    %eax,%eax
-        jz      .Lparse_vga
-        push    %ebx
-        pushl   $sym_phys(.Ledid_no)
-        call    .Lstr_prefix
-        add     $8,%esp
-        decb    sym_phys(opt_edid)    /* opt_edid=1: edid=no */
-        test    %eax,%eax
-        jz      .Lparse_vga
-        decb    sym_phys(opt_edid)    /* opt_edid=0: default */
-
-.Lparse_vga:
-        /* Check for 'vga=' command-line option. */
-        movl    $sym_phys(.Lvga_opt),4(%esp)
-        call    .Lfind_option
-        add     $8,%esp
-        test    %eax,%eax
-        jz      .Lcmdline_exit
-        cmpb    $'=',3(%eax)
-        jne     .Lcmdline_exit
-        add     $4,%eax
-
-        /* Found the 'vga=' option. Default option is to display vga menu. */
-        movw    $ASK_VGA,sym_phys(boot_vid_mode)
-
-        /* Check for 'vga=text-80x<rows>. */
-        mov     %eax,%ebx
-        push    %ebx
-        pushl   $sym_phys(.Lvga_text80)
-        call    .Lstr_prefix
-        add     $8,%esp
-        test    %eax,%eax
-        jnz     .Lparse_vga_gfx
-
-        /* We have 'vga=text-80x<rows>'. */
-        add     $8,%ebx
-        push    %ebx
-        call    .Latoi
-        add     $4,%esp
-        mov     %ax,%bx
-        lea     sym_phys(.Lvga_text_modes),%esi
-1:      lodsw
-        test    %ax,%ax
-        jz      .Lcmdline_exit
-        cmp     %ax,%bx
-        lodsw
-        jne     1b
-        mov     %ax,sym_phys(boot_vid_mode)
-        jmp     .Lcmdline_exit
-
-.Lparse_vga_gfx:
-        /* Check for 'vga=gfx-<width>x<height>x<depth>'. */
-        push    %ebx
-        pushl   $sym_phys(.Lvga_gfx)
-        call    .Lstr_prefix
-        add     $8,%esp
-        test    %eax,%eax
-        jnz     .Lparse_vga_mode
-
-        /* We have 'vga=gfx-<width>x<height>x<depth>'. */
-        /* skip 'gfx-' */
-        add     $4,%ebx
-        /* parse <width> */
-        push    %ebx
-        call    .Latoi
-        pop     %esi
-        mov     %ax,sym_phys(vesa_size)+0
-        /* skip 'x' */
-        lodsb
-        cmpb    $'x',%al
-        jne     .Lcmdline_exit
-        /* parse <height> */
-        push    %esi
-        call    .Latoi
-        pop     %esi
-        mov     %ax,sym_phys(vesa_size)+2
-        /* skip 'x' */
-        lodsb
-        cmpb    $'x',%al
-        jne     .Lcmdline_exit
-        /* parse <depth> */
-        push    %esi
-        call    .Latoi
-        pop     %esi
-        mov     %ax,sym_phys(vesa_size)+4
-        /* commit to vesa mode */
-        movw    $VIDEO_VESA_BY_SIZE,sym_phys(boot_vid_mode)
-        jmp     .Lcmdline_exit
-
-.Lparse_vga_mode:
-        /* Check for 'vga=mode-<mode>'. */
-        push    %ebx
-        pushl   $sym_phys(.Lvga_mode)
-        call    .Lstr_prefix
-        add     $8,%esp
-        test    %eax,%eax
-        jnz     .Lparse_vga_current
-
-        /* We have 'vga=mode-<mode>'. */
-        add     $5,%ebx
-        push    %ebx
-        call    .Latoi
-        add     $4,%esp
-        mov     %ax,sym_phys(boot_vid_mode)
-        jmp     .Lcmdline_exit
-
-.Lparse_vga_current:
-        /* Check for 'vga=current'. */
-        push    %ebx
-        pushl   $sym_phys(.Lvga_current)
-        call    .Lstr_prefix
-        add     $8,%esp
-        test    %eax,%eax
-        jnz     .Lcmdline_exit
-
-        /* We have 'vga=current'. */
-        movw    $VIDEO_CURRENT_MODE,sym_phys(boot_vid_mode)
-
-.Lcmdline_exit:
-        popa
-        ret
-
-        .pushsection .init.rodata, "a", @progbits
-
-.Lvga_text_modes: /* rows, mode_number */
-        .word   25,VIDEO_80x25
-        .word   50,VIDEO_80x50
-        .word   43,VIDEO_80x43
-        .word   28,VIDEO_80x28
-        .word   30,VIDEO_80x30
-        .word   34,VIDEO_80x34
-        .word   60,VIDEO_80x60
-        .word   0
-
-.Lvga_opt:
-        .asciz  "vga"
-.Lvga_text80:
-        .asciz  "text-80x"
-.Lvga_gfx:
-        .asciz  "gfx-"
-.Lvga_mode:
-        .asciz  "mode-"
-.Lvga_current:
-        .asciz  "current"
-.Lno_rm_opt:
-        .asciz  "no-real-mode"
-.Ltboot_opt:
-        .asciz  "tboot"
-.Ledid_opt:
-        .asciz  "edid"
-.Ledid_force:
-        .asciz  "force"
-.Ledid_no:
-        .asciz  "no"
-.Ledd_opt:
-        .asciz  "edd"
-
-        .popsection
diff --git a/xen/arch/x86/boot/cmdline.c b/xen/arch/x86/boot/cmdline.c
new file mode 100644
index 0000000..291651b
--- /dev/null
+++ b/xen/arch/x86/boot/cmdline.c
@@ -0,0 +1,339 @@
+/*
+ * 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 <http://www.gnu.org/licenses/>.
+ *
+ * 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 "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;
+    u16 boot_vid_mode;
+    u16 vesa_width;
+    u16 vesa_height;
+    u16 vesa_depth;
+} 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 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 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);
+}
+
+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;
+    }
+}
+
+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);
+    vga_parse(cmdline, ebo);
+}
diff --git a/xen/arch/x86/boot/defs.h b/xen/arch/x86/boot/defs.h
new file mode 100644
index 0000000..1b347c5
--- /dev/null
+++ b/xen/arch/x86/boot/defs.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 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 <http://www.gnu.org/licenses/>.
+ *
+ * max() was copied from xen/xen/include/xen/kernel.h.
+ */
+
+#ifndef __BOOT_DEFS_H__
+#define __BOOT_DEFS_H__
+
+#include "../../../include/xen/stdbool.h"
+
+#define __packed	__attribute__((__packed__))
+#define __stdcall	__attribute__((__stdcall__))
+
+#define NULL		((void *)0)
+
+#define ALIGN_UP(arg, align) \
+                (((arg) + (align) - 1) & ~((typeof(arg))(align) - 1))
+
+#define max(x,y) ({ \
+        const typeof(x) _x = (x);       \
+        const typeof(y) _y = (y);       \
+        (void) (&_x == &_y);            \
+        _x > _y ? _x : _y; })
+
+#define _p(val)		((void *)(unsigned long)(val))
+
+#define tolower(c)	((c) | 0x20)
+
+typedef unsigned char u8;
+typedef unsigned short u16;
+typedef unsigned int u32;
+typedef unsigned long long u64;
+typedef unsigned int size_t;
+
+#define U16_MAX		((u16)(~0U))
+#define UINT_MAX	(~0U)
+
+#endif /* __BOOT_DEFS_H__ */
diff --git a/xen/arch/x86/boot/edd.S b/xen/arch/x86/boot/edd.S
index 5c80da6..73371f9 100644
--- a/xen/arch/x86/boot/edd.S
+++ b/xen/arch/x86/boot/edd.S
@@ -142,9 +142,6 @@ edd_next:
 edd_done:
         ret
 
-opt_edd:
-        .byte   0                               # edd=on/off/skipmbr
-
 GLOBAL(boot_edd_info_nr)
         .byte   0
 GLOBAL(boot_mbr_signature_nr)
diff --git a/xen/arch/x86/boot/head.S b/xen/arch/x86/boot/head.S
index 00fa47d..12bbd8e 100644
--- a/xen/arch/x86/boot/head.S
+++ b/xen/arch/x86/boot/head.S
@@ -506,6 +506,13 @@ trampoline_setup:
         cmpb    $0,sym_phys(skip_realmode)
         jnz     1f
 
+        /* Bail if there is no command line to parse. */
+        mov     sym_phys(multiboot_ptr),%ebx
+        testl   $MBI_CMDLINE,MB_flags(%ebx)
+        jz      1f
+
+        pushl   $sym_phys(early_boot_opts)
+        pushl   MB_cmdline(%ebx)
         call    cmdline_parse_early
 
 1:
@@ -524,6 +531,7 @@ trampoline_setup:
         /* Jump into the relocated trampoline. */
         lret
 
+cmdline_parse_early:
 #include "cmdline.S"
 
 reloc:
diff --git a/xen/arch/x86/boot/reloc.c b/xen/arch/x86/boot/reloc.c
index e421b07..cb8464d 100644
--- a/xen/arch/x86/boot/reloc.c
+++ b/xen/arch/x86/boot/reloc.c
@@ -25,21 +25,10 @@ asm (
     "    jmp  reloc                    \n"
     );
 
-typedef unsigned int u32;
-typedef unsigned long long u64;
-
+#include "defs.h"
 #include "../../../include/xen/multiboot.h"
 #include "../../../include/xen/multiboot2.h"
 
-#define NULL		((void *)0)
-
-#define __stdcall	__attribute__((__stdcall__))
-
-#define ALIGN_UP(arg, align) \
-                (((arg) + (align) - 1) & ~((typeof(arg))(align) - 1))
-
-#define _p(val)		((void *)(unsigned long)(val))
-
 #define get_mb2_data(tag, type, member)   (((multiboot2_tag_##type##_t *)(tag))->member)
 #define get_mb2_string(tag, type, member) ((u32)get_mb2_data(tag, type, member))
 
diff --git a/xen/arch/x86/boot/trampoline.S b/xen/arch/x86/boot/trampoline.S
index b013614..9f04b2c 100644
--- a/xen/arch/x86/boot/trampoline.S
+++ b/xen/arch/x86/boot/trampoline.S
@@ -220,8 +220,22 @@ trampoline_boot_cpu_entry:
         /* Jump to the common bootstrap entry point. */
         jmp     trampoline_protmode_entry
 
+#include "video.h"
+
+        .align  2
+        .byte   0
+/* Keep in sync with cmdline.c:early_boot_opts_t type! */
+early_boot_opts:
 skip_realmode:
         .byte   0
+opt_edd:
+        .byte   0                               /* edd=on/off/skipmbr */
+opt_edid:
+        .byte   0                               /* EDID parsing option (force/no/default). */
+GLOBAL(boot_vid_mode)
+        .word   VIDEO_80x25                     /* If we don't run at all, assume basic video mode 3 at 80x25. */
+vesa_size:
+        .word   0,0,0                           /* width x depth x height */
 
 GLOBAL(kbd_shift_flags)
         .byte   0
diff --git a/xen/arch/x86/boot/video.S b/xen/arch/x86/boot/video.S
index 2aafbeb..335a51c 100644
--- a/xen/arch/x86/boot/video.S
+++ b/xen/arch/x86/boot/video.S
@@ -945,7 +945,6 @@ store_edid:
 #endif
         ret
 
-opt_edid:       .byte   0       # EDID parsing option (force/no/default)
 mt_end:         .word   0       # End of video mode table if built
 edit_buf:       .space  6       # Line editor buffer
 card_name:      .word   0       # Pointer to adapter name
@@ -991,12 +990,6 @@ name_bann:      .asciz  "Video adapter: "
 
 force_size:     .word   0       # Use this size instead of the one in BIOS vars
 
-vesa_size:      .word   0,0,0   # width x depth x height
-
-/* If we don't run at all, assume basic video mode 3 at 80x25. */
-        .align  2
-GLOBAL(boot_vid_mode)
-        .word   VIDEO_80x25
 GLOBAL(boot_vid_info)
         .byte   0, 0    /* orig_x, orig_y */
         .byte   3       /* text mode 3    */
