[PATCH v2] x86/boot: This program cannot be run in DOS mode.$

From: Pali Rohár
Date: Mon Apr 01 2024 - 05:48:08 EST


There is a well known requirement which everybody knows: Every PE binary
executed in DOS must print legendary and famous message to the computer
screen:

This program cannot be run in DOS mode.

Linux kernel binary bzImage with enabled config option CONFIG_EFI_STUB is
bootable EFI binary. Every bootable EFI binary is in PE format. So bzImage
is also PE binary, but currently it does not meet that error message
requirement.

So fix this issue by properly filling all fields in the MZ DOS header part
of PE binary. And add a simple code which prints the required message on
the computer screen to fulfil the requirement.

In this case the required message "This program cannot be run in DOS mode."
is not fully truth as for example LOADLIN.EXE application can run Linux
kernel from DOS mode. So extend error message and prints additional line:

Use DOS loader LOADLIN.EXE.

For sure it helps users to know what they should do when they unexpectedly
try to run Linux kernel from DOS mode.

Note that DOS strings are not null-terminated but rather dollar-terminated.
Therefore it is required to pay for printing this error message in $$$.

Signed-off-by: Pali Rohár <pali@xxxxxxxxxx>

---
Changes in v2:

Due to big popularity of the first version, now after years I'm sending a
second version of this patch. Second version fixes booting issue by not
moving content of PE header (it is not needed now as the PE code after
years evolved) and patch is rebased on the top of the master branch.
---
arch/x86/boot/header.S | 60 ++++++++++++++++++++++++++++++++++++------
1 file changed, 52 insertions(+), 8 deletions(-)

diff --git a/arch/x86/boot/header.S b/arch/x86/boot/header.S
index b5c79f43359b..b0a8d89b510c 100644
--- a/arch/x86/boot/header.S
+++ b/arch/x86/boot/header.S
@@ -42,14 +42,58 @@ SYSSEG = 0x1000 /* historical load address >> 4 */
.code16
.section ".bstext", "ax"
#ifdef CONFIG_EFI_STUB
- # "MZ", MS-DOS header
- .word MZ_MAGIC
- .org 0x38
- #
- # Offset to the PE header.
- #
- .long LINUX_PE_MAGIC
- .long pe_header
+dos_header:
+ # "MZ", DOS header
+ .word MZ_MAGIC # e_magic - Magic number
+ .word (pe_header-dos_header)%512 # e_cblp - Bytes on last page of file
+ .word (pe_header-dos_header+511)/512 # e_cp - Pages in file
+ .word 0 # e_crlc - Relocations
+ .word (dos_start-dos_header+15)/16 # e_cparhdr - Size of header in paragraphs
+ .word 0 # e_minalloc - Minimum extra paragraphs needed
+ .word 0xffff # e_maxalloc - Maximum extra paragraphs needed
+ .word 0 # e_ss - Initial (relative) SS value
+ .word 0xb8 # e_sp - Initial SP value
+ .word 0 # e_csum - Checksum
+ .word 0 # e_ip - Initial IP value
+ .word 0 # e_cs - Initial (relative) CS value
+ .word dos_start-dos_header # e_lfarlc - File address of relocation table
+ .word 0 # e_ovno - Overlay number
+ .word 1 # e_ver - Linker version, 1 for DOS compatibility
+ .word 0 # e_res[1] - reserved
+ .word 0 # e_flags - Old exe header flags, behavior bits
+ .word 0 # e_res[3] - reserved
+ .word 0 # e_oemid - OEM identifier
+ .word 0 # e_oeminfo - OEM information
+ .word 0 # e_res2[0] - reserved
+ .word 0 # e_res2[1] - reserved
+ .word 0 # e_res2[2] - reserved
+ .word 0 # e_res2[3] - reserved
+ .word 0 # e_res2[4] - reserved
+ .word 0 # e_res2[5] - reserved
+ .word 0 # e_res2[6] - reserved
+ .word 0 # e_res2[7] - reserved
+ .long LINUX_PE_MAGIC # e_res2[8], e_res2[9] - unknown
+ .long pe_header-dos_header # e_lfanew - File address of new exe header
+
+ # DOS start code (must be paragraph aligned)
+ .balign 16, 0
+dos_start:
+ # Print dos_msg string (ds:dx)
+ push %cs
+ pop %ds
+ mov $(dos_msg-dos_start), %dx
+ mov $0x09, %ah
+ int $0x21
+
+ # Terminate program
+ mov $0x4c01, %ax
+ int $0x21
+
+dos_msg:
+ .ascii "This program cannot be run in DOS mode.\r\n"
+ .ascii "Use DOS loader LOADLIN.EXE.\r\r\n"
+ .ascii "$"
+
pe_header:
.long PE_MAGIC

--
2.20.1