1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395
|
// SPDX-License-Identifier: GPL-2.0
//
// setup.S 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.
//
// Copyright (C) 2020-2021 Martin Whitaker.
//
// Derived from memtest86+ setup.S and head.S:
//
// 1-Jan-96 Modified by Chris Brady for use as a boot/loader for memtest-86.
#define __ASSEMBLY__
#include "boot.h"
#include "build_version.h"
#define BOOT_PARAMS_START (SETUP_SECS * 512)
#define BOOT_PARAMS_END (BOOT_PARAMS_START + 4096)
.section ".setup", "ax", @progbits
.code16
# Emulate the Linux boot header, to allow loading by other boot loaders.
# Indicate that the main program code should be loaded in high memory.
# bootsect.S will fix up the values if we are booted directly from the BIOS.
.globl setup
setup:
jmp do_setup
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
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
.byte 0
# Pad to the declared size.
.org (SETUP_SECS*512)
|