mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2026-08-02 09:29:23 -05:00
Some modern UEFI refuse to load PE image when a section have both IMAGE_SCN_MEM_WRITE and IMAGE_SCN_MEM_EXECUTE flag set. The .text section was using both because relocation need to write in data area. Split the .text PE section in two: .text with read-execute only for code and rodata, and .data with read-write for dynamic, data and got. Also set IMAGE_DLLCHARACTERISTICS_NX_COMPAT flag accordingly (we only need it for startup and not when Memtest86+ is running later). For x86 and LoongArch headers: - NumberOfSections go from 3 to 4 - Remove IMAGE_SCN_MEM_WRITE from .text section - Add new .data section with RW flag, no execute - Fix SizeOfInitializedData to include .data and .sbat - Set IMAGE_DLLCHARACTERISTICS_NX_COMPAT flag - BaseOfData now point to .data instead of .sbat (PE32) - .dynamic moved after relocation tables (memtest_shared.lds) - page-aligned _etext_ro symbol used as read-only/writable boundary (memtest_shared.lds)
967 lines
22 KiB
ArmAsm
967 lines
22 KiB
ArmAsm
// SPDX-License-Identifier: GPL-2.0
|
|
//
|
|
// header.S supports booting directly from the 1) BIOS (FDD), 2) UEFI or 3) via
|
|
// an intermediate bootloader that supports the Linux boot protocol. All-in-one.
|
|
//
|
|
// 1) If booted directly from the BIOS, ".bootsect" gets loaded at address 0x7C00
|
|
// with DL register set to the boot drive number: 0x00 - FDD, 0x80 - first HDD.
|
|
// Unfortunately, when executing "MZ" signature as x86 instructions, the original
|
|
// value of DX becomes instantly and irrevocably lost, but this is fine as we only
|
|
// support booting from FDD anyway. Once started, the code immediately prints
|
|
// boot_msg - "Loading ". Next, it sets DX to 0x00 (as assumed by the code) and
|
|
// creates a new Floppy Disk Drive parameter table. Then, it tries to load
|
|
// ".setup" section immediately after itself (address 0x7E00) that contains
|
|
// setup code, Once successful, it prints memtest version and starts loading
|
|
// the main program code at address 0x10000. After each successful read,
|
|
// a dot - "." is printed. If read fails, error message gets printed and
|
|
// the read operation is restarted. At the end, 07E0h:0000h is called which
|
|
// immediately jumps into do_setup that handles switches CPU into protected mode
|
|
// and calls kernel entry point at 0x10000.
|
|
//
|
|
// 2) To support loading from the UEFI, it provides the MS-DOS & PE/COFF headers.
|
|
// The PE/COFF headers point to offset 0x10000 + 0x1E0 where efi_boot from
|
|
// startup32.S / startup64.S that handles EFI handover and the rest of booting.
|
|
//
|
|
// 3) When using an intermediate bootloader, it provides the first few bytes of
|
|
// the Linux boot header (at the end of the ".bootsect" section), with the
|
|
// remainder of the header and the setup code being provided in the ".setup".
|
|
// section.
|
|
//
|
|
// Copyright (C) 2020-2024 Martin Whitaker.
|
|
//
|
|
// Derived from Linux 5.6 arch/x86/boot/header.S:
|
|
//
|
|
// Copyright (C) 1991, 1992 Linus Torvalds
|
|
//
|
|
// Based on bootsect.S and setup.S
|
|
// modified by more people than can be counted
|
|
//
|
|
// Rewritten as a common file by H. Peter Anvin (Apr 2007)
|
|
|
|
#define __ASSEMBLY__
|
|
|
|
#include "boot.h"
|
|
#include "build_version.h"
|
|
#include "peimage.h"
|
|
|
|
# The EFI loader loads the header at ImageBase, so we have to locate the main program
|
|
# after that. This means we can't load the main program at HIGH_LOAD_ADDR. Pick a load
|
|
# address well away from HIGH_LOAD_ADDR, to avoid overlap when relocating the code.
|
|
|
|
#define IMAGE_BASE 0x200000
|
|
#define BOOT_PARAMS_START (SETUP_SECS * 512)
|
|
#define BOOT_PARAMS_END (BOOT_PARAMS_START + 4096)
|
|
|
|
.section ".bootsect", "ax", @progbits
|
|
.code16
|
|
|
|
.globl boot
|
|
boot:
|
|
# "MZ", the MS-DOS header signature.
|
|
# Fortunately it translates to mostly harmless x86 instructions.
|
|
.byte 0x4d # dec %bp
|
|
.byte 0x5a # pop %dx
|
|
|
|
ljmp $BOOT_SEG, $init
|
|
|
|
init:
|
|
movw %cs, %ax
|
|
movw %ax, %ds
|
|
movw %ax, %es
|
|
movw %ax, %ss
|
|
movw $BOOT_STACK_TOP, %bp
|
|
movw %bp, %sp
|
|
sti
|
|
cld
|
|
|
|
leaw boot_msg, %bp
|
|
movw $(boot_msg_end - boot_msg), %cx
|
|
call print_string
|
|
|
|
jmp init_fdd
|
|
|
|
print_string:
|
|
# Print string from %es:%bp of length %cx via BIOS routine int 10h: 1301h
|
|
push %cx
|
|
movb $0x03, %ah # read cursor pos
|
|
xorb %bh, %bh # page 0
|
|
int $0x10
|
|
|
|
pop %cx
|
|
movb $0x07, %bl # (page 0 set above) attribute 7 (normal)
|
|
movw $0x1301, %ax # write string, move cursor
|
|
int $0x10
|
|
ret
|
|
|
|
boot_msg:
|
|
.ascii "Loading "
|
|
boot_msg_end:
|
|
|
|
.org 0x3c
|
|
# The PE header pointer.
|
|
.long pe_header
|
|
|
|
init_fdd:
|
|
# Many BIOS's default disk parameter tables will not recognize
|
|
# multi-sector reads beyond the maximum sector number specified
|
|
# in the default diskette parameter tables - this may mean 7
|
|
# sectors in some cases.
|
|
#
|
|
# Since single sector reads are slow and out of the question,
|
|
# we must take care of this by creating new parameter tables
|
|
# (for the first disk) in RAM. We will set the maximum sector
|
|
# count to 18 - the most we will encounter on an HD 1.44.
|
|
#
|
|
# High doesn't hurt. Low does.
|
|
#
|
|
# Segments are as follows:
|
|
# ds=es=ss=cs = BOOT_SEG,
|
|
# fs = 0, gs = parameter table segment
|
|
|
|
xorw %dx, %dx
|
|
|
|
pushw $0
|
|
popw %fs
|
|
movw $0x78, %bx # fs:bx is parameter table address
|
|
lgs %fs:(%bx),%si # gs:si is source
|
|
|
|
movw %dx, %di # es:di is destination
|
|
movw $6, %cx # copy 12 bytes
|
|
cld
|
|
|
|
rep movsw %gs:(%si), (%di)
|
|
|
|
movw %dx, %di
|
|
movb $18, 4(%di) # patch sector count
|
|
|
|
movw %di, %fs:(%bx)
|
|
movw %es, %fs:2(%bx)
|
|
|
|
movw %cs, %ax
|
|
movw %ax, %fs
|
|
movw %ax, %gs
|
|
|
|
xorb %ah, %ah # reset FDC
|
|
xorb %dl, %dl
|
|
int $0x13
|
|
|
|
# Load the setup sectors directly after the boot block.
|
|
# Note that 'es' is already set up.
|
|
|
|
load_setup:
|
|
xorw %dx, %dx # drive 0, head 0
|
|
movw $0x0002, %cx # sector 2, track 0
|
|
movw $0x0200, %bx # address = 512, in BOOT_SEG
|
|
movw $(0x0200 + SETUP_SECS), %ax # service 2, nr of sectors
|
|
# (assume all on head 0, track 0)
|
|
int $0x13 # read it
|
|
jnc load_setup_done # ok - continue
|
|
|
|
pushw %ax # dump error code
|
|
call print_nl
|
|
movw %sp, %bp
|
|
call print_hex
|
|
popw %ax
|
|
|
|
xorb %dl, %dl # reset FDC
|
|
xorb %ah, %ah
|
|
int $0x13
|
|
jmp load_setup
|
|
|
|
load_setup_done:
|
|
|
|
# Get disk drive parameters, specifically number of sectors/track.
|
|
# It seems that there is no BIOS call to get the number of sectors.
|
|
# Guess 18 sectors if sector 18 can be read, 15 if sector 15 can be
|
|
# read. Otherwise guess 9.
|
|
|
|
xorw %dx, %dx # drive 0, head 0
|
|
movw $0x0012, %cx # sector 18, track 0
|
|
movw $BOOT_STACK, %bx # use the bottom of the stack (es = cs)
|
|
movw $0x0201, %ax # service 2, 1 sector
|
|
int $0x13
|
|
jnc got_sectors
|
|
movb $0x0f, %cl # sector 15
|
|
movw $0x0201, %ax # service 2, 1 sector
|
|
int $0x13
|
|
jnc got_sectors
|
|
movb $0x09, %cl
|
|
|
|
got_sectors:
|
|
movw %cx, %cs:sectors
|
|
movw $BOOT_SEG, %ax
|
|
movw %ax, %es
|
|
|
|
leaw mt86plus_version, %bp
|
|
movw $(mt86plus_version_end - mt86plus_version), %cx
|
|
call print_string
|
|
|
|
# Load the main test program.
|
|
|
|
movw $MAIN_SEG, %ax
|
|
movw %ax, %es
|
|
call read_it
|
|
call kill_motor
|
|
call turn_off_cursor
|
|
call print_nl
|
|
|
|
# Fix up the Linux boot header to indicate we've loaded into low memory.
|
|
|
|
movl $LOW_LOAD_ADDR, code32_start
|
|
|
|
# After that (everything loaded), we jump to the setup code loaded
|
|
# directly after the boot block.
|
|
|
|
ljmp $SETUP_SEG, $0
|
|
|
|
# This subroutine loads the system at address 0x10000, making sure no 64KB
|
|
# boundaries are crossed. We try to load it as fast as possible, loading
|
|
# whole tracks whenever we can.
|
|
#
|
|
# in: es - starting address segment (normally 0x1000)
|
|
#
|
|
sread: .word 1 + SETUP_SECS # sectors read of current track
|
|
head: .word 0 # current head
|
|
track: .word 0 # current track
|
|
|
|
read_it:
|
|
movw %es, %ax
|
|
testw $0x0fff, %ax
|
|
die:
|
|
jne die # es must be at 64kB boundary
|
|
xorw %bx,%bx # bx is starting address within segment
|
|
rp_read:
|
|
movw %es, %ax
|
|
subw $MAIN_SEG, %ax # have we loaded all yet?
|
|
cmpw sys_size, %ax
|
|
jbe ok1_read
|
|
ret
|
|
ok1_read:
|
|
movw %cs:sectors, %ax
|
|
subw sread, %ax
|
|
movw %ax, %cx
|
|
shlw $9, %cx
|
|
addw %bx, %cx
|
|
jnc ok2_read
|
|
je ok2_read
|
|
xorw %ax, %ax
|
|
subw %bx, %ax
|
|
shrw $9, %ax
|
|
ok2_read:
|
|
call read_track
|
|
movw %ax, %cx
|
|
add sread, %ax
|
|
cmpw %cs:sectors, %ax
|
|
jne ok3_read
|
|
movw $1, %ax
|
|
subw head, %ax
|
|
jne ok4_read
|
|
incw track
|
|
ok4_read:
|
|
movw %ax, head
|
|
xorw %ax, %ax
|
|
ok3_read:
|
|
movw %ax, sread
|
|
shlw $9, %cx
|
|
addw %cx, %bx
|
|
jnc rp_read
|
|
movw %es, %ax
|
|
addb $0x10, %ah
|
|
movw %ax, %es
|
|
xorw %bx, %bx
|
|
jmp rp_read
|
|
|
|
read_track:
|
|
pusha
|
|
pusha
|
|
movw $0xe2e, %ax # loading... message 2e = .
|
|
movw $7, %bx
|
|
int $0x10
|
|
popa
|
|
|
|
movw track, %dx
|
|
movw sread, %cx
|
|
incw %cx
|
|
movb %dl, %ch
|
|
movw head, %dx
|
|
movb %dl, %dh
|
|
andw $0x0100, %dx
|
|
movb $2, %ah
|
|
|
|
pushw %dx # save for error dump
|
|
pushw %cx
|
|
pushw %bx
|
|
pushw %ax
|
|
|
|
int $0x13
|
|
jc bad_rt
|
|
addw $8, %sp
|
|
popa
|
|
ret
|
|
|
|
bad_rt:
|
|
pushw %ax # save error code
|
|
call print_all # ah = error, al = read
|
|
|
|
xorb %ah, %ah
|
|
xorb %dl, %dl
|
|
int $0x13
|
|
|
|
addw $10, %sp
|
|
popa
|
|
jmp read_track
|
|
|
|
# This subroutine is for debugging purposes. It will print out all of the
|
|
# registers. The assumption is that this is called from a routine, with a
|
|
# stack frame like:
|
|
# dx
|
|
# cx
|
|
# bx
|
|
# ax
|
|
# err
|
|
# ret <- sp
|
|
|
|
print_all:
|
|
movw $5, %cx # error code + 4 registers
|
|
movw %sp, %bp
|
|
|
|
print_loop:
|
|
pushw %cx # save count left
|
|
call print_nl # nl for readability
|
|
|
|
cmpb 5, %cl # see if register name is needed
|
|
jae no_reg
|
|
|
|
movw $(0xe05 + 'A' - 1), %ax
|
|
subb %cl, %al
|
|
int $0x10
|
|
movb $'X', %al
|
|
int $0x10
|
|
movb $':', %al
|
|
int $0x10
|
|
|
|
no_reg:
|
|
addw $2, %bp # next register
|
|
call print_hex # print it
|
|
popw %cx
|
|
loop print_loop
|
|
ret
|
|
|
|
print_nl:
|
|
movw $0xe0d, %ax # CR
|
|
int $0x10
|
|
movb $0x0a, %al # LF
|
|
int $0x10
|
|
ret
|
|
|
|
# This subroutine is for debugging purposes, and prints the word pointed to
|
|
# by ss:bp in hexadecimal.
|
|
|
|
print_hex:
|
|
movw $4, %cx # 4 hex digits
|
|
movw (%bp), %dx # load word into dx
|
|
|
|
print_digit:
|
|
rolw $4, %dx # rotate so that lowest 4 bits are used
|
|
movb $0xe, %ah
|
|
movb %dl, %al # mask off so we have only next nibble
|
|
andb $0xf, %al
|
|
addb $'0', %al # convert to 0-based digit
|
|
cmpb $'9', %al # check for overflow
|
|
jbe good_digit
|
|
addb $('A' - '0' - 10), %al
|
|
|
|
good_digit:
|
|
int $0x10
|
|
loop print_digit
|
|
ret
|
|
|
|
# This subroutine turns off the floppy drive motor, so that we enter the
|
|
# kernel in a known state, and don't have to worry about it later.
|
|
|
|
kill_motor:
|
|
pushw %dx
|
|
movw $0x3f2, %dx
|
|
xorb %al, %al
|
|
outb %al, %dx
|
|
popw %dx
|
|
ret
|
|
|
|
# This subroutine turns off the text display cursor.
|
|
|
|
turn_off_cursor:
|
|
movb $0x01, %ah
|
|
movb $0x00, %bh
|
|
movw $0x2000, %cx
|
|
int $0x10
|
|
ret
|
|
|
|
# Local variables.
|
|
|
|
sectors:
|
|
.word 0
|
|
|
|
.org 497
|
|
# Emulate the Linux boot header, to allow loading by intermediate boot loaders.
|
|
# Indicate that the main program code should be loaded in high memory.
|
|
# The code from ".bootsect" will fix up the values if we are booted directly
|
|
# from the BIOS.
|
|
|
|
setup_sects:
|
|
.byte SETUP_SECS
|
|
root_flags:
|
|
.word 0
|
|
sys_size:
|
|
.long _sys_size
|
|
ram_size:
|
|
.word 0
|
|
vid_mode:
|
|
.word 0
|
|
root_dev:
|
|
.word 0
|
|
boot_flag:
|
|
.word 0xAA55
|
|
|
|
.section ".setup", "ax", @progbits
|
|
.globl setup
|
|
setup:
|
|
jmp do_setup
|
|
|
|
.globl header
|
|
header:
|
|
.ascii "HdrS"
|
|
version:
|
|
.word 0x020c
|
|
realmode_swtch:
|
|
.long 0
|
|
start_sys_seg:
|
|
.word 0x1000
|
|
kernel_version:
|
|
.word mt86plus_version-512
|
|
type_of_loader:
|
|
.byte 0
|
|
loadflags:
|
|
.byte 0x1 # LOADED_HIGH
|
|
setup_move_size:
|
|
.word 0
|
|
.globl code32_start
|
|
code32_start:
|
|
.long HIGH_LOAD_ADDR
|
|
ramdisk_image:
|
|
.long 0
|
|
ramdisk_size:
|
|
.long 0
|
|
bootsect_kludge:
|
|
.long 0
|
|
heap_end_ptr:
|
|
.word 0
|
|
ext_loader_ver:
|
|
.byte 0
|
|
ext_loader_type:
|
|
.byte 0
|
|
cmd_line_ptr:
|
|
.long 0
|
|
initrd_addr_max:
|
|
.long 0xffffffff
|
|
kernel_alignment:
|
|
.long 4096
|
|
relocatable_kernel:
|
|
.byte 0
|
|
min_alignment:
|
|
.byte 12
|
|
xload_flags:
|
|
#ifdef __x86_64__
|
|
.word 0x9 # XLF_KERNEL_64,XLF_EFI_HANDOVER_64
|
|
#else
|
|
.word 0x4 # XLF_EFI_HANDOVER_32
|
|
#endif
|
|
cmd_line_size:
|
|
.long 255
|
|
hardware_subarch:
|
|
.long 0
|
|
hardware_subarch_data:
|
|
.quad 0
|
|
payload_offset:
|
|
.long 0
|
|
payload_length:
|
|
.long 0
|
|
setup_data:
|
|
.quad 0
|
|
pref_address:
|
|
.quad HIGH_LOAD_ADDR
|
|
init_size:
|
|
.long _init_size
|
|
handover_offset:
|
|
.long 0x10
|
|
|
|
// The code below collects the memory map information from the BIOS, disables
|
|
// APM, enables A20, and performs the switch from real mode to protected mode
|
|
// before jumping to the main program entry point.
|
|
//
|
|
// The memory map information is stored in the 4KB block of memory immediately
|
|
// following the setup code. The layout of the information matches the Linux
|
|
// boot_params struct. A pointer to this block is passed to the main program,
|
|
// for compatiblity with the Linux 32-bit boot protocol.
|
|
|
|
do_setup:
|
|
# Reload the segment registers, except for the stack.
|
|
|
|
movw %cs, %ax
|
|
movw %ax, %ds
|
|
movw %ax, %es
|
|
|
|
# Get the memory map and disable APM.
|
|
|
|
call get_mem_info
|
|
call disable_apm
|
|
|
|
# Disable interrupts.
|
|
|
|
cli
|
|
movb $0x80, %al # disable NMI
|
|
outb %al, $0x70
|
|
|
|
# Enable A20.
|
|
|
|
# Try to switch using the fast A20 gate.
|
|
movw $0x92, %dx
|
|
inb %dx, %al
|
|
# Skip if it's unimplemented (read returns 0xff).
|
|
cmpb $0xff, %al
|
|
jz 0f
|
|
orb $0x02, %al # set the ALT_A20_GATE bit
|
|
andb $0xfe, %al # clear the INIT_NOW bit
|
|
outb %al, %dx
|
|
0:
|
|
# Use the keyboard controller method anyway.
|
|
call empty_8042
|
|
movb $0xd1, %al # send write command
|
|
outb %al, $0x64
|
|
call empty_8042
|
|
movb $0xdf, %al # A20 on
|
|
outb %al, $0x60
|
|
call empty_8042
|
|
|
|
# Set up a minimal GDT and IDT.
|
|
|
|
xorl %eax, %eax
|
|
movw %cs, %ax
|
|
shll $4, %eax
|
|
addl %eax, gdt_descr - setup + 2
|
|
lgdt gdt_descr - setup
|
|
lidt idt_descr - setup
|
|
|
|
# Load a pointer to the boot_params block into ESI.
|
|
|
|
xorl %esi, %esi
|
|
movw %cs, %si
|
|
shll $4, %esi
|
|
addl $BOOT_PARAMS_START, %esi
|
|
|
|
# Fix up the jump address.
|
|
|
|
movl (code32_start - setup), %eax
|
|
movl %eax, (jump - setup + 2)
|
|
|
|
# Copy code32_start to the boot_params struct.
|
|
|
|
movl %eax, (BOOT_PARAMS_START + 0x214)
|
|
|
|
# Copy cmd_line_ptr and cmd_line_size to the boot_params struct.
|
|
|
|
movl (cmd_line_ptr - setup), %eax
|
|
movl %eax, (BOOT_PARAMS_START + 0x228)
|
|
movl (cmd_line_size - setup), %eax
|
|
movl %eax, (BOOT_PARAMS_START + 0x238)
|
|
|
|
# Switch to protected mode.
|
|
|
|
movl %cr0, %eax
|
|
orl $1, %eax
|
|
movl %eax, %cr0
|
|
jmp flush
|
|
flush:
|
|
# Reload the segment registers and jump to the main test program.
|
|
|
|
movw $KERNEL_DS, %ax
|
|
movw %ax, %ds
|
|
movw %ax, %es
|
|
movw %ax, %ss
|
|
movw %ax, %fs
|
|
movw %ax, %gs
|
|
jump:
|
|
data32 ljmp $KERNEL_CS, $0
|
|
|
|
|
|
# This subroutine queries the BIOS to determine the system memory map
|
|
# and stores the results in the boot_params structure that we pass to
|
|
# the startup code.
|
|
|
|
#define SMAP 0x534d4150
|
|
|
|
get_mem_info:
|
|
push %ds
|
|
push %es
|
|
|
|
# Set DS and ES to point to the start of the boot_params structure.
|
|
|
|
movw %ds, %ax
|
|
addw $(BOOT_PARAMS_START >> 4), %ax
|
|
movw %ax, %ds
|
|
movw %ax, %es
|
|
|
|
# Zero the entire boot_params structure.
|
|
|
|
movw $0x0000, %di
|
|
movw $0x0400, %cx
|
|
xorl %eax, %eax
|
|
cld
|
|
rep stosl
|
|
|
|
# First try method E820. E820 returns memory classified into a whole
|
|
# bunch of different types, and allows memory holes and everything.
|
|
|
|
mem_e820:
|
|
movw $E820_MAP, %di # destination pointer
|
|
xorl %ebx, %ebx # continuation counter
|
|
|
|
loop_e820:
|
|
movl $0x0000e820, %eax # e820, upper word zeroed
|
|
movl $SMAP, %edx # ASCII 'SMAP'
|
|
movl $20, %ecx # size of the e820 record
|
|
int $0x15 # make the call
|
|
jc done_e820 # bail out if it fails
|
|
|
|
cmpl $SMAP, %eax # check the return is 'SMAP'
|
|
jne done_e820 # bail out if it fails
|
|
|
|
incb (E820_ENTRIES)
|
|
addw $E820_ENTRY_SIZE, %di
|
|
|
|
movb (E820_ENTRIES), %al # check for table full
|
|
cmpb $E820_MAP_SIZE, %al
|
|
je done_e820
|
|
|
|
cmpl $0, %ebx # any more entries?
|
|
jne loop_e820
|
|
|
|
done_e820:
|
|
cmpb $0, (E820_ENTRIES)
|
|
jnz get_mem_done
|
|
|
|
# Next try method E801.
|
|
|
|
mem_e801:
|
|
stc # Fix to work around buggy BIOSs
|
|
xorw %cx,%cx # which don't clear/set carry on
|
|
xorw %dx,%dx # pass/error of e801h memory size
|
|
# call or merely pass cx,dx through
|
|
# without changing them.
|
|
movw $0xe801, %ax
|
|
int $0x15
|
|
jc mem_88
|
|
|
|
cmpw $0x0, %cx # Kludge to handle BIOSes which
|
|
jne 0f # report their extended memory in
|
|
cmpw $0x0, %dx # AX/BX rather than CX/DX. The spec
|
|
jne 0f # I have read seems to indicate that
|
|
movw %ax, %cx # AX/BX are more reasonable anyway.
|
|
movw %bx, %dx
|
|
0:
|
|
jmp fake_e820
|
|
|
|
# Finally try method 88.
|
|
|
|
mem_88:
|
|
movb $0x88, %ah
|
|
int $0x15
|
|
movw %ax, %cx
|
|
movw $0, %dx
|
|
|
|
fake_e820:
|
|
# Write entry for memory below 1MB.
|
|
movl $0x0, E820_ADDR(%di)
|
|
movl $0xa0000, E820_SIZE(%di)
|
|
movl $1, E820_TYPE(%di)
|
|
incb (E820_ENTRIES)
|
|
addw $E820_ENTRY_SIZE, %di
|
|
|
|
# Write entry for memory between 1MB and 16MB.
|
|
|
|
andl $0xffff, %ecx # convert to 32-bits
|
|
jz 0f
|
|
shll $10, %ecx # convert to bytes
|
|
movl $0x100000, E820_ADDR(%di)
|
|
movl %ecx, E820_SIZE(%di)
|
|
movl $1, E820_TYPE(%di)
|
|
incb (E820_ENTRIES)
|
|
addw $E820_ENTRY_SIZE, %di
|
|
0:
|
|
# Write entry for memory above 16MB.
|
|
|
|
andl $0xffff, %edx # convert to 32-bits
|
|
jz 1f
|
|
shll $16, %edx # convert to bytes
|
|
movl $0x1000000, E820_ADDR(%di)
|
|
movl %edx, E820_SIZE(%di)
|
|
movl $1, E820_TYPE(%di)
|
|
incb (E820_ENTRIES)
|
|
addw $E820_ENTRY_SIZE, %di
|
|
1:
|
|
|
|
get_mem_done:
|
|
pop %es
|
|
pop %ds
|
|
ret
|
|
|
|
# This subroutine disables APM if it is present.
|
|
|
|
disable_apm:
|
|
movw $0x5300, %ax # APM BIOS installation check
|
|
xorw %bx, %bx
|
|
int $0x15
|
|
jc disable_apm_done # error -> no APM BIOS
|
|
|
|
cmpw $0x504d, %bx # check for "PM" signature
|
|
jne disable_apm_done # no signature -> no APM BIOS
|
|
|
|
movw $0x5304, %ax # Disconnect first just in case
|
|
xorw %bx, %bx
|
|
int $0x15 # ignore return code
|
|
|
|
movw $0x5301, %ax # Real Mode connect
|
|
xorw %bx, %bx
|
|
int $0x15
|
|
jc disable_apm_done # error
|
|
|
|
movw $0x5308, %ax # Disable APM
|
|
mov $0xffff, %bx
|
|
xorw %cx, %cx
|
|
int $0x15
|
|
|
|
disable_apm_done:
|
|
ret
|
|
|
|
# This subroutine checks that the keyboard command queue is empty (after
|
|
# emptying the output buffers). No timeout is used - if this hangs there
|
|
# is something wrong with the machine, and we probably couldn't proceed
|
|
# anyway.
|
|
|
|
empty_8042:
|
|
call delay
|
|
inb $0x64, %al # 8042 status port
|
|
cmpb $0xff, %al # skip if not implemented
|
|
jz empty_8042_ret
|
|
testb $1, %al # anything in the output buffer?
|
|
jz no_output
|
|
call delay
|
|
inb $0x60, %al # read it
|
|
jmp empty_8042
|
|
|
|
no_output:
|
|
testb $2, %al # is input buffer full?
|
|
jnz empty_8042 # yes - loop
|
|
empty_8042_ret:
|
|
ret
|
|
|
|
# This subroutine provides a short delay.
|
|
|
|
delay:
|
|
.word 0x00eb # jmp $+2
|
|
ret
|
|
|
|
# A minimal GDT and IDT.
|
|
|
|
.align 4
|
|
gdt:
|
|
.quad 0x0000000000000000 # NULL descriptor
|
|
.quad 0x0000000000000000 # not used
|
|
.quad 0x00c09a0000007fff # 128MB 32-bit code at 0x000000
|
|
.quad 0x00c0920000007fff # 128MB 32-bit code at 0x000000
|
|
gdt_end:
|
|
|
|
.word 0 # for alignment
|
|
gdt_descr:
|
|
.word gdt_end - gdt - 1 # gdt limit
|
|
.long gdt - setup # gdt base - relocated at run time
|
|
|
|
.word 0 # for alignment
|
|
idt_descr:
|
|
.word 0 # idt limit=0
|
|
.long 0 # idt base=0
|
|
|
|
mt86plus_version:
|
|
.ascii "Memtest86+ v" , MT_VERSION
|
|
mt86plus_version_end:
|
|
.byte 0
|
|
|
|
pe_header:
|
|
.ascii "PE"
|
|
.word 0
|
|
|
|
coff_header:
|
|
#ifdef __x86_64__
|
|
.word IMAGE_FILE_MACHINE_X64 # Machine (x86-64)
|
|
#else
|
|
.word IMAGE_FILE_MACHINE_I386 # Machine (i386)
|
|
#endif
|
|
.word 4 # NumberOfSections
|
|
.long 0 # TimeDateStamp
|
|
.long 0 # PointerToSymbolTable
|
|
.long 0 # NumberOfSymbols
|
|
.word section_table - optional_header # SizeOfOptionalHeader
|
|
#ifdef __x86_64__
|
|
.word IMAGE_FILE_DEBUG_STRIPPED \
|
|
| IMAGE_FILE_LOCAL_SYMS_STRIPPED \
|
|
| IMAGE_FILE_LINE_NUMS_STRIPPED \
|
|
| IMAGE_FILE_EXECUTABLE_IMAGE # Characteristics
|
|
#else
|
|
.word IMAGE_FILE_32BIT_MACHINE \
|
|
| IMAGE_FILE_DEBUG_STRIPPED \
|
|
| IMAGE_FILE_LOCAL_SYMS_STRIPPED \
|
|
| IMAGE_FILE_LINE_NUMS_STRIPPED \
|
|
| IMAGE_FILE_EXECUTABLE_IMAGE # Characteristics.
|
|
#endif
|
|
|
|
optional_header:
|
|
#ifdef __x86_64__
|
|
.word IMAGE_NT_OPTIONAL_HDR64_MAGIC # PE32+ format
|
|
#else
|
|
.word IMAGE_NT_OPTIONAL_HDR32_MAGIC # PE32 format
|
|
#endif
|
|
.byte 0x02 # MajorLinkerVersion
|
|
.byte 0x14 # MinorLinkerVersion
|
|
|
|
.long _virt_text_size # SizeOfCode
|
|
.long _virt_init_size # SizeOfInitializedData
|
|
.long 0 # SizeOfUninitializedData
|
|
|
|
.long _virt_text_start + 0x1e0 # AddressOfEntryPoint
|
|
|
|
.long _virt_text_start # BaseOfCode
|
|
#ifndef __x86_64__
|
|
.long _virt_data_start # BaseOfData
|
|
#endif
|
|
|
|
extra_header_fields:
|
|
#ifdef __x86_64__
|
|
.quad IMAGE_BASE # ImageBase
|
|
#else
|
|
.long IMAGE_BASE # ImageBase
|
|
#endif
|
|
.long 4096 # SectionAlignment
|
|
.long 512 # FileAlignment
|
|
.word 0 # MajorOperatingSystemVersion
|
|
.word 0 # MinorOperatingSystemVersion
|
|
.word 0 # MajorImageVersion
|
|
.word 0 # MinorImageVersion
|
|
.word 0 # MajorSubsystemVersion
|
|
.word 0 # MinorSubsystemVersion
|
|
.long 0 # Win32VersionValue
|
|
|
|
.long _virt_img_size # SizeOfImage
|
|
.long _file_head_size # SizeOfHeaders
|
|
.long 0 # CheckSum
|
|
.word 10 # Subsystem (EFI application)
|
|
.word IMAGE_DLLCHARACTERISTICS_NX_COMPAT # DllCharacteristics
|
|
#ifdef __x86_64__
|
|
.quad 0 # SizeOfStackReserve
|
|
.quad 0 # SizeOfStackCommit
|
|
.quad 0 # SizeOfHeapReserve
|
|
.quad 0 # SizeOfHeapCommit
|
|
#else
|
|
.long 0 # SizeOfStackReserve
|
|
.long 0 # SizeOfStackCommit
|
|
.long 0 # SizeOfHeapReserve
|
|
.long 0 # SizeOfHeapCommit
|
|
#endif
|
|
.long 0 # LoaderFlags
|
|
.long IMAGE_DIRECTORY_ENTRY_DEBUG # NumberOfRvaAndSizes
|
|
|
|
.long 0 # DataDirectory.Export.VirtualAddress
|
|
.long 0 # DataDirectory.Export.Size
|
|
.long 0 # DataDirectory.Import.VirtualAddress
|
|
.long 0 # DataDirectory.Import.Size
|
|
.long 0 # DataDirectory.Resource.VirtualAddress
|
|
.long 0 # DataDirectory.Resource.Size
|
|
.long 0 # DataDirectory.Exception.VirtualAddress
|
|
.long 0 # DataDirectory.Exception.Size
|
|
.long 0 # DataDirectory.Certs.VirtualAddress
|
|
.long 0 # DataDirectory.Certs.Size
|
|
.long _virt_reloc_start # DataDirectory.BaseReloc.VirtualAddress
|
|
.long _real_reloc_size # DataDirectory.BaseReloc.Size
|
|
|
|
# Section table
|
|
section_table:
|
|
.ascii ".text"
|
|
.byte 0
|
|
.byte 0
|
|
.byte 0
|
|
.long _virt_text_size # VirtualSize
|
|
.long _virt_text_start # VirtualAddress
|
|
.long _file_text_size # SizeOfRawData
|
|
.long _file_text_start # PointerToRawData
|
|
.long 0 # PointerToRelocations
|
|
.long 0 # PointerToLineNumbers
|
|
.word 0 # NumberOfRelocations
|
|
.word 0 # NumberOfLineNumbers
|
|
.long IMAGE_SCN_MEM_READ \
|
|
| IMAGE_SCN_MEM_EXECUTE \
|
|
| IMAGE_SCN_CNT_CODE # Characteristics (section flags)
|
|
|
|
.ascii ".data"
|
|
.byte 0
|
|
.byte 0
|
|
.byte 0
|
|
.long _virt_data_size # VirtualSize
|
|
.long _virt_data_start # VirtualAddress
|
|
.long _file_data_size # SizeOfRawData
|
|
.long _file_data_start # PointerToRawData
|
|
.long 0 # PointerToRelocations
|
|
.long 0 # PointerToLineNumbers
|
|
.word 0 # NumberOfRelocations
|
|
.word 0 # NumberOfLineNumbers
|
|
.long IMAGE_SCN_MEM_READ \
|
|
| IMAGE_SCN_MEM_WRITE \
|
|
| IMAGE_SCN_CNT_INITIALIZED_DATA # Characteristics (section flags)
|
|
|
|
.ascii ".reloc"
|
|
.byte 0
|
|
.byte 0
|
|
.long _virt_reloc_size # VirtualSize
|
|
.long _virt_reloc_start # VirtualAddress
|
|
.long _file_reloc_size # SizeOfRawData
|
|
.long _file_reloc_start # PointerToRawData
|
|
.long 0 # PointerToRelocations
|
|
.long 0 # PointerToLineNumbers
|
|
.word 0 # NumberOfRelocations
|
|
.word 0 # NumberOfLineNumbers
|
|
.long IMAGE_SCN_MEM_READ \
|
|
| IMAGE_SCN_CNT_INITIALIZED_DATA # Characteristics (section flags)
|
|
|
|
.ascii ".sbat"
|
|
.byte 0
|
|
.byte 0
|
|
.byte 0
|
|
.long _virt_sbat_size # VirtualSize
|
|
.long _virt_sbat_start # VirtualAddress
|
|
.long _file_sbat_size # SizeOfRawData
|
|
.long _file_sbat_start # PointerToRawData
|
|
.long 0 # PointerToRelocations
|
|
.long 0 # PointerToLineNumbers
|
|
.word 0 # NumberOfRelocations
|
|
.word 0 # NumberOfLineNumbers
|
|
.long IMAGE_SCN_MEM_READ \
|
|
| IMAGE_SCN_CNT_INITIALIZED_DATA # Characteristics (section flags)
|
|
|
|
# Pad to the declared size.
|
|
.org (SETUP_SECS*512)
|
|
|
|
.section ".reloc"
|
|
.long 0 // Page RVA
|
|
.long 10 // Block Size (2*4+2)
|
|
.word (IMAGE_REL_BASED_ABSOLUTE << 12) + 0 // reloc 0 -> 0
|
|
|
|
.section ".sbat", "a", @progbits
|
|
.incbin "../boot/sbat.csv"
|