1/*
2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 *
18 */
19
20FILE_LICENCE ( GPL2_OR_LATER )
21
22	.arch i386
23
24/**
25 * High memory temporary load address
26 *
27 * Temporary buffer into which to copy (or decompress) our runtime
28 * image, prior to calling get_memmap() and relocate().  We don't
29 * actually leave anything here once install() has returned.
30 *
31 * We use the start of an even megabyte so that we don't have to worry
32 * about the current state of the A20 line.
33 *
34 * We use 4MB rather than 2MB because some PXE stack / PMM BIOS
35 * combinations are known to place data required by other UNDI ROMs
36 * loader around the 2MB mark.
37 */
38	.globl	HIGHMEM_LOADPOINT
39	.equ	HIGHMEM_LOADPOINT, ( 4 << 20 )
40
41/* Image compression enabled */
42#define COMPRESS 1
43
44#define CR0_PE 1
45
46/*****************************************************************************
47 * Utility function: print character (with LF -> LF,CR translation)
48 *
49 * Parameters:
50 *   %al : character to print
51 *   %ds:di : output buffer (or %di=0 to print to console)
52 * Returns:
53 *   %ds:di : next character in output buffer (if applicable)
54 *****************************************************************************
55 */
56	.section ".prefix.lib", "awx", @progbits
57	.code16
58	.globl	print_character
59print_character:
60	/* Preserve registers */
61	pushw	%ax
62	pushw	%bx
63	pushw	%bp
64	/* If %di is non-zero, write character to buffer and exit */
65	testw	%di, %di
66	jz	1f
67	movb	%al, %ds:(%di)
68	incw	%di
69	jmp	3f
701:	/* Print character */
71	movw	$0x0007, %bx		/* page 0, attribute 7 (normal) */
72	movb	$0x0e, %ah		/* write char, tty mode */
73	cmpb	$0x0a, %al		/* '\n'? */
74	jne	2f
75	int	$0x10
76	movb	$0x0d, %al
772:	int	$0x10
78	/* Restore registers and return */
793:	popw	%bp
80	popw	%bx
81	popw	%ax
82	ret
83	.size	print_character, . - print_character
84
85/*****************************************************************************
86 * Utility function: print a NUL-terminated string
87 *
88 * Parameters:
89 *   %ds:si : string to print
90 *   %ds:di : output buffer (or %di=0 to print to console)
91 * Returns:
92 *   %ds:si : character after terminating NUL
93 *   %ds:di : next character in output buffer (if applicable)
94 *****************************************************************************
95 */
96	.section ".prefix.lib", "awx", @progbits
97	.code16
98	.globl	print_message
99print_message:
100	/* Preserve registers */
101	pushw	%ax
102	/* Print string */
1031: 	lodsb
104	testb	%al, %al
105	je	2f
106	call	print_character
107	jmp	1b
1082:	/* Restore registers and return */
109	popw	%ax
110	ret
111	.size	print_message, . - print_message
112
113/*****************************************************************************
114 * Utility functions: print hex digit/byte/word/dword
115 *
116 * Parameters:
117 *   %al (low nibble) : digit to print
118 *   %al : byte to print
119 *   %ax : word to print
120 *   %eax : dword to print
121 *   %ds:di : output buffer (or %di=0 to print to console)
122 * Returns:
123 *   %ds:di : next character in output buffer (if applicable)
124 *****************************************************************************
125 */
126	.section ".prefix.lib", "awx", @progbits
127	.code16
128	.globl	print_hex_dword
129print_hex_dword:
130	rorl	$16, %eax
131	call	print_hex_word
132	rorl	$16, %eax
133	/* Fall through */
134	.size	print_hex_dword, . - print_hex_dword
135	.globl	print_hex_word
136print_hex_word:
137	xchgb	%al, %ah
138	call	print_hex_byte
139	xchgb	%al, %ah
140	/* Fall through */
141	.size	print_hex_word, . - print_hex_word
142	.globl	print_hex_byte
143print_hex_byte:
144	rorb	$4, %al
145	call	print_hex_nibble
146	rorb	$4, %al
147	/* Fall through */
148	.size	print_hex_byte, . - print_hex_byte
149	.globl	print_hex_nibble
150print_hex_nibble:
151	/* Preserve registers */
152	pushw	%ax
153	/* Print digit (technique by Norbert Juffa <norbert.juffa@amd.com> */
154	andb	$0x0f, %al
155	cmpb	$10, %al
156	sbbb	$0x69, %al
157	das
158	call	print_character
159	/* Restore registers and return */
160	popw	%ax
161	ret
162	.size	print_hex_nibble, . - print_hex_nibble
163
164/*****************************************************************************
165 * Utility function: print PCI bus:dev.fn
166 *
167 * Parameters:
168 *   %ax : PCI bus:dev.fn to print
169 *   %ds:di : output buffer (or %di=0 to print to console)
170 * Returns:
171 *   %ds:di : next character in output buffer (if applicable)
172 *****************************************************************************
173 */
174	.section ".prefix.lib", "awx", @progbits
175	.code16
176	.globl	print_pci_busdevfn
177print_pci_busdevfn:
178	/* Preserve registers */
179	pushw	%ax
180	/* Print bus */
181	xchgb	%al, %ah
182	call	print_hex_byte
183	/* Print ":" */
184	movb	$( ':' ), %al
185	call	print_character
186	/* Print device */
187	movb	%ah, %al
188	shrb	$3, %al
189	call	print_hex_byte
190	/* Print "." */
191	movb	$( '.' ), %al
192	call	print_character
193	/* Print function */
194	movb	%ah, %al
195	andb	$0x07, %al
196	call	print_hex_nibble
197	/* Restore registers and return */
198	popw	%ax
199	ret
200	.size	print_pci_busdevfn, . - print_pci_busdevfn
201
202/*****************************************************************************
203 * Utility function: clear current line
204 *
205 * Parameters:
206 *   %ds:di : output buffer (or %di=0 to print to console)
207 * Returns:
208 *   %ds:di : next character in output buffer (if applicable)
209 *****************************************************************************
210 */
211	.section ".prefix.lib", "awx", @progbits
212	.code16
213	.globl	print_kill_line
214print_kill_line:
215	/* Preserve registers */
216	pushw	%ax
217	pushw	%cx
218	/* Print CR */
219	movb	$( '\r' ), %al
220	call	print_character
221	/* Print 79 spaces */
222	movb	$( ' ' ), %al
223	movw	$79, %cx
2241:	call	print_character
225	loop	1b
226	/* Print CR */
227	movb	$( '\r' ), %al
228	call	print_character
229	/* Restore registers and return */
230	popw	%cx
231	popw	%ax
232	ret
233	.size	print_kill_line, . - print_kill_line
234
235/****************************************************************************
236 * pm_call (real-mode near call)
237 *
238 * Call routine in 16-bit protected mode for access to extended memory
239 *
240 * Parameters:
241 *   %ax : address of routine to call in 16-bit protected mode
242 * Returns:
243 *   none
244 * Corrupts:
245 *   %ax
246 *
247 * The specified routine is called in 16-bit protected mode, with:
248 *
249 *   %cs : 16-bit code segment with base matching real-mode %cs
250 *   %ss : 16-bit data segment with base matching real-mode %ss
251 *   %ds,%es,%fs,%gs : 32-bit data segment with zero base and 4GB limit
252 *
253 ****************************************************************************
254 */
255
256#ifndef KEEP_IT_REAL
257
258	/* GDT for protected-mode calls */
259	.section ".prefix.lib", "awx", @progbits
260	.align 16
261pm_call_vars:
262gdt:
263gdt_limit:		.word gdt_length - 1
264gdt_base:		.long 0
265			.word 0 /* padding */
266pm_cs:		/* 16-bit protected-mode code segment */
267	.equ    PM_CS, pm_cs - gdt
268	.word   0xffff, 0
269	.byte   0, 0x9b, 0x00, 0
270pm_ss:		/* 16-bit protected-mode stack segment */
271	.equ    PM_SS, pm_ss - gdt
272	.word   0xffff, 0
273	.byte   0, 0x93, 0x00, 0
274pm_ds:		/* 32-bit protected-mode flat data segment */
275	.equ    PM_DS, pm_ds - gdt
276	.word   0xffff, 0
277	.byte   0, 0x93, 0xcf, 0
278gdt_end:
279	.equ	gdt_length, . - gdt
280	.size	gdt, . - gdt
281
282	.section ".prefix.lib", "awx", @progbits
283	.align 16
284pm_saved_gdt:
285	.long	0, 0
286	.size	pm_saved_gdt, . - pm_saved_gdt
287
288	.equ	pm_call_vars_size, . - pm_call_vars
289#define PM_CALL_VAR(x) ( -pm_call_vars_size + ( (x) - pm_call_vars ) )
290
291	.section ".prefix.lib", "awx", @progbits
292	.code16
293pm_call:
294	/* Preserve registers, flags, and RM return point */
295	pushw	%bp
296	movw	%sp, %bp
297	subw	$pm_call_vars_size, %sp
298	andw	$0xfff0, %sp
299	pushfl
300	pushw	%gs
301	pushw	%fs
302	pushw	%es
303	pushw	%ds
304	pushw	%ss
305	pushw	%cs
306	pushw	$99f
307
308	/* Set up local variable block, and preserve GDT */
309	pushw	%cx
310	pushw	%si
311	pushw	%di
312	pushw	%ss
313	popw	%es
314	movw	$pm_call_vars, %si
315	leaw	PM_CALL_VAR(pm_call_vars)(%bp), %di
316	movw	$pm_call_vars_size, %cx
317	cs rep movsb
318	popw	%di
319	popw	%si
320	popw	%cx
321	sgdt	PM_CALL_VAR(pm_saved_gdt)(%bp)
322
323	/* Set up GDT bases */
324	pushl	%eax
325	pushl	%edi
326	xorl	%eax, %eax
327	movw	%ss, %ax
328	shll	$4, %eax
329	movzwl	%bp, %edi
330	addr32 leal PM_CALL_VAR(gdt)(%eax, %edi), %eax
331	movl	%eax, PM_CALL_VAR(gdt_base)(%bp)
332	movw	%cs, %ax
333	movw	$PM_CALL_VAR(pm_cs), %di
334	call	set_seg_base
335	movw	%ss, %ax
336	movw	$PM_CALL_VAR(pm_ss), %di
337	call	set_seg_base
338	popl	%edi
339	popl	%eax
340
341	/* Switch CPU to protected mode and load up segment registers */
342	pushl	%eax
343	cli
344	data32 lgdt PM_CALL_VAR(gdt)(%bp)
345	movl	%cr0, %eax
346	orb	$CR0_PE, %al
347	movl	%eax, %cr0
348	ljmp	$PM_CS, $1f
3491:	movw	$PM_SS, %ax
350	movw	%ax, %ss
351	movw	$PM_DS, %ax
352	movw	%ax, %ds
353	movw	%ax, %es
354	movw	%ax, %fs
355	movw	%ax, %gs
356	popl	%eax
357
358	/* Call PM routine */
359	call	*%ax
360
361	/* Set real-mode segment limits on %ds, %es, %fs and %gs */
362	movw	%ss, %ax
363	movw	%ax, %ds
364	movw	%ax, %es
365	movw	%ax, %fs
366	movw	%ax, %gs
367
368	/* Return CPU to real mode */
369	movl	%cr0, %eax
370	andb	$0!CR0_PE, %al
371	movl	%eax, %cr0
372
373	/* Restore registers and flags */
374	lret	/* will ljmp to 99f */
37599:	popw	%ss
376	popw	%ds
377	popw	%es
378	popw	%fs
379	popw	%gs
380	data32 lgdt PM_CALL_VAR(pm_saved_gdt)(%bp)
381	popfl
382	movw	%bp, %sp
383	popw	%bp
384	ret
385	.size pm_call, . - pm_call
386
387set_seg_base:
388	rolw	$4, %ax
389	movw	%ax, 2(%bp,%di)
390	andw	$0xfff0, 2(%bp,%di)
391	movb	%al, 4(%bp,%di)
392	andb	$0x0f, 4(%bp,%di)
393	ret
394	.size set_seg_base, . - set_seg_base
395
396#endif /* KEEP_IT_REAL */
397
398/****************************************************************************
399 * copy_bytes (real-mode or 16-bit protected-mode near call)
400 *
401 * Copy bytes
402 *
403 * Parameters:
404 *   %ds:esi : source address
405 *   %es:edi : destination address
406 *   %ecx : length
407 * Returns:
408 *   %ds:esi : next source address
409 *   %es:edi : next destination address
410 * Corrupts:
411 *   None
412 ****************************************************************************
413 */
414	.section ".prefix.lib", "awx", @progbits
415	.code16
416copy_bytes:
417	pushl %ecx
418	rep addr32 movsb
419	popl %ecx
420	ret
421	.size copy_bytes, . - copy_bytes
422
423/****************************************************************************
424 * install_block (real-mode near call)
425 *
426 * Install block to specified address
427 *
428 * Parameters:
429 *   %esi : source physical address (must be a multiple of 16)
430 *   %edi : destination physical address (must be a multiple of 16)
431 *   %ecx : length of (decompressed) data
432 *   %edx : total length of block (including any uninitialised data portion)
433 * Returns:
434 *   %esi : next source physical address (will be a multiple of 16)
435 * Corrupts:
436 *   none
437 ****************************************************************************
438 */
439	.section ".prefix.lib", "awx", @progbits
440	.code16
441install_block:
442
443#ifdef KEEP_IT_REAL
444
445	/* Preserve registers */
446	pushw	%ds
447	pushw	%es
448	pushl	%ecx
449	pushl	%edi
450
451	/* Convert %esi and %edi to segment registers */
452	shrl	$4, %esi
453	movw	%si, %ds
454	xorw	%si, %si
455	shrl	$4, %edi
456	movw	%di, %es
457	xorw	%di, %di
458
459#else /* KEEP_IT_REAL */
460
461	/* Call self in protected mode */
462	pushw	%ax
463	movw	$1f, %ax
464	call	pm_call
465	popw	%ax
466	ret
4671:
468	/* Preserve registers */
469	pushl	%ecx
470	pushl	%edi
471
472#endif /* KEEP_IT_REAL */
473
474
475#if COMPRESS
476	/* Decompress source to destination */
477	call	decompress16
478#else
479	/* Copy source to destination */
480	call	copy_bytes
481#endif
482
483	/* Zero .bss portion */
484	negl	%ecx
485	addl	%edx, %ecx
486	pushw	%ax
487	xorw	%ax, %ax
488	rep addr32 stosb
489	popw	%ax
490
491	/* Round up %esi to start of next source block */
492	addl	$0xf, %esi
493	andl	$~0xf, %esi
494
495
496#ifdef KEEP_IT_REAL
497
498	/* Convert %ds:esi back to a physical address */
499	movzwl	%ds, %cx
500	shll	$4, %ecx
501	addl	%ecx, %esi
502
503	/* Restore registers */
504	popl	%edi
505	popl	%ecx
506	popw	%es
507	popw	%ds
508
509#else /* KEEP_IT_REAL */
510
511	/* Restore registers */
512	popl	%edi
513	popl	%ecx
514
515#endif
516
517	ret
518	.size install_block, . - install_block
519
520/****************************************************************************
521 * alloc_basemem (real-mode near call)
522 *
523 * Allocate space for .text16 and .data16 from top of base memory.
524 * Memory is allocated using the BIOS free base memory counter at
525 * 0x40:13.
526 *
527 * Parameters:
528 *   none
529 * Returns:
530 *   %ax : .text16 segment address
531 *   %bx : .data16 segment address
532 * Corrupts:
533 *   none
534 ****************************************************************************
535 */
536	.section ".prefix.lib", "awx", @progbits
537	.code16
538	.globl	alloc_basemem
539alloc_basemem:
540	/* Preserve registers */
541	pushw	%fs
542
543	/* FBMS => %ax as segment address */
544	pushw	$0x40
545	popw	%fs
546	movw	%fs:0x13, %ax
547	shlw	$6, %ax
548
549	/* Calculate .data16 segment address */
550	subw	$_data16_memsz_pgh, %ax
551	pushw	%ax
552
553	/* Calculate .text16 segment address */
554	subw	$_text16_memsz_pgh, %ax
555	pushw	%ax
556
557	/* Update FBMS */
558	shrw	$6, %ax
559	movw	%ax, %fs:0x13
560
561	/* Retrieve .text16 and .data16 segment addresses */
562	popw	%ax
563	popw	%bx
564
565	/* Restore registers and return */
566	popw	%fs
567	ret
568	.size alloc_basemem, . - alloc_basemem
569
570/****************************************************************************
571 * free_basemem (real-mode near call)
572 *
573 * Free space allocated with alloc_basemem.
574 *
575 * Parameters:
576 *   %ax : .text16 segment address
577 *   %bx : .data16 segment address
578 * Returns:
579 *   %ax : 0 if successfully freed
580 * Corrupts:
581 *   none
582 ****************************************************************************
583 */
584	.section ".text16", "ax", @progbits
585	.code16
586	.globl	free_basemem
587free_basemem:
588	/* Preserve registers */
589	pushw	%fs
590
591	/* Check FBMS counter */
592	pushw	%ax
593	shrw	$6, %ax
594	pushw	$0x40
595	popw	%fs
596	cmpw	%ax, %fs:0x13
597	popw	%ax
598	jne	1f
599
600	/* Check hooked interrupt count */
601	cmpw	$0, %cs:hooked_bios_interrupts
602	jne	1f
603
604	/* OK to free memory */
605	addw	$_text16_memsz_pgh, %ax
606	addw	$_data16_memsz_pgh, %ax
607	shrw	$6, %ax
608	movw	%ax, %fs:0x13
609	xorw	%ax, %ax
610
6111:	/* Restore registers and return */
612	popw	%fs
613	ret
614	.size free_basemem, . - free_basemem
615
616	.section ".text16.data", "aw", @progbits
617	.globl	hooked_bios_interrupts
618hooked_bios_interrupts:
619	.word	0
620	.size	hooked_bios_interrupts, . - hooked_bios_interrupts
621
622/****************************************************************************
623 * install (real-mode near call)
624 *
625 * Install all text and data segments.
626 *
627 * Parameters:
628 *   none
629 * Returns:
630 *   %ax  : .text16 segment address
631 *   %bx  : .data16 segment address
632 * Corrupts:
633 *   none
634 ****************************************************************************
635 */
636	.section ".prefix.lib", "awx", @progbits
637	.code16
638	.globl install
639install:
640	/* Preserve registers */
641	pushl	%esi
642	pushl	%edi
643	/* Allocate space for .text16 and .data16 */
644	call	alloc_basemem
645	/* Image source = %cs:0000 */
646	xorl	%esi, %esi
647	/* Image destination = HIGHMEM_LOADPOINT */
648	movl	$HIGHMEM_LOADPOINT, %edi
649	/* Install text and data segments */
650	call	install_prealloc
651	/* Restore registers and return */
652	popl	%edi
653	popl	%esi
654	ret
655	.size install, . - install
656
657/****************************************************************************
658 * install_prealloc (real-mode near call)
659 *
660 * Install all text and data segments.
661 *
662 * Parameters:
663 *   %ax  : .text16 segment address
664 *   %bx  : .data16 segment address
665 *   %esi : Image source physical address (or zero for %cs:0000)
666 *   %edi : Decompression temporary area physical address
667 * Corrupts:
668 *   none
669 ****************************************************************************
670 */
671	.section ".prefix.lib", "awx", @progbits
672	.code16
673	.globl install_prealloc
674install_prealloc:
675	/* Save registers */
676	pushal
677	pushw	%ds
678	pushw	%es
679
680	/* Sanity: clear the direction flag asap */
681	cld
682
683	/* Calculate physical address of payload (i.e. first source) */
684	testl	%esi, %esi
685	jnz	1f
686	movw	%cs, %si
687	shll	$4, %esi
6881:	addl	$_payload_lma, %esi
689
690	/* Install .text16 and .data16 */
691	pushl	%edi
692	movzwl	%ax, %edi
693	shll	$4, %edi
694	movl	$_text16_memsz, %ecx
695	movl	%ecx, %edx
696	call	install_block		/* .text16 */
697	movzwl	%bx, %edi
698	shll	$4, %edi
699	movl	$_data16_filesz, %ecx
700	movl	$_data16_memsz, %edx
701	call	install_block		/* .data16 */
702	popl	%edi
703
704	/* Set up %ds for access to .data16 */
705	movw	%bx, %ds
706
707#ifdef KEEP_IT_REAL
708	/* Initialise libkir */
709	movw	%ax, (init_libkir_vector+2)
710	lcall	*init_libkir_vector
711#else
712	/* Install .text and .data to temporary area in high memory,
713	 * prior to reading the E820 memory map and relocating
714	 * properly.
715	 */
716	movl	$_textdata_filesz, %ecx
717	movl	$_textdata_memsz, %edx
718	call	install_block
719
720	/* Initialise librm at current location */
721	movw	%ax, (init_librm_vector+2)
722	lcall	*init_librm_vector
723
724	/* Call relocate() to determine target address for relocation.
725	 * relocate() will return with %esi, %edi and %ecx set up
726	 * ready for the copy to the new location.
727	 */
728	movw	%ax, (prot_call_vector+2)
729	pushl	$relocate
730	lcall	*prot_call_vector
731	popl	%edx /* discard */
732
733	/* Copy code to new location */
734	pushl	%edi
735	pushw	%ax
736	movw	$copy_bytes, %ax
737	call	pm_call
738	popw	%ax
739	popl	%edi
740
741	/* Initialise librm at new location */
742	lcall	*init_librm_vector
743
744#endif
745	/* Restore registers */
746	popw	%es
747	popw	%ds
748	popal
749	ret
750	.size install_prealloc, . - install_prealloc
751
752	/* Vectors for far calls to .text16 functions */
753	.section ".data16", "aw", @progbits
754#ifdef KEEP_IT_REAL
755init_libkir_vector:
756	.word init_libkir
757	.word 0
758	.size init_libkir_vector, . - init_libkir_vector
759#else
760init_librm_vector:
761	.word init_librm
762	.word 0
763	.size init_librm_vector, . - init_librm_vector
764prot_call_vector:
765	.word prot_call
766	.word 0
767	.size prot_call_vector, . - prot_call_vector
768#endif
769
770/****************************************************************************
771 * uninstall (real-mode near call)
772 *
773 * Uninstall all text and data segments.
774 *
775 * Parameters:
776 *   %ax  : .text16 segment address
777 *   %bx  : .data16 segment address
778 * Returns:
779 *   none
780 * Corrupts:
781 *   none
782 ****************************************************************************
783 */
784	.section ".text16", "ax", @progbits
785	.code16
786	.globl uninstall
787uninstall:
788	call	free_basemem
789	ret
790	.size uninstall, . - uninstall
791
792
793
794	/* File split information for the compressor */
795#if COMPRESS
796	.section ".zinfo", "a", @progbits
797	.ascii	"COPY"
798	.long	_prefix_lma
799	.long	_prefix_filesz
800	.long	_max_align
801	.ascii	"PACK"
802	.long	_text16_lma
803	.long	_text16_filesz
804	.long	_max_align
805	.ascii	"PACK"
806	.long	_data16_lma
807	.long	_data16_filesz
808	.long	_max_align
809	.ascii	"PACK"
810	.long	_textdata_lma
811	.long	_textdata_filesz
812	.long	_max_align
813#else /* COMPRESS */
814	.section ".zinfo", "a", @progbits
815	.ascii	"COPY"
816	.long	_prefix_lma
817	.long	_filesz
818	.long	_max_align
819#endif /* COMPRESS */
820