⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 init32.asm

📁 Windows上的精简Linux系统
💻 ASM
字号:
;; -*- fundamental -*-;; $Id: init32.asm,v 1.2 2003/07/11 00:02:30 hpa Exp $;; -----------------------------------------------------------------------;;   ;;   Copyright 1994-2003 H. Peter Anvin - All Rights Reserved;;;;   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, Inc., 53 Temple Place Ste 330,;;   Bostom MA 02111-1307, USA; either version 2 of the License, or;;   (at your option) any later version; incorporated herein by reference.;;;; -----------------------------------------------------------------------;;;; init32.asm;; ;; Routine to trampoline into 32-bit protected memory.  This code is;; derived from bcopy32.inc and com32.inc in the main SYSLINUX distribution.;;%define MY_CS 0x0800%define CS_BASE (MY_CS << 4); Low memory bounce buffer%define BOUNCE_SEG	(MY_CS+0x1000)%define DO_WBINVD 0%define STACK_HEAP_SIZE	(128*1024)		section .rodata align=16		section .data   align=16		section .bss    align=16		global init32		;; We enter protected mode, set up a flat 32-bit environment, run rep movsd; and then exit.  IMPORTANT: This code assumes cs == MY_CS.;; This code is probably excessively anal-retentive in its handling of; segments, but this stuff is painful enough as it is without having to rely; on everything happening "as it ought to.";		section .rodata	; desc base, limit, flags%macro	desc 3	dd (%2 & 0xffff) | ((%1 & 0xffff) << 16)	dd (%1 & 0xff000000) | (%2 & 0xf0000) | ((%3 & 0xf0ff) << 8) | ((%1 & 0x00ff0000) >> 16)%endmacro			align 4call32_gdt:	dw call32_gdt_size-1	; Null descriptor - contains GDT.adj1:		dd call32_gdt+CS_BASE	; pointer for LGDT instruction		dw 0				; 0008: Code segment, use16, readable, dpl 0, base CS_BASE, 64K		desc CS_BASE, 0xffff, 0x009b		; 0010: Data segment, use16, read/write, dpl 0, base CS_BASE, 64K		desc CS_BASE, 0xffff, 0x0093		; 0018: Data segment, use16, read/write, dpl 0, base 0, 4G		desc 0, 0xfffff, 0x809b		; 0020: Code segment, use32, read/write, dpl 0, base 0, 4G		desc 0, 0xfffff, 0xc09b		; 0028: Data segment, use32, read/write, dpl 0, base 0, 4G		desc 0, 0xfffff, 0xc093	call32_gdt_size:	equ $-call32_gdterr_a20:	db 'ERROR: A20 gate not responding!',13,10,0			bits 16		section .bss		alignb 4SavedSSSP	resd 1			; Place to save SS:SPReturn		resd 1			; Return valueA20Test		resw 1			; Space to test A20A20Tries	resb 1				section .data		alignb 4Target		dd 0			; Target addressTarget_Seg	dw 20h			; Target CSA20Type		dw 0			; Default = unknown				section .text;; Routines to enable and disable (yuck) A20.  These routines are gathered; from tips from a couple of sources, including the Linux kernel and; http://www.x86.org/.  The need for the delay to be as large as given here; is indicated by Donnie Barnes of RedHat, the problematic system being an; IBM ThinkPad 760EL.;; We typically toggle A20 twice for every 64K transferred.; %define	io_delay	call _io_delay%define IO_DELAY_PORT	80h		; Invalid port (we hope!)%define disable_wait 	32		; How long to wait for a disable%define A20_DUNNO	0		; A20 type unknown%define A20_NONE	1		; A20 always on?%define A20_BIOS	2		; A20 BIOS enable%define A20_KBC		3		; A20 through KBC%define A20_FAST	4		; A20 through port 92h		align 2A20List		dw a20_dunno, a20_none, a20_bios, a20_kbc, a20_fastA20DList	dw a20d_dunno, a20d_none, a20d_bios, a20d_kbc, a20d_fasta20_adjust_cnt	equ ($-A20List)/2slow_out:	out dx, al		; Fall through_io_delay:	out IO_DELAY_PORT,al		out IO_DELAY_PORT,al		retenable_a20:		pushad		mov byte [A20Tries],255 ; Times to try to make this worktry_enable_a20:;; Flush the caches;%if DO_WBINVD		call try_wbinvd%endif;; If the A20 type is known, jump straight to type;		mov bp,[A20Type]		add bp,bp			; Convert to word offset.adj4:		jmp word [bp+A20List];; First, see if we are on a system with no A20 gate;a20_dunno:a20_none:		mov byte [A20Type], A20_NONE		call a20_test		jnz a20_done;; Next, try the BIOS (INT 15h AX=2401h);a20_bios:		mov byte [A20Type], A20_BIOS		mov ax,2401h		pushf				; Some BIOSes muck with IF		int 15h		popf		call a20_test		jnz a20_done;; Enable the keyboard controller A20 gate;a20_kbc:		mov dl, 1			; Allow early exit		call empty_8042		jnz a20_done			; A20 live, no need to use KBC		mov byte [A20Type], A20_KBC	; Starting KBC command sequence		mov al,0D1h			; Command write		out 064h, al		call empty_8042_uncond		mov al,0DFh			; A20 on		out 060h, al		call empty_8042_uncond		; Verify that A20 actually is enabled.  Do that by		; observing a word in low memory and the same word in		; the HMA until they are no longer coherent.  Note that		; we don't do the same check in the disable case, because		; we don't want to *require* A20 masking (SYSLINUX should		; work fine without it, if the BIOS does.).kbc_wait:	push cx		xor cx,cx.kbc_wait_loop:		call a20_test		jnz a20_done_pop		loop .kbc_wait_loop		pop cx;; Running out of options here.  Final attempt: enable the "fast A20 gate";a20_fast:		mov byte [A20Type], A20_FAST	; Haven't used the KBC yet		in al, 092h		or al,02h		and al,~01h			; Don't accidentally reset the machine!		out 092h, al.fast_wait:	push cx		xor cx,cx.fast_wait_loop:		call a20_test		jnz a20_done_pop		loop .fast_wait_loop		pop cx;; Oh bugger.  A20 is not responding.  Try frobbing it again; eventually give up; and report failure to the user.;		dec byte [A20Tries]		jnz try_enable_a20		; Error message time		mov si,err_a20print_err:		lodsb		and al,al		jz die		mov bx,7		mov ah,0xe		int 10h		jmp print_errdie:		sti.hlt:		hlt		jmp short .hlt;; A20 unmasked, proceed...;a20_done_pop:	pop cxa20_done:	popad		ret;; This routine tests if A20 is enabled (ZF = 0).  This routine; must not destroy any register contents.;a20_test:		push es		push cx		push ax		mov cx,0FFFFh		; HMA = segment 0FFFFh		mov es,cx		mov cx,32		; Loop count		mov ax,[A20Test].a20_wait:	inc ax		mov [A20Test],ax		io_delay		; Serialize, and fix delay		cmp ax,[es:A20Test+CS_BASE+10h]		loopz .a20_wait.a20_done:	pop ax		pop cx		pop es		retdisable_a20:		pushad;; Flush the caches;%if DO_WBINVD		call try_wbinvd%endif		mov bp,[A20Type]		add bp,bp			; Convert to word offset.adj5:		jmp word [bp+A20DList]a20d_bios:		mov ax,2400h		pushf				; Some BIOSes muck with IF		int 15h		popf		jmp short a20d_snooze;; Disable the "fast A20 gate";a20d_fast:		in al, 092h		and al,~03h		out 092h, al		jmp short a20d_snooze;; Disable the keyboard controller A20 gate;a20d_kbc:		call empty_8042_uncond		mov al,0D1h		out 064h, al		; Command write		call empty_8042_uncond		mov al,0DDh		; A20 off		out 060h, al		call empty_8042_uncond		; Wait a bit for it to take effecta20d_snooze:		push cx		mov cx, disable_wait.delayloop:	call a20_test		jz .disabled		loop .delayloop.disabled:	pop cxa20d_dunno:a20d_none:		popad		ret;; Routine to empty the 8042 KBC controller.  If dl != 0; then we will test A20 in the loop and exit if A20 is; suddenly enabled.;empty_8042_uncond:		xor dl,dlempty_8042:		call a20_test		jz .a20_on		and dl,dl		jnz .done.a20_on:	io_delay		in al, 064h		; Status port		test al,1		jz .no_output		io_delay		in al, 060h		; Read input		jmp short empty_8042.no_output:		test al,2		jnz empty_8042		io_delay.done:		ret	;; Execute a WBINVD instruction if possible on this CPU;%if DO_WBINVDtry_wbinvd:		wbinvd		ret%endif		section .bss		alignb 4PMESP		resd 1			; Protected mode %esp		section .idt nobits alloc exec write align=4096		alignb 4096pm_idt		resb 4096		; Protected-mode IDT, followed by interrupt stubspm_entry:	equ 0x100000		section .rodata		align 4, db 0call32_pmidt:		dw 8*256		; Limit		dd pm_idt+CS_BASE	; Addresscall32_rmidt:		dw 0ffffh		; Limit		dd 0			; Address		section .text;; This is the main entrypoint in this function;init32:		mov ebx,call32_call_start+CS_BASE	; Where to go in PMcall32_enter_pm:		mov ax,cs		mov ds,ax		cli		mov [SavedSSSP],sp		mov [SavedSSSP+2],ss		cld		call a20_test		jnz .a20ok		call enable_a20.a20ok:		lgdt [call32_gdt]	; Set up GDT		lidt [call32_pmidt]	; Set up the IDT		mov eax,cr0		or al,1		mov cr0,eax		; Enter protected mode		jmp 20h:dword .in_pm+CS_BASE				bits 32.in_pm:		xor eax,eax		; Available for future use...		mov fs,eax		mov gs,eax		mov al,28h		; Set up data segments		mov es,eax		mov ds,eax		mov ss,eax		mov esp,[PMESP+CS_BASE]	; Load protmode %esp if available		jmp ebx			; Go to where we need to go;; This is invoked before first dispatch of the 32-bit code, in 32-bit mode;call32_call_start:		;		; Point the stack into low memory		; We have: this segment, bounce buffer, then stack+heap		;		mov esp, CS_BASE + 0x20000 + STACK_HEAP_SIZE		and esp, ~0xf		;		; Set up the protmode IDT and the interrupt jump buffers		;		mov edi,pm_idt+CS_BASE		; Form an interrupt gate descriptor		; WARNING: This is broken if pm_idt crosses a 64K boundary;		; however, it can't because of the alignment constraints.		mov ebx,pm_idt+CS_BASE+8*256		mov eax,0x0020ee00		xchg ax,bx		xor ecx,ecx		inc ch				; ecx <- 256		push ecx.make_idt:		stosd		add eax,8		xchg eax,ebx		stosd		xchg eax,ebx		loop .make_idt		pop ecx		; Each entry in the interrupt jump buffer contains		; the following instructions:		;		; 00000000 60                pushad		; 00000001 B0xx              mov al,<interrupt#>		; 00000003 E9xxxxxxxx        jmp call32_handle_interrupt		mov eax,0xe900b060		mov ebx,call32_handle_interrupt+CS_BASE		sub ebx,edi.make_ijb:		stosd		sub [edi-2],cl			; Interrupt #		xchg eax,ebx		sub eax,8		stosd		xchg eax,ebx		loop .make_ijb		; Now everything is set up for interrupts...		push dword (BOUNCE_SEG << 4)	; Bounce buffer address		push dword call32_syscall+CS_BASE ; Syscall entry point		sti				; Interrupts OK now		call pm_entry-CS_BASE		; Run the program...		; ... on return ...		mov [Return+CS_BASE],eax		; ... fall through to call32_exit ...call32_exit:		mov bx,call32_done	; Return to command loopcall32_enter_rm:		cli		cld		mov [PMESP+CS_BASE],esp	; Save exit %esp		xor esp,esp		; Make sure the high bits are zero		jmp 08h:.in_pm16	; Return to 16-bit mode first		bits 16.in_pm16:		mov ax,10h		; Real-mode-like segment		mov es,ax		mov ds,ax		mov ss,ax		mov fs,ax		mov gs,ax		lidt [call32_rmidt]	; Real-mode IDT (rm needs no GDT)		mov eax,cr0		and al,~1		mov cr0,eax		jmp MY_CS:.in_rm.in_rm:					; Back in real mode		mov ax,cs		; Set up sane segments		mov ds,ax		mov es,ax		mov fs,ax		mov gs,ax		lss sp,[SavedSSSP]	; Restore stack		jmp bx			; Go to whereever we need to go...call32_done:		call disable_a20		sti		mov ax,[Return]		ret;; 16-bit support code;		bits 16;; 16-bit interrupt-handling code;call32_int_rm:		pushf				; Flags on stack		push cs				; Return segment		push word .cont			; Return address		push dword edx			; Segment:offset of IVT entry		retf				; Invoke IVT routine.cont:		; ... on resume ...		mov ebx,call32_int_resume+CS_BASE		jmp call32_enter_pm		; Go back to PM;; 16-bit system call handling code;call32_sys_rm:		pop gs		pop fs		pop es		pop ds		popad		popfd		retf				; Invoke routine.return:		pushfd		pushad		push ds		push es		push fs		push gs		mov ebx,call32_sys_resume+CS_BASE		jmp call32_enter_pm;; 32-bit support code;		bits 32;; This is invoked on getting an interrupt in protected mode.  At; this point, we need to context-switch to real mode and invoke; the interrupt routine.;; When this gets invoked, the registers are saved on the stack and; AL contains the register number.;call32_handle_interrupt:		movzx eax,al		xor ebx,ebx		; Actually makes the code smaller		mov edx,[ebx+eax*4]	; Get the segment:offset of the routine		mov bx,call32_int_rm		jmp call32_enter_rm	; Go to real modecall32_int_resume:		popad		iret;; Syscall invocation.  We manifest a structure on the real-mode stack,; containing the call32sys_t structure from <call32.h> as well as; the following entries (from low to high address):; - Target offset; - Target segment; - Return offset; - Return segment (== real mode cs); - Return flags;call32_syscall:		pushfd			; Save IF among other things...		pushad			; We only need to save some, but...		cld		movzx edi,word [SavedSSSP+CS_BASE]		movzx eax,word [SavedSSSP+CS_BASE+2]		sub edi,54		; Allocate 54 bytes		mov [SavedSSSP+CS_BASE],di		shl eax,4		add edi,eax		; Create linear address		mov esi,[esp+11*4]	; Source regs		xor ecx,ecx		mov cl,11		; 44 bytes to copy		rep movsd		movzx eax,byte [esp+10*4] ; Interrupt number		; ecx == 0 here; adding it to the EA makes the		; encoding smaller		mov eax,[ecx+eax*4]	; Get IVT entry		stosd			; Save in stack frame		mov eax,call32_sys_rm.return + (MY_CS << 16) ; Return seg:offs		stosd			; Save in stack frame		mov eax,[edi-12]	; Return flags		and eax,0x200cd7	; Mask (potentially) unsafe flags		mov [edi-12],eax	; Primary flags entry		stosw			; Return flags		mov bx,call32_sys_rm		jmp call32_enter_rm	; Go to real mode		; On return, the 44-byte return structure is on the		; real-mode stack.call32_sys_resume:		movzx esi,word [SavedSSSP+CS_BASE]		movzx eax,word [SavedSSSP+CS_BASE+2]		mov edi,[esp+12*4]	; Dest regs		shl eax,4		add esi,eax		; Create linear address		and edi,edi		; NULL pointer?		jnz .do_copy.no_copy:	mov edi,esi		; Do a dummy copy-to-self.do_copy:	xor ecx,ecx		mov cl,11		; 44 bytes		rep movsd		; Copy register block		add dword [SavedSSSP+CS_BASE],44	; Remove from stack		popad		popfd		ret			; Return to 32-bit program

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -