; >>> this is file LOADLINM.ASM ;============================================================================ ; LOADLIN v1.6 (C) 1994..1996 Hans Lermen (lermen@elserv.ffm.fgan.de) ; ; This program is free software; you can redistribute it and/or modify ; it under the terms of the GNU General Public License as published by ; the Free Software Foundation; either version 2 of the License, or ; (at your option) any later version. ; ; This program is distributed in the hope that it will be useful, ; but WITHOUT ANY WARRANTY; without even the implied warranty of ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ; GNU General Public License for more details. ; ; You should have received a copy of the GNU General Public License ; along with this program; if not, write to the Free Software ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ; ;---------------------------------------------------------------------------- ; Comments and bug reports are welcome and may be sent to: ; E-Mail: lermen@elserv.ffm.fgan.de ; SnailMail: Hans Lermen ; Am Muehlenweg 38 ; D53424 REMAGEN-Unkelbach ; GERMANY ; ;============================================================================ ;--------------------------------------------------------------------------- call_pmode_routine proc near ; NOTE: must have called get_VCPI_interface before this ; _AND_ must be already moved high, else the pagetable ; will not be aligned. ; input: ; AX = offset of protected mode routine ; The called routine will have a pointer in ebp to a pushad_struct ; of the other input registers, and all segment register point to ; High_seg ; @@regs struc pushAD_struc @@ @@regs ends pushf CLI pushad mov ebp,esp ; ÚÄÄÄÄÄ IRET stack for return to VM86 push 0 push gs push 0 push fs push 0 push ds push 0 push es push 0 push ss push ebp sub sp,4 ; space for EFLAGS push 0 push cs push 0 push offset @@pmode_return ; ÀÄÄÄÄÄÄ IRET stack for return to VM86 mov cs:pmode_return_esp,esp lea ax,@@pmode_task movzx eax,ax mov dword ptr protected_mode_target,eax mov word ptr protected_mode_target+4,g_code movzx esi,cs:High_Seg shl esi,4 lea si,our_CR3[si] mov ax,0DE0Ch int emm_int ; jumps to protected mode ; and comes here in 16-bit protected mode @@pmode_task: mov ax,g_data mov ss,ax mov ds,ax mov es,ax mov fs,ax mov gs,ax mov esp,cs:pmode_return_esp ; ebp pointing to pushad_struct mov ax, word ptr [bp].@@eax call ax ; call the routine ; now we return to VM86 mode cli ; to be save mov ax,g_core ; selector for addressing first MByte mov ds,ax .386p CLTS ; clear taskswitch flag .386 mov eax,0DE0Ch call fword ptr cs:server_vcpi_entry ; back to VM86 ; and returns here in VM86 mode @@pmode_return: popad popf ret call_pmode_routine endp IF 0 AUXPAGE = (modul_end-code_org0) ; behind our code ELSE AUXPAGE = 0 ; at start of our segment, only used in protected mode ENDIF AUXPAGE_size = 02000h AUXPAGE_entry = (AUXPAGE shr (12-2)) AUXPAGE_S = AUXPAGE AUXPAGE_T = AUXPAGE+AUXPAGE_size AUXPAGE_S_entry equ page0+AUXPAGE_entry[bx] AUXPAGE_T_entry equ page0+(AUXPAGE_entry+(AUXPAGE_size shr (12-2)))[bx] AUXPAGE_BITS = 3 ; writeable, present move_anywhere_vcpi proc near ; input: ; ESI= linear source address ; EDI= linear target address ; CX= size to move, must be <= 4096 !!! ; output: ; all registers preserved ; we are in VM86 and at this point assume to have VCPI push ax lea ax,@@pmove call call_pmode_routine pop ax ret ; this will be executed in 16-bit protected mode @@pmove: @@regs struc pushAD_struc @@ @@regs ends ; save pagetable-entries mov bx,High_Seg shr bx,(12-4-2) push AUXPAGE_S_entry push AUXPAGE_S_entry+4 push AUXPAGE_T_entry push AUXPAGE_T_entry+4 mmm macro reg,st mov eax,[bp].@@e® mov reg,ax and reg,0fffh lea reg,[reg]+AUXPAGE_&st and ax,0f000h or al,AUXPAGE_BITS mov AUXPAGE_&st&_entry,eax add eax,1000h mov AUXPAGE_&st&_entry+4,eax endm mmm si,S ; map source mmm di,T ; map target .386p mov eax,CR3 ; flush TLB mov CR3,eax .386 cld movzx ecx,cx ror ecx,2 rep movsd rol ecx,2 rep movsb ; restore pagetable-entries pop AUXPAGE_T_entry+4 pop AUXPAGE_T_entry pop AUXPAGE_S_entry+4 pop AUXPAGE_S_entry ret move_anywhere_vcpi endp ;--------------------------------------------------------------- move_anywhere_INT15 proc near ; input: ESI = source linear address ; EDI = destination linear address ; CX = count ; returns: ; all registers preserved ; @@descript struc @@limit dw ? @@base0 dw ? @@base16 db ? @@typbyte db ? @@limit16 db ? @@base24 db ? @@descript ends push_ es,eax,esi,edi,ecx mov cs:@@src.@@base0,si shld eax,esi,16 mov cs:@@src.@@base16,al mov cs:@@src.@@base24,ah mov cs:@@dest.@@base0,di shld eax,edi,16 mov cs:@@dest.@@base16,al mov cs:@@dest.@@base24,ah mov cx,(1000h/2) ; force to be one page ; NOTE: INT15 moves are in WORDS, ; because we always move 1000h, ; we simple do it also for the last page push cs pop es lea si,@@gdt mov ax,08700h int 15h pop_ es,eax,esi,edi,ecx ret align qword @@gdt @@descript <0,0,0,0,0,0> @@descript <0,0,0,0,0,0> @@src @@descript <0ffffh,0,0,093h,0,0> @@dest @@descript <0ffffh,0,0,093h,0,0> @@descript <0,0,0,0,0,0> ; BIOS CS @@descript <0,0,0,0,0,0> ; BIOS SS move_anywhere_INT15 endp ;============================================================================ DUMMY_XMS_BLOCK_START = 100000h XMS_GET_VERSION = 0 XMS_QUERY_FREE = 8 XMS_ALLOC = 9 XMS_FREE = 0ah XMS_MOVE_BLOCK = 0bh XMS_LOCK_BLOCK = 0ch XMS_UNLOCK_BLOCK = 0dh xmscall macro function mov ah,function call cs:xms_entry endm check_for_XMS proc near push_ ds,es,ax,bx mov cs:xms_entry,0 mov cs:xms_avail,0 mov ax,4300h INT 2fh cmp al,80h jnz @@ex mov ax,4310h INT 2fh mov word ptr cs:xms_entry,bx mov word ptr cs:xms_entry+2,es xmscall XMS_GET_VERSION cmp ax,200h jb @@ex0 xmscall XMS_QUERY_FREE or ax,ax jz @@ex0 mov cs:xms_avail,ax @@ex: pop_ ds,es,ax,bx cmp cs:xms_entry,0 ret @@ex0: mov cs:xms_entry,0 jmp @@ex check_for_XMS endp allocate_xms_block proc near push_ bx,dx cmp cs:xms_entry,0 je @@ex0 mov dx,cs:xms_avail xmscall XMS_ALLOC or ax,ax je @@ex0 mov cs:xms_handle,dx @@ex: pop_ bx,dx or eax,eax ret @@ex0: xor eax,eax jmp @@ex allocate_xms_block endp lock_xms_block proc near ; try to lock the XMS block ; (needed to get the phys.address) push_ ax,bx,dx mov dx,cs:xms_handle xmscall XMS_LOCK_BLOCK or ax,ax je @@err mov word ptr cs:xms_phys_addr,bx mov word ptr cs:xms_phys_addr+2,dx pop_ ax,bx,dx ret @@err: push cs pop ds lea dx,@@tx call print jmp exit_to_dos @@tx db 13,10,'Cannot lock XMS memory',13,10,'$' lock_xms_block endp free_xms_block proc near push_ ax,bx,dx mov dx,cs:xms_handle xmscall XMS_UNLOCK_BLOCK mov dx, cs:xms_handle xmscall XMS_FREE pop_ ax,bx,dx ret free_xms_block endp move_anywhere_xms proc near ; input: ; ESI= linear source address (must be in lowmem ) ; EDI= linear target address ; CX= size to move, must be <= 4096 !!! (actually _is_ always 4096) ; output: ; all registers preserved cmp edi,DUMMY_XMS_BLOCK_START jb move_simple pushad push ds mov ecx,1000h ; force a length of one page to move ; NOTE: XMS moves have to be even, ; because we alway move 1000h, ; we simple do it also for the last page mov @@length,ecx sub edi,DUMMY_XMS_BLOCK_START mov @@doffs,edi mov di,xms_handle mov @@dhandle,di ror esi,4 mov word ptr @@soffs+2,si xor si,si rol esi,4 mov word ptr @@soffs,si lea si,@@length push cs pop ds xmscall XMS_MOVE_BLOCK pop ds popad ret @@length dd ? @@shandle dw 0 @@soffs dd ? @@dhandle dw ? @@doffs dd ? move_anywhere_xms endp ;-------------------------------------------------------------------- move_simple proc near ; input: ; ESI= linear source address ; EDI= linear target address ; CX= size to move, must be <= 4096 !!! ; output: ; all registers preserved pushad push_ ds,es movzx ecx,cx cld ror edi,4 mov es,di xor di,di rol edi,4 ror esi,4 mov ds,si xor si,si rol esi,4 ror ecx,2 rep movsd rol ecx,2 rep movsb @@ex: pop_ ds,es popad ret move_simple endp build_buffer_heap proc near pushad ; first we setup the normal low heap ; (why not use it) movzx eax,kernel_load_frame shl eax,4 mov heap_ptr,eax movzx eax,High_Seg ; NOTE: this is page aligned ! shl eax,4 mov heap_end,eax sub eax,heap_ptr shr eax,12 mov heap_max_pages,eax ; now we try to get some extended memory cmp need_mapped_put_buffer,0 jz @@err cmp cpu_type,cpu_386V86 je @@vcpi ; we check what we can use to access extended memory call check_for_XMS jz @@need_int15 cmp cs:xms_avail,128 jb @@need_int15 sub cs:xms_avail,64 ; leave a 64K minimum for system call allocate_xms_block jz @@need_int15 movzx eax,cs:xms_avail shr eax,2 ; from 1KB to 4KB add heap_max_pages,eax mov high_heap_ptr,DUMMY_XMS_BLOCK_START mov high_mem_access,USING_XMS mov move_anywhere, offset move_anywhere_xms @@ex: mov eax,heap_max_pages shl eax,12 mov load_buffer_size,eax popad ret @@need_int15: mov ax,08800h int 15h sub ax,64+64 ; leave HMA untouched, we need 64Kminimum ; (else we would waste more mem than winning) jbe @@err movzx eax,ax shr eax,2 ; from 1KB to 4KB add heap_max_pages,eax mov high_heap_ptr,0110000h mov high_mem_access,USING_INT15 mov move_anywhere, offset move_anywhere_int15 jmp @@ex @@err: mov high_mem_access,0 mov move_anywhere, offset move_simple jmp @@ex @@vcpi: cmp have_VCPI,0 jz @@ex push edx mov ax,0DE03h ; get avail 4K VCPI-page int emm_int sub edx,(64/4) ; leave a 64K minimum for system jb @@err add heap_max_pages,edx mov high_mem_access,USING_VCPI mov move_anywhere, offset move_anywhere_vcpi pop edx jmp @@ex build_buffer_heap endp get_buffer_from_heap proc near ; output: EAX = linear address of 4K buffer, page aligned ; (if XMS, then the offset within the buffer) ; CARRY =1, if memory overflow cmp heap_max_pages,0 jna @@err dec heap_max_pages mov eax,heap_ptr cmp eax,heap_end jnb @@high add heap_ptr,1000h @@ex1: clc @@ex: ret @@high: cmp high_mem_access,USING_VCPI ; what heap are we using je @@vcpi mov eax,high_heap_ptr add high_heap_ptr,1000h jmp @@ex1 @@vcpi: push edx mov ax,0DE04h ; get 4K VCPI-page int emm_int mov eax,edx and ax,0f000h pop edx jmp @@ex1 @@err: xor eax,eax stc jmp @@ex get_buffer_from_heap endp free_extended_memory proc near cmp need_mapped_put_buffer,0 jz @@ex cmp high_mem_access,USING_XMS je @@xms cmp high_mem_access,USING_VCPI jne @@ex @@vcpi: push_ eax,bx,edx mov bx,word ptr pageadjlist.ncount shl bx,2 @@loop: sub bx,4 jb @@ex1 mov edx,pageadjlist.sources[bx] cmp edx,0100000h jb @@ex1 mov ax,0DE05h ; free 4K VCPI-page int emm_int jmp @@loop @@ex1: pop_ eax,bx,edx @@ex: ret @@xms: call free_xms_block jmp @@ex free_extended_memory endp final_page_adjust_list_handling proc near cmp need_mapped_put_buffer,0 jz @@ex cmp high_mem_access,USING_XMS jne @@ex push_ eax,bx call lock_xms_block mov bx,word ptr pageadjlist.ncount shl bx,2 @@loop: sub bx,4 jb @@ex1 mov eax,pageadjlist.sources[bx] sub eax,DUMMY_XMS_BLOCK_START jb @@ex1 add eax,xms_phys_addr mov pageadjlist.sources[bx],eax jmp @@loop @@ex1: pop_ eax,bx @@ex: ret final_page_adjust_list_handling endp open_new_mapped_block proc near ; input: ; EDI = linear address of final target address cmp need_mapped_put_buffer,0 jz @@ex push_ bx,edi mov bx,word ptr pageadjlist.number_of_blocks shl bx,3 ; * sizeof(pblock) and di,0f000h mov pageadjlist.blocks.taddr[bx],edi mov di,word ptr pageadjlist.ncount mov pageadjlist.blocks.tstart[bx],di mov pageadjlist.blocks.tcount[bx],0 inc pageadjlist.number_of_blocks mov do_mapped_put_buffer,1 ; notice 'put_buffer' what to do pop_ bx,edi @@ex: ret open_new_mapped_block endp map_high_page proc near ; input: ; EDI = linear address of source address ; because this is called strict sequentially, the target address ; is allways given by the next free entry in the pageadjlist cmp need_mapped_put_buffer,0 jz @@ex push_ bx,edi mov bx,word ptr pageadjlist.ncount shl bx,2 and di,0f000h mov pageadjlist.sources[bx],edi inc pageadjlist.ncount mov bx,word ptr pageadjlist.number_of_blocks dec bx shl bx,3 ; * sizeof(pblock) inc pageadjlist.blocks.tcount[bx] pop_ bx,edi @@ex: ret map_high_page endp put_buffer proc near ; input: ; AX= count ; EDI= linear destination address (may be > 0100000h) ; DS:DX= source address in lowmem ; output: ; none, all registers preserved ; in case of memory overflow it doesn't return (jumps to exit_to_dos) ; pushad push_ ds,es movzx ecx,ax cmp cs:need_mapped_put_buffer,0 jnz @@mapped ; we can mov it normally cld ror edi,4 mov es,di xor di,di rol edi,4 mov si,dx ror ecx,2 rep movsd rol ecx,2 rep movsb @@ex: pop_ ds,es popad ret @@mapped: ; do high move call get_buffer_from_heap jc @@err ; we don't need EDI any more, because ; if we come here we are writing ; strict sequentialy and have set the ; starting address of the block ; via open_new_mapped_block mov edi,eax mov si,ds movzx esi,si shl esi,4 movzx edx,dx add esi,edx call map_high_page call move_anywhere jmp @@ex @@err: pop_ ds,es popad DosCall DOS_CLOSE_FILE lea dx,@@tx call print jmp exit_to_dos @@tx db 13,10,'Out of memory (may be low or extended)',13,10,'$' put_buffer endp ; --------------------------------------------------------------- ; initrd stuff MAXPHYSMEM_FOR_INT15 = 1000000h load_initrd proc near cmp cs:option_initrd,0 jz @@ex pushad cmp need_mapped_put_buffer,0 jz @@err_unable call get_effective_physmem mov end_of_physmem,eax cmp high_mem_access,USING_INT15 jne @@1 ; INT15 on some BIOSes will not access above 16Mb cmp eax,MAXPHYSMEM_FOR_INT15 jbe @@1 mov eax,MAXPHYSMEM_FOR_INT15 @@1: mov @@end_of_physmem,eax mov ax,DOS_OPEN_FILE shl 8 lea dx,rdimage_name DosInt jc @@err_open mov fhandle,ax mov bx,ax call get_filesize mov ramdisk_size,eax neg eax add eax,@@end_of_physmem and ax,0f000h ; round down to full page boundary mov ramdisk_image,eax movzx eax,kernel_size add ax,0ffh mov al,0 ; round up to page boundary shl eax,4 ; size of compressed kernel mov ecx,eax shl eax,1 ; estimated size of decompressed kernel ; (we assume a decompression rate of 1:2) sub ecx,(90000h - 2000h) ; the low buffer size (for bzimages) jb @@3 add eax,ecx ; add padding @@3: add eax,(100000h+2000h) ; (+ gunzip-heap), now we have end of kernel cmp eax,ramdisk_image jnb @@err_mem ; ok we have place ; now loading the kernel ; NOTE: needing EDI for open_new_mapped_block mov edi,ramdisk_image call open_new_mapped_block ; open the first block mov bx,fhandle mov ecx,ramdisk_size mov print_dots,3 call read_handle ; read the ramdisk call print_crlf mov print_dots,0 call print_crlf jc @@err_io cmp eax,ecx jnz @@err_io ; ok, all is read into memory DosCall DOS_CLOSE_FILE popad @@ex: ret @@err: popad mov dx,@@errtx_addr call print jmp exit_to_dos @@err_open: mov @@errtx_addr,offset @@tx jmp @@err @@err_mem: mov @@errtx_addr,offset @@txmem jmp @@err @@err_io: mov @@errtx_addr,offset @@txio jmp @@err @@err_unable: mov @@errtx_addr,offset @@txno jmp @@err @@end_of_physmem dd 0 @@tx db 13,10,"can't open image file for initrd",13,10,'$' @@txmem db 13,10,"no place after kernel for initrd",13,10,'$' @@txio db 13,10,"IO-error while reading initrd",13,10,'$' @@txno db 13,10,"no support in setup for reading initrd",13,10,'$' @@errtx_addr dw 0 load_initrd endp get_filesize proc near ; input: ; bx = filehandle ; output: ; eax = filesize, all other registers reserved push_ cx,dx,esi mov ax,4201h ; seek to current position xor cx,cx xor dx,dx DosInt ; ... and return current position in DX:AX push_ ax,dx ; save it mov ax,4202h ; seek to last position xor cx,cx xor dx,dx DosInt ; ... and return EOF position in DX:AX mov si,dx shl esi,16 mov si,ax pop_ dx,cx mov ax,4200h ; seek to saved position DosInt mov eax,esi pop_ cx,dx,esi ret get_filesize endp