From patchwork Mon Dec 5 22:25:13 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Kiper X-Patchwork-Id: 9461683 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 31FC36022E for ; Mon, 5 Dec 2016 22:28:52 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 2440E28285 for ; Mon, 5 Dec 2016 22:28:52 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 18B64282E2; Mon, 5 Dec 2016 22:28:52 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-4.2 required=2.0 tests=BAYES_00, RCVD_IN_DNSWL_MED, UNPARSEABLE_RELAY autolearn=ham version=3.3.1 Received: from lists.xenproject.org (lists.xenproject.org [192.237.175.120]) (using TLSv1.2 with cipher AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id 42D9228285 for ; Mon, 5 Dec 2016 22:28:50 +0000 (UTC) Received: from localhost ([127.0.0.1] helo=lists.xenproject.org) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1cE1iG-0003Ij-FX; Mon, 05 Dec 2016 22:26:20 +0000 Received: from mail6.bemta6.messagelabs.com ([193.109.254.103]) by lists.xenproject.org with esmtp (Exim 4.84_2) (envelope-from ) id 1cE1iE-0003HK-Vq for xen-devel@lists.xenproject.org; Mon, 05 Dec 2016 22:26:19 +0000 Received: from [193.109.254.147] by server-8.bemta-6.messagelabs.com id D1/43-30093-A89E5485; Mon, 05 Dec 2016 22:26:18 +0000 X-Brightmail-Tracker: H4sIAAAAAAAAA+NgFprEIsWRWlGSWpSXmKPExsUyZ7p8oG7nS9c Ig2/zxSy+b5nM5MDocfjDFZYAxijWzLyk/IoE1oydV34yF8x6wVix4ugElgbGV/MZuxi5OIQE 2pkkrj+cyAzhfGWUmPd7EVRmA6PEoc5ZUE4/o8TO2zeYuhg5OdgEdCQufnnIDmKLCChJ3Fs1m QmkiFlgJpPE6TsnWUASwgK+ElfOLGYDsVkEVCUeT9nDDGLzCrhL7LjZB1YjIaAo0f1sAlgNp4 CHxIqDbWBDhYBqVt+7DlVjLNE3q49lAiPfAkaGVYwaxalFZalFuoYmeklFmekZJbmJmTm6hgZ mermpxcWJ6ak5iUnFesn5uZsYgSHDAAQ7GK9vDDjEKMnBpCTKu/CBa4QQX1J+SmVGYnFGfFFp TmrxIUYZDg4lCV6nF0A5waLU9NSKtMwcYPDCpCU4eJREeI+CpHmLCxJzizPTIVKnGI05pj1b/ JSJ48PxJU+ZhFjy8vNSpcR5vUBKBUBKM0rz4AbBouoSo6yUMC8j0GlCPAWpRbmZJajyrxjFOR iVhHkngkzhycwrgdv3CugUJqBTThx3BjmlJBEhJdXAuETjoK38FaUja1/lbBX8bxaU8SRWc1K u1dvfcsoWrUfmK/9mtb89JZZr9enoN6d33FRcs792FYe7RMix3xzPWZp/rA5mNY56PClM1uZb IbtkMG8Tm97q7+L6nfu3V0pa9qgc+5bCzbHc4f6XJyv3BKpdNzqW7njpCsvLf7/8jnUppBsWb eU7rcRSnJFoqMVcVJwIAJsLXyqlAgAA X-Env-Sender: daniel.kiper@oracle.com X-Msg-Ref: server-15.tower-27.messagelabs.com!1480976775!22939325!1 X-Originating-IP: [156.151.31.81] X-SpamReason: No, hits=0.0 required=7.0 tests=sa_preprocessor: VHJ1c3RlZCBJUDogMTU2LjE1MS4zMS44MSA9PiAyODgzMzk=\n X-StarScan-Received: X-StarScan-Version: 9.0.16; banners=-,-,- X-VirusChecked: Checked Received: (qmail 39595 invoked from network); 5 Dec 2016 22:26:16 -0000 Received: from userp1040.oracle.com (HELO userp1040.oracle.com) (156.151.31.81) by server-15.tower-27.messagelabs.com with DHE-RSA-AES256-GCM-SHA384 encrypted SMTP; 5 Dec 2016 22:26:16 -0000 Received: from aserv0021.oracle.com (aserv0021.oracle.com [141.146.126.233]) by userp1040.oracle.com (Sentrion-MTA-4.3.2/Sentrion-MTA-4.3.2) with ESMTP id uB5MQ3RC025278 (version=TLSv1 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Mon, 5 Dec 2016 22:26:03 GMT Received: from aserv0121.oracle.com (aserv0121.oracle.com [141.146.126.235]) by aserv0021.oracle.com (8.13.8/8.14.4) with ESMTP id uB5MQ3Ma009311 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK); Mon, 5 Dec 2016 22:26:03 GMT Received: from abhmp0004.oracle.com (abhmp0004.oracle.com [141.146.116.10]) by aserv0121.oracle.com (8.13.8/8.13.8) with ESMTP id uB5MQ1nB015149; Mon, 5 Dec 2016 22:26:01 GMT Received: from olila.local.net-space.pl (/10.175.220.3) by default (Oracle Beehive Gateway v4.0) with ESMTP ; Mon, 05 Dec 2016 14:26:00 -0800 From: Daniel Kiper To: xen-devel@lists.xenproject.org Date: Mon, 5 Dec 2016 23:25:13 +0100 Message-Id: <1480976718-12198-9-git-send-email-daniel.kiper@oracle.com> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1480976718-12198-1-git-send-email-daniel.kiper@oracle.com> References: <1480976718-12198-1-git-send-email-daniel.kiper@oracle.com> X-Source-IP: aserv0021.oracle.com [141.146.126.233] Cc: jgross@suse.com, sstabellini@kernel.org, andrew.cooper3@citrix.com, cardoe@cardoe.com, pgnet.dev@gmail.com, ning.sun@intel.com, julien.grall@arm.com, jbeulich@suse.com, qiaowei.ren@intel.com, gang.wei@intel.com, fu.wei@linaro.org Subject: [Xen-devel] [PATCH v11 08/13] x86/boot: implement early command line parser in C X-BeenThere: xen-devel@lists.xen.org X-Mailman-Version: 2.1.18 Precedence: list List-Id: Xen developer discussion List-Unsubscribe: , List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: xen-devel-bounces@lists.xen.org Sender: "Xen-devel" X-Virus-Scanned: ClamAV using ClamSMTP Current early command line parser implementation in assembler is very difficult to change to relocatable stuff using segment registers. This requires a lot of changes in very weird and fragile code. So, reimplement this functionality in C. This way code will be relocatable out of the box (without playing with segment registers) and much easier to maintain. Additionally, put all common cmdline.c and reloc.c definitions into defs.h header. This way we do not duplicate needlessly some stuff. And finally remove unused xen/include/asm-x86/config.h header from reloc.c dependencies. Suggested-by: Andrew Cooper Signed-off-by: Daniel Kiper Acked-by: Jan Beulich --- v7 - suggestions/fixes: - add min() macro (suggested by Jan Beulich), - add padding to early_boot_opts_t in more standard way (suggested by Jan Beulich), - simplify defs.h dependencies (suggested by Jan Beulich). v6 - suggestions/fixes: - put common cmdline.c and reloc.c definitions into defs.h header (suggested by Jan Beulich), - use xen/include/xen/stdbool.h and bool type from it instead of own defined bool_t (suggested by Jan Beulich), - define delim_chars as constant (suggested by Jan Beulich), - properly align trampoline.S:early_boot_opts struct (suggested by Jan Beulich), - fix overflow check in strtoui() (suggested by Jan Beulich), - remove unused xen/include/asm-x86/config.h header from reloc.c dependencies, - improve commit message. v4 - suggestions/fixes: - move to stdcall calling convention (suggested by Jan Beulich), - define bool_t and use it properly (suggested by Jan Beulich), - put list of delimiter chars into static const char[] (suggested by Jan Beulich), - use strlen() instead of strlen_opt() (suggested by Jan Beulich), - change strtoi() to strtoui() and optimize it a bit (suggested by Jan Beulich), - define strchr() and use it in strtoui() (suggested by Jan Beulich), - optimize vga_parse() (suggested by Jan Beulich), - move !cmdline check from assembly to C (suggested by Jan Beulich), - remove my name from copyright (Oracle requirement) (suggested by Konrad Rzeszutek Wilk). v3 - suggestions/fixes: - optimize some code (suggested by Jan Beulich), - put VESA data into early_boot_opts_t members (suggested by Jan Beulich), - rename some functions and variables (suggested by Jan Beulich), - move around video.h include in xen/arch/x86/boot/trampoline.S (suggested by Jan Beulich), - fix coding style (suggested by Jan Beulich), - fix build with older GCC (suggested by Konrad Rzeszutek Wilk), - remove redundant comments (suggested by Jan Beulich), - add some comments - improve commit message (suggested by Jan Beulich). --- .gitignore | 5 +- xen/arch/x86/Makefile | 2 +- xen/arch/x86/boot/Makefile | 11 +- xen/arch/x86/boot/build32.mk | 2 + xen/arch/x86/boot/cmdline.S | 367 ---------------------------------------- xen/arch/x86/boot/cmdline.c | 340 +++++++++++++++++++++++++++++++++++++ xen/arch/x86/boot/defs.h | 58 +++++++ xen/arch/x86/boot/edd.S | 3 - xen/arch/x86/boot/head.S | 8 + xen/arch/x86/boot/reloc.c | 13 +- xen/arch/x86/boot/trampoline.S | 15 ++ xen/arch/x86/boot/video.S | 7 - 12 files changed, 437 insertions(+), 394 deletions(-) delete mode 100644 xen/arch/x86/boot/cmdline.S create mode 100644 xen/arch/x86/boot/cmdline.c create mode 100644 xen/arch/x86/boot/defs.h diff --git a/.gitignore b/.gitignore index a2f34a1..d2967d8 100644 --- a/.gitignore +++ b/.gitignore @@ -250,9 +250,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 2a0781a..e74fe62 100644 --- a/xen/arch/x86/Makefile +++ b/xen/arch/x86/Makefile @@ -220,5 +220,5 @@ 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 diff --git a/xen/arch/x86/boot/Makefile b/xen/arch/x86/boot/Makefile index 06893d8..c6246c8 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 = defs.h $(BASEDIR)/include/xen/stdbool.h + +CMDLINE_DEPS = $(DEFS_H_DEPS) video.h + +RELOC_DEPS = $(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. */ - 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'. */ - 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-xx'. */ - push %ebx - pushl $sym_phys(.Lvga_gfx) - call .Lstr_prefix - add $8,%esp - test %eax,%eax - jnz .Lparse_vga_mode - - /* We have 'vga=gfx-xx'. */ - /* skip 'gfx-' */ - add $4,%ebx - /* parse */ - push %ebx - call .Latoi - pop %esi - mov %ax,sym_phys(vesa_size)+0 - /* skip 'x' */ - lodsb - cmpb $'x',%al - jne .Lcmdline_exit - /* parse */ - push %esi - call .Latoi - pop %esi - mov %ax,sym_phys(vesa_size)+2 - /* skip 'x' */ - lodsb - cmpb $'x',%al - jne .Lcmdline_exit - /* parse */ - 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-'. */ - push %ebx - pushl $sym_phys(.Lvga_mode) - call .Lstr_prefix - add $8,%esp - test %eax,%eax - jnz .Lparse_vga_current - - /* We have 'vga=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..06aa064 --- /dev/null +++ b/xen/arch/x86/boot/cmdline.c @@ -0,0 +1,340 @@ +/* + * 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 "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; + 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..6abdc15 --- /dev/null +++ b/xen/arch/x86/boot/defs.h @@ -0,0 +1,58 @@ +/* + * 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 . + * + * 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 min(x,y) ({ \ + const typeof(x) _x = (x); \ + const typeof(y) _y = (y); \ + (void) (&_x == &_y); \ + _x < _y ? _x : _y; }) + +#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 ac93df0..6c97e1f 100644 --- a/xen/arch/x86/boot/head.S +++ b/xen/arch/x86/boot/head.S @@ -514,6 +514,13 @@ trampoline_setup: cmpb $0,sym_phys(efi_platform) 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: @@ -532,6 +539,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 b22bf1e..0f2e372 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..2715d17 100644 --- a/xen/arch/x86/boot/trampoline.S +++ b/xen/arch/x86/boot/trampoline.S @@ -220,8 +220,23 @@ trampoline_boot_cpu_entry: /* Jump to the common bootstrap entry point. */ jmp trampoline_protmode_entry +#include "video.h" + + .align 2 +/* 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). */ +/* Padding. */ + .byte 0 +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 */