summaryrefslogtreecommitdiff
path: root/gpxe/src/arch/i386/prefix/romprefix.S
blob: 1eb87c37082743408efd6986e4b7060b92646dc8 (plain)
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
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
/* At entry, the processor is in 16 bit real mode and the code is being
 * executed from an address it was not linked to. Code must be pic and
 * 32 bit sensitive until things are fixed up.
 *
 * Also be very careful as the stack is at the rear end of the interrupt
 * table so using a noticeable amount of stack space is a no-no.
 */

#define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
#define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) )
#define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
#define PNP_GET_BBS_VERSION 0x60

	.text
	.code16
	.arch i386
	.section ".prefix", "ax", @progbits
	
	.org	0x00
romheader:
	.word	0xAA55			/* BIOS extension signature */
romheader_size:	.byte _load_size_sect	/* Size in 512-byte blocks */
	jmp	init			/* Initialisation vector */
checksum:
	.byte	0
	.org	0x16
	.word	undiheader
	.org	0x18
	.word	pciheader
	.org	0x1a
	.word	pnpheader
	.size romheader, . - romheader
	
	.section ".zinfo.fixup", "a"	/* Compressor fixup information */
	.ascii	"SUBB"
	.long	romheader_size
	.long	512
	.long	0
	.previous

pciheader:
	.ascii	"PCIR"			/* Signature */
	.word	pci_vendor_id		/* Vendor ID */ 
	.word	pci_device_id		/* Device ID */
	.word	0x0000			/* pointer to vital product data */
	.word	pciheader_len		/* PCI data structure length */
	.byte	0x00			/* PCI data structure revision */
	.byte	0x02			/* Device Base Type code */
	.byte	0x00			/* Device Sub-Type code */
	.byte	0x00			/* Device Interface Type code */
pciheader_size:	.word _load_size_sect	/* Image length same as offset 02h */
	.word	0x0001			/* revision level of code/data */
	.byte	0x00			/* code type */
	.byte	0x80			/* Flags (last PCI data structure) */
	.word	0x0000			/* reserved */
	.equ pciheader_len, . - pciheader
	.size pciheader, . - pciheader
	
	.section ".zinfo.fixup", "a"	/* Compressor fixup information */
	.ascii	"SUBW"
	.long	pciheader_size
	.long	512
	.long	0
	.previous

pnpheader:
	.ascii	"$PnP"			/* Signature */
	.byte	0x01			/* Structure revision */
	.byte	( pnpheader_len	/ 16 )	/* Length (in 16 byte increments) */
	.word	0x0000			/* Offset of next header */
	.byte	0x00			/* Reserved */
	.byte	0x00			/* Checksum */
	.long	0x00000000		/* Device identifier */
	.word	mfgstr			/* Manufacturer string */
	.word	prodstr			/* Product name */
	.byte	0x02			/* Device base type code */
	.byte	0x00			/* Device sub-type code */
	.byte	0x00			/* Device interface type code */
	.byte	0x54			/* Device indicator */
	.word	0x0000			/* Boot connection vector */
	.word	0x0000			/* Disconnect vector */
	.word	bev_entry		/* Boot execution vector */
	.word	0x0000			/* Reserved */
	.word	0x0000			/* Static resource information vector*/
	.equ pnpheader_len, . - pnpheader
	.size pnpheader, . - pnpheader

/* Manufacturer string */
mfgstr:
	.asciz	"http://etherboot.org"
	.size mfgstr, . - mfgstr

/* Product string
 *
 * Defaults to "gPXE".  If the ROM image is writable at initialisation
 * time, it will be filled in to include the PCI bus:dev.fn number of
 * the card as well.
 */
prodstr:
	.ascii	"gPXE"
prodstr_separator:
	.byte	0
	.ascii	"(PCI "
prodstr_pci_id:
	.asciz	"xx:xx.x)"		/* Filled in by init code */
	.size prodstr, . - prodstr
	
undiheader:
	.ascii	"UNDI"			/* Signature */
	.byte	undiheader_len		/* Length of structure */
	.byte	0			/* Checksum */
	.byte	0			/* Structure revision */
	.byte	0,1,2			/* PXE version: 2.1.0 */
	.word	undiloader		/* Offset to loader routine */
	.word	_data16_size		/* Stack segment size */
	.word	_data16_size		/* Data segment size */
	.word	_text16_size		/* Code segment size */
	.equ undiheader_len, . - undiheader
	.size undiheader, . - undiheader

/* Initialisation (called once during POST)
 *
 * Determine whether or not this is a PnP system via a signature
 * check.  If it is PnP, return to the PnP BIOS indicating that we are
 * a boot-capable device; the BIOS will call our boot execution vector
 * if it wants to boot us.  If it is not PnP, hook INT 19.
 */
init:
	/* Preserve registers, clear direction flag, set %ds=%cs */
	pushaw
	pushw	%ds
	pushw	%es
	pushw	%fs
	cld
	pushw	%cs
	popw	%ds
	pushw	$0x40
	popw	%fs
	movw	%di, %bx
	xorw	%di, %di
	/* Print message as early as possible */
	movw	$init_message, %si
	call	print_message
	call	print_pci_busdevfn
	/* Fill in product name string, if possible */
	movw	$prodstr_pci_id, %di
	call	print_pci_busdevfn
	movb	$' ', prodstr_separator
	xorw	%di, %di
	/* Check for PnP BIOS */
	testw	$0x0f, %bx	/* PnP signature must be aligned - bochs    */
	jnz	hook_int19	/* uses unalignment to indicate 'fake' PnP. */
	cmpl	$PNP_SIGNATURE, %es:0(%bx)
	jne	hook_int19
	/* Is PnP: print PnP message */
	movw	$init_message_pnp, %si
	call	print_message
	/* Check for BBS */
	pushw	%es:0x1b(%bx)	/* Real-mode data segment */
	pushw	%ds		/* &(bbs_version) */
	pushw	$bbs_version
	pushw	$PNP_GET_BBS_VERSION
	lcall	*%es:0xd(%bx)
	addw	$8, %sp
	testw	%ax, %ax
	jne	hook_int19
	movw	$init_message_bbs, %si
	call	print_message
	jmp	hook_bbs
	/* Not BBS-compliant - must hook INT 19 */
hook_int19:
	movw	$init_message_int19, %si
	call	print_message
	xorw	%ax, %ax
	movw	%ax, %es
	pushl	%es:( 0x19 * 4 )
	popl	orig_int19
	pushw	%cs
	pushw	$int19_entry
	popl	%es:( 0x19 * 4 )
hook_bbs:
	/* Check for PMM */
	movw	$( 0xe00 - 1 ), %bx
pmm_scan:
	incw	%bx
	jz	no_pmm
	movw	%bx, %es
	cmpl	$PMM_SIGNATURE, %es:0
	jne	pmm_scan
	xorw	%dx, %dx
	xorw	%si, %si
	movzbw	%es:5, %cx
1:	es lodsb
	addb	%al, %dl
	loop	1b
	jnz	pmm_scan
	/* PMM found: print PMM message */
	movw	$init_message_pmm, %si
	call	print_message
	/* Try to allocate 2MB block via PMM */
	pushw	$0x0006		/* Aligned, extended memory */
	pushl	$0xffffffff	/* No handle */
	pushl	$( 0x00200000 / 16 ) /* 2MB in paragraphs */
	pushw	$0x0000		/* pmmAllocate */
	lcall	*%es:7
	addw	$12, %sp
	testw	%dx, %dx	/* %ax==0 even on success, since align=2MB */
	jnz	gotpmm
	movw	$init_message_pmm_failed, %si
	call	print_message
	jmp	no_pmm
gotpmm:	/* PMM allocation succeeded: copy ROM to PMM block */
	pushal			/* PMM presence implies 1kB stack */
	movw	%ax, %es	/* %ax=0 already - see above */
	pushw	%dx
	pushw	%ax
	popl	%edi
	movl	%edi, image_source
	xorl	%esi, %esi
	movzbl	romheader_size, %ecx
	shll	$9, %ecx
	addr32 rep movsb	/* PMM presence implies flat real mode */
	movl	%edi, decompress_to
	/* Shrink ROM and update checksum */
	xorw	%bx, %bx
	xorw	%si, %si
	movw	$_prefix_size_sect, %cx
	movb	%cl, romheader_size
	shlw	$9, %cx
1:	lodsb
	addb	%al, %bl
	loop	1b
	subb	%bl, checksum
	popal
no_pmm:	/* Prompt for POST-time shell */
	movw	$init_message_prompt, %si
	call	print_message
	/* Empty the keyboard buffer before waiting for input */
empty_keyboard_buffer:
	movb	$0x01, %ah
	int	$0x16
	jz	1f
	xorw	%ax, %ax
	int	$0x16
	jmp	empty_keyboard_buffer
1:	/* Wait for up to 3s for a key press */
	movw	$(18 * 3), %cx	/* Approx 3s worth of timer ticks */
wait_for_key:
	decw	%cx
	jz	no_key_pressed
	/* Wait for timer tick to be updated */
	movl	%fs:(0x6c), %eax
1:	pushf
	sti
	hlt
	popf
	cmpl	%fs:(0x6c), %eax
	je	1b
	/* Check to see if a key was pressed */
	movb	$0x01, %ah
	int	$0x16
	jz	wait_for_key
	/* Check to see if key was Ctrl-B */
	cmpb	$0x02, %al
	je	1f
	/* Key was not Ctrl-B: remove from buffer and stop waiting */
	xorw	%ax, %ax
	int	$0x16
	jmp	no_key_pressed
1:	/* Key was Ctrl-B: leave in keyboard buffer and invoke gPXE.
	 * The keypress will be picked up by the initial shell
	 * prompt, and we will drop into a shell.
	 */
	pushw	%cs
	call	exec
no_key_pressed:
	/* Print blank lines to terminate messages */
	movw	$init_message_end, %si
	call	print_message
	/* Restore registers */
	popw	%fs
	popw	%es
	popw	%ds
	popaw
	/* Indicate boot capability to PnP BIOS, if present */
	movw	$0x20, %ax
	lret
	.size init, . - init

init_message:
	.asciz	"gPXE (http://etherboot.org) - PCI "
	.size	init_message, . - init_message
init_message_pnp:
	.asciz	" PnP"
	.size	init_message_pnp, . - init_message_pnp
init_message_bbs:
	.asciz	" BBS"
	.size	init_message_bbs, . - init_message_bbs
init_message_pmm:
	.asciz	" PMM"
	.size	init_message_pmm, . - init_message_pmm
init_message_pmm_failed:
	.asciz	"(failed)"
	.size	init_message_pmm_failed, . - init_message_pmm_failed
init_message_int19:
	.asciz	" INT19"
	.size	init_message_int19, . - init_message_int19
init_message_prompt:
	.asciz	"\nPress Ctrl-B to configure gPXE..."
	.size	init_message_prompt, . - init_message_prompt
init_message_end:
	.asciz	"\n\n\n"
	.size	init_message_end, . - init_message_end

/* ROM image location
 *
 * May be either within option ROM space, or within PMM-allocated block.
 */
image_source:
	.long	0
	.size	image_source, . - image_source

/* Temporary decompression area
 *
 * May be either at HIGHMEM_LOADPOINT, or within PMM-allocated block.
 */
decompress_to:
	.long	HIGHMEM_LOADPOINT
	.size	decompress_to, . - decompress_to

/* BBS version
 *
 * Filled in by BBS BIOS.  We ignore the value.
 */
bbs_version:
	.word	0

/* Boot Execution Vector entry point
 *
 * Called by the PnP BIOS when it wants to boot us.
 */
bev_entry:
	pushw	%cs
	call	exec
	lret
	.size	bev_entry, . - bev_entry

/* INT19 entry point
 *
 * Called via the hooked INT 19 if we detected a non-PnP BIOS.  We
 * attempt to return via the original INT 19 vector (if we were able to
 * store it).
 */
int19_entry:
	pushw	%cs
	call	exec
	movl	%cs:orig_int19, %eax
	testl	%eax, %eax
	je	1f
	/* Chain to original INT 19 vector */
	ljmp	*%cs:orig_int19
1:	/* No chained vector: issue INT 18 as a last resort */
	int	$0x18
	.size	int19_entry, . - int19_entry
orig_int19:
	.long	0
	.size	orig_int19, . - orig_int19

/* Execute as a boot device
 *
 */
exec:	/* Set %ds = %cs */
	pushw	%cs
	popw	%ds

	/* Print message as soon as possible */
	movw	$exec_message, %si
	xorw	%di, %di
	call	print_message

	/* Store magic word on BIOS stack and remember BIOS %ss:sp */
	pushl	$STACK_MAGIC
	movw	%ss, %dx
	movw	%sp, %bp

	/* Obtain a reasonably-sized temporary stack */
	xorw	%ax, %ax
	movw	%ax, %ss
	movw	$0x7c00, %sp

	/* Install gPXE */
	movl	image_source, %esi
	movl	decompress_to, %edi
	call	alloc_basemem
	call	install_prealloc

	/* Set up real-mode stack */
	movw	%bx, %ss
	movw	$_estack16, %sp

	/* Jump to .text16 segment */
	pushw	%ax
	pushw	$1f
	lret
	.section ".text16", "awx", @progbits
1:	/* Call main() */
	pushl	$main
	pushw	%cs
	call	prot_call
	/* No need to clean up stack; we are about to reload %ss:sp */
	
	/* Restore BIOS stack */
	movw	%dx, %ss
	movw	%bp, %sp

	/* Check magic word on BIOS stack */
	popl	%eax
	cmpl	$STACK_MAGIC, %eax
	jne	1f
	/* BIOS stack OK: return to caller */
	lret
1:	/* BIOS stack corrupt: use INT 18 */
	int	$0x18
	.previous

exec_message:
	.asciz	"Entering gPXE\n"
	.size exec_message, . - exec_message

/* UNDI loader
 *
 * Called by an external program to load our PXE stack.
 */
undiloader:
	/* Save registers */
	pushl	%esi
	pushl	%edi
	pushw	%es
	pushw	%bx
	/* UNDI loader parameter structure address into %es:%di */
	movw	%sp, %bx
	movw	%ss:12(%bx), %di
	movw	%ss:14(%bx), %es
	/* Install to specified real-mode addresses */
	pushw	%di
	movw	%es:12(%di), %bx
	movw	%es:14(%di), %ax
	movl	%cs:image_source, %esi
	movl	%cs:decompress_to, %edi
	call	install_prealloc
	popw	%di
	/* Call UNDI loader C code */
	pushl	$pxe_loader_call
	pushw	%cs
	pushw	$1f
	pushw	%ax
	pushw	$prot_call
	lret
1:	popw	%bx	/* discard */
	popw	%bx	/* discard */
	/* Restore registers and return */
	popw	%bx
	popw	%es
	popl	%edi
	popl	%esi
	lret
	.size undiloader, . - undiloader