2 * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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.
20 FILE_LICENCE ( GPL2_OR_LATER )
25 * High memory temporary load address
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.
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.
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.
38 .globl HIGHMEM_LOADPOINT
39 .equ HIGHMEM_LOADPOINT, ( 4 << 20 )
41 /* Image compression enabled */
46 /*****************************************************************************
47 * Utility function: print character (with LF -> LF,CR translation)
50 * %al : character to print
51 * %ds:di : output buffer (or %di=0 to print to console)
53 * %ds:di : next character in output buffer (if applicable)
54 *****************************************************************************
56 .section ".prefix.lib", "awx", @progbits
58 .globl print_character
60 /* Preserve registers */
64 /* If %di is non-zero, write character to buffer and exit */
70 1: /* Print character */
71 movw $0x0007, %bx /* page 0, attribute 7 (normal) */
72 movb $0x0e, %ah /* write char, tty mode */
73 cmpb $0x0a, %al /* '\n'? */
78 /* Restore registers and return */
83 .size print_character, . - print_character
85 /*****************************************************************************
86 * Utility function: print a NUL-terminated string
89 * %ds:si : string to print
90 * %ds:di : output buffer (or %di=0 to print to console)
92 * %ds:si : character after terminating NUL
93 * %ds:di : next character in output buffer (if applicable)
94 *****************************************************************************
96 .section ".prefix.lib", "awx", @progbits
100 /* Preserve registers */
108 2: /* Restore registers and return */
111 .size print_message, . - print_message
113 /*****************************************************************************
114 * Utility functions: print hex digit/byte/word/dword
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)
123 * %ds:di : next character in output buffer (if applicable)
124 *****************************************************************************
126 .section ".prefix.lib", "awx", @progbits
128 .globl print_hex_dword
134 .size print_hex_dword, . - print_hex_dword
135 .globl print_hex_word
141 .size print_hex_word, . - print_hex_word
142 .globl print_hex_byte
145 call print_hex_nibble
148 .size print_hex_byte, . - print_hex_byte
149 .globl print_hex_nibble
151 /* Preserve registers */
153 /* Print digit (technique by Norbert Juffa <norbert.juffa@amd.com> */
159 /* Restore registers and return */
162 .size print_hex_nibble, . - print_hex_nibble
164 /*****************************************************************************
165 * Utility function: print PCI bus:dev.fn
168 * %ax : PCI bus:dev.fn to print
169 * %ds:di : output buffer (or %di=0 to print to console)
171 * %ds:di : next character in output buffer (if applicable)
172 *****************************************************************************
174 .section ".prefix.lib", "awx", @progbits
176 .globl print_pci_busdevfn
178 /* Preserve registers */
196 call print_hex_nibble
197 /* Restore registers and return */
200 .size print_pci_busdevfn, . - print_pci_busdevfn
202 /*****************************************************************************
203 * Utility function: clear current line
206 * %ds:di : output buffer (or %di=0 to print to console)
208 * %ds:di : next character in output buffer (if applicable)
209 *****************************************************************************
211 .section ".prefix.lib", "awx", @progbits
213 .globl print_kill_line
215 /* Preserve registers */
221 /* Print 79 spaces */
224 1: call print_character
229 /* Restore registers and return */
233 .size print_kill_line, . - print_kill_line
235 /****************************************************************************
236 * pm_call (real-mode near call)
238 * Call routine in 16-bit protected mode for access to extended memory
241 * %ax : address of routine to call in 16-bit protected mode
247 * The specified routine is called in 16-bit protected mode, with:
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
253 ****************************************************************************
258 /* GDT for protected-mode calls */
259 .section ".prefix.lib", "awx", @progbits
263 gdt_limit: .word gdt_length - 1
265 .word 0 /* padding */
266 pm_cs: /* 16-bit protected-mode code segment */
267 .equ PM_CS, pm_cs - gdt
269 .byte 0, 0x9b, 0x00, 0
270 pm_ss: /* 16-bit protected-mode stack segment */
271 .equ PM_SS, pm_ss - gdt
273 .byte 0, 0x93, 0x00, 0
274 pm_ds: /* 32-bit protected-mode flat data segment */
275 .equ PM_DS, pm_ds - gdt
277 .byte 0, 0x93, 0xcf, 0
279 .equ gdt_length, . - gdt
282 .section ".prefix.lib", "awx", @progbits
286 .size pm_saved_gdt, . - pm_saved_gdt
288 .equ pm_call_vars_size, . - pm_call_vars
289 #define PM_CALL_VAR(x) ( -pm_call_vars_size + ( (x) - pm_call_vars ) )
291 .section ".prefix.lib", "awx", @progbits
294 /* Preserve registers, flags, and RM return point */
297 subw $pm_call_vars_size, %sp
308 /* Set up local variable block, and preserve GDT */
314 movw $pm_call_vars, %si
315 leaw PM_CALL_VAR(pm_call_vars)(%bp), %di
316 movw $pm_call_vars_size, %cx
321 sgdt PM_CALL_VAR(pm_saved_gdt)(%bp)
323 /* Set up GDT bases */
330 addr32 leal PM_CALL_VAR(gdt)(%eax, %edi), %eax
331 movl %eax, PM_CALL_VAR(gdt_base)(%bp)
333 movw $PM_CALL_VAR(pm_cs), %di
336 movw $PM_CALL_VAR(pm_ss), %di
341 /* Switch CPU to protected mode and load up segment registers */
344 data32 lgdt PM_CALL_VAR(gdt)(%bp)
358 /* Call PM routine */
361 /* Set real-mode segment limits on %ds, %es, %fs and %gs */
368 /* Return CPU to real mode */
373 /* Restore registers and flags */
374 lret /* will ljmp to 99f */
380 data32 lgdt PM_CALL_VAR(pm_saved_gdt)(%bp)
385 .size pm_call, . - pm_call
390 andw $0xfff0, 2(%bp,%di)
392 andb $0x0f, 4(%bp,%di)
394 .size set_seg_base, . - set_seg_base
396 #endif /* KEEP_IT_REAL */
398 /****************************************************************************
399 * copy_bytes (real-mode or 16-bit protected-mode near call)
404 * %ds:esi : source address
405 * %es:edi : destination address
408 * %ds:esi : next source address
409 * %es:edi : next destination address
412 ****************************************************************************
414 .section ".prefix.lib", "awx", @progbits
421 .size copy_bytes, . - copy_bytes
423 /****************************************************************************
424 * install_block (real-mode near call)
426 * Install block to specified address
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)
434 * %esi : next source physical address (will be a multiple of 16)
437 ****************************************************************************
439 .section ".prefix.lib", "awx", @progbits
445 /* Preserve registers */
451 /* Convert %esi and %edi to segment registers */
459 #else /* KEEP_IT_REAL */
461 /* Call self in protected mode */
468 /* Preserve registers */
472 #endif /* KEEP_IT_REAL */
476 /* Decompress source to destination */
479 /* Copy source to destination */
483 /* Zero .bss portion */
491 /* Round up %esi to start of next source block */
498 /* Convert %ds:esi back to a physical address */
503 /* Restore registers */
509 #else /* KEEP_IT_REAL */
511 /* Restore registers */
518 .size install_block, . - install_block
520 /****************************************************************************
521 * alloc_basemem (real-mode near call)
523 * Allocate space for .text16 and .data16 from top of base memory.
524 * Memory is allocated using the BIOS free base memory counter at
530 * %ax : .text16 segment address
531 * %bx : .data16 segment address
534 ****************************************************************************
536 .section ".prefix.lib", "awx", @progbits
540 /* Preserve registers */
543 /* FBMS => %ax as segment address */
549 /* Calculate .data16 segment address */
550 subw $_data16_memsz_pgh, %ax
553 /* Calculate .text16 segment address */
554 subw $_text16_memsz_pgh, %ax
561 /* Retrieve .text16 and .data16 segment addresses */
565 /* Restore registers and return */
568 .size alloc_basemem, . - alloc_basemem
570 /****************************************************************************
571 * free_basemem (real-mode near call)
573 * Free space allocated with alloc_basemem.
576 * %ax : .text16 segment address
577 * %bx : .data16 segment address
579 * %ax : 0 if successfully freed
582 ****************************************************************************
584 .section ".text16", "ax", @progbits
588 /* Preserve registers */
591 /* Check FBMS counter */
600 /* Check hooked interrupt count */
601 cmpw $0, %cs:hooked_bios_interrupts
604 /* OK to free memory */
605 addw $_text16_memsz_pgh, %ax
606 addw $_data16_memsz_pgh, %ax
611 1: /* Restore registers and return */
614 .size free_basemem, . - free_basemem
616 .section ".text16.data", "aw", @progbits
617 .globl hooked_bios_interrupts
618 hooked_bios_interrupts:
620 .size hooked_bios_interrupts, . - hooked_bios_interrupts
622 /****************************************************************************
623 * install (real-mode near call)
625 * Install all text and data segments.
630 * %ax : .text16 segment address
631 * %bx : .data16 segment address
634 ****************************************************************************
636 .section ".prefix.lib", "awx", @progbits
640 /* Preserve registers */
643 /* Allocate space for .text16 and .data16 */
645 /* Image source = %cs:0000 */
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 */
655 .size install, . - install
657 /****************************************************************************
658 * install_prealloc (real-mode near call)
660 * Install all text and data segments.
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
669 ****************************************************************************
671 .section ".prefix.lib", "awx", @progbits
673 .globl install_prealloc
680 /* Sanity: clear the direction flag asap */
683 /* Calculate physical address of payload (i.e. first source) */
688 1: addl $_payload_lma, %esi
690 /* Install .text16 and .data16 */
694 movl $_text16_memsz, %ecx
696 call install_block /* .text16 */
699 movl $_data16_filesz, %ecx
700 movl $_data16_memsz, %edx
701 call install_block /* .data16 */
704 /* Set up %ds for access to .data16 */
708 /* Initialise libkir */
709 movw %ax, (init_libkir_vector+2)
710 lcall *init_libkir_vector
712 /* Install .text and .data to temporary area in high memory,
713 * prior to reading the E820 memory map and relocating
716 movl $_textdata_filesz, %ecx
717 movl $_textdata_memsz, %edx
720 /* Initialise librm at current location */
721 movw %ax, (init_librm_vector+2)
722 lcall *init_librm_vector
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.
728 movw %ax, (prot_call_vector+2)
730 lcall *prot_call_vector
731 popl %edx /* discard */
733 /* Copy code to new location */
736 movw $copy_bytes, %ax
741 /* Initialise librm at new location */
742 lcall *init_librm_vector
745 /* Restore registers */
750 .size install_prealloc, . - install_prealloc
752 /* Vectors for far calls to .text16 functions */
753 .section ".data16", "aw", @progbits
758 .size init_libkir_vector, . - init_libkir_vector
763 .size init_librm_vector, . - init_librm_vector
767 .size prot_call_vector, . - prot_call_vector
770 /****************************************************************************
771 * uninstall (real-mode near call)
773 * Uninstall all text and data segments.
776 * %ax : .text16 segment address
777 * %bx : .data16 segment address
782 ****************************************************************************
784 .section ".text16", "ax", @progbits
790 .size uninstall, . - uninstall
794 /* File split information for the compressor */
796 .section ".zinfo", "a", @progbits
811 .long _textdata_filesz
814 .section ".zinfo", "a", @progbits
819 #endif /* COMPRESS */