File: setup.S

package info (click to toggle)
memtest86%2B 7.20-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 1,772 kB
  • sloc: ansic: 22,575; asm: 2,497; makefile: 589; sh: 408
file content (395 lines) | stat: -rw-r--r-- 8,415 bytes parent folder | download | duplicates (3)
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)