;//--------------------------------------------------------------- ;// ondemand.s (a revision of our 'tryexec.s' demo) ;// ;// This example shows how to modify our 'tryexec.s' program ;// so as to incorporate 'demand-loading' of the '.text' and ;// '.data' program-segments in an exectable ELF file-image. ;// ;// Key changes are: ;// (1) remove instruction-loops from 'load_and_exec_image' ;// that perform memory-to-memory copying to initialize ;// the .text and .data program-segments ;// (2) mark their LDT segment-descriptors as 'not present' ;// (3) setup a 'memmap' data-structure which describes the ;// memory-to-memory copying needed to initialize these ;// program-segments when they are first accessed ;// (4) add an interrupt-gate and exception-handler for the ;// segment-not-present fault which utilizes the memmap ;// data-structure to implement demand-loading. ;// ;// (5) mark the LDT descriptor for the stack 'not present' ;// (6) add an interrupt-gate and exception-handler for the ;// stack-fault exception (can use the same handler) ;// ;// You can place this 'second-stage loader' program on our ;// boot-disk using these commands: ;// ;// assemble with: $ as86 ondemand.s -b ondemand.b ;// install using: $ dd if=ondemand.b of=/dev/fd0 seek=1 ;// ;// NOTE: This code begins executing with CS:IP = 1000:0002. ;// ;// programmer: ALLAN CRUSE ;// written on: 30 MAR 2004 ;// bug repair: 10 MAY 2004 -- to preserve EBX in subroutine ;//--------------------------------------------------------------- ;----------------------------------------------------------------- .WORD 0xABCD ; programming signature ;----------------------------------------------------------------- main: mov ax, cs ; address this segment mov ds, ax ; with DS register pop dword return_address ; store return-address mov ss, ax ; adjust SS register lea esp, tos0 ; and set new stacktop call initialize_os_tables call enter_protected_mode call execute_program_demo call leave_protected_mode push dword return_address ; recover our exit-address retf ; exit to program launcher ;----------------------------------------------------------------- return_address: .LONG 0 ; to store exit-address ;----------------------------------------------------------------- sys_call_table: ; the jump-table for our system-call dispatcher .LONG do_nothing ; for sustem-call 0 .LONG do_exit ; for system-call 1 .LONG do_nothing ; for system-call 2 .LONG do_nothing ; for system-call 3 .LONG do_write ; for system-call 4 NR_SYSTEM_CALLS EQU (* - sys_call_table)/4 ; number of entries ;----------------------------------------------------------------- ;----------------------------------------------------------------- ; EQUATES realCS EQU 0x1000 ; segment-address of code limGDT EQU 0x0047 ; allocates 9 descriptors sel_es EQU 0x0008 ; vram-segment selector sel_cs EQU 0x0010 ; code-segment selector sel_ss EQU 0x0018 ; data-segment selector sel_CS EQU 0x0020 ; code-segment selector sel_SS EQU 0x0028 ; data-segment selector sel_ts EQU 0x0030 ; TSS-segment selector sel_ls EQU 0x0038 ; LDT-segment selector sel_bs EQU 0x0040 ; bios-segment selector sel_fs EQU 0x0048 ; flat-segment selector userCS EQU 0x0007 ; code-segment selector userDS EQU 0x000F ; data-segment selector userSS EQU 0x0017 ; stak-segment selector ;----------------------------------------------------------------- .ALIGN 8 ;----------------------------------------------------------------- theGDT: .WORD 0x0000, 0x0000, 0x0000, 0x0000 ; null descriptor .WORD 0x7FFF, 0x8000, 0x920B, 0x0000 ; vram descriptor .WORD 0xFFFF, 0x0000, 0x9A01, 0x0000 ; code descriptor .WORD 0xFFFF, 0x0000, 0x9201, 0x0000 ; data descriptor .WORD 0x17FF, 0x0000, 0x9A01, 0x0040 ; code descriptor .WORD 0xFFFF, 0x0000, 0x9200, 0x00CF ; data descriptor .WORD 0x000B, theTSS, 0x8901, 0x0000 ; TSS descriptor .WORD 0x0017, theLDT, 0x8201, 0x0000 ; LDT descriptor .WORD 0x0100, 0x0400, 0x9200, 0x0000 ; bios descriptor ;----------------------------------------------------------------- theLDT: .WORD 0xFFFF, 0x0000, 0x7A00, 0x00CF ; code present=no .WORD 0xFFFF, 0x0000, 0x7200, 0x00CF ; data present=no .WORD 0xFFFF, 0x0000, 0x7200, 0x00CF ; stak present=ok ;----------------------------------------------------------------- theIDT: .SPACE 2048 ; for 256 gate-descriptors ;----------------------------------------------------------------- theTSS: .LONG 0, 0x00020000, sel_SS ; 32bit Task-State Segment ;----------------------------------------------------------------- memmap: ; from to size type .LONG 0x11800, 0x08048000, 0xA00, 0xFA .LONG 0x11800, 0x08049000, 0xA00, 0xF2 .LONG 0x11800, 0x08048000, 0x000, 0xF2 ;----------------------------------------------------------------- initialize_os_tables: ; initialize IDT descriptor for gate 0x80 mov edi, #0x80 ; ID-number for the gate lea di, theIDT[edi*8] ; gate's offset-address mov 0[di], #isrSVC ; entry-point's loword mov 2[di], #sel_CS ; 32bit code-selector mov 4[di], #0xEE00 ; 32bit interrupt-gate mov 6[di], #0x0000 ; entry-point's hiword ; initialize IDT descriptor for gate 0x0B mov edi, #0x0B ; ID-number for the gate lea di, theIDT[edi*8] ; gate's offset-address mov 0[di], #isrSNP ; entry-point's loword mov 2[di], #sel_CS ; 32bit code-selector mov 4[di], #0x8E00 ; 32bit interrupt-gate mov 6[di], #0x0000 ; entry-point's hiword ; initialize IDT descriptor for gate 0x0D mov edi, #0x0D ; ID-number for the gate lea di, theIDT[edi*8] ; gate's offset-address mov 0[di], #isrGPF ; entry-point's loword mov 2[di], #sel_CS ; 32bit code-selector mov 4[di], #0x8E00 ; 32bit interrupt-gate mov 6[di], #0x0000 ; entry-point's hiword ; initialize IDT descriptor for gate 0x08 mov edi, #0x08 ; ID-number for the gate lea di, theIDT[edi*8] ; gate's offset-address mov 0[di], #isrGPF ; entry-point's loword mov 2[di], #sel_CS ; 32bit code-selector mov 4[di], #0x8E00 ; 32bit interrupt-gate mov 6[di], #0x0000 ; entry-point's hiword ; initialize IDT descriptor for gate 0x0C mov edi, #0x0C ; ID-number for the gate lea di, theIDT[edi*8] ; gate's offset-address mov 0[di], #isrSNP ; entry-point's loword mov 2[di], #sel_CS ; 32bit code-selector mov 4[di], #0x8E00 ; 32bit interrupt-gate mov 6[di], #0x0000 ; entry-point's hiword ret ;----------------------------------------------------------------- ;----------------------------------------------------------------- enter_protected_mode: cli ; no device interrupts mov eax, cr0 ; get machine status bts eax, #0 ; set PE-bit to 1 mov cr0, eax ; enable protection push #0x0001 ; push base-address hiword push #theGDT ; push base-address loword push #limGDT ; push GDT's segment-limit lgdt [esp] ; load GDTR register-image add sp, #6 ; discard three stackwords push #0x0001 ; push base-address hiword push #theIDT ; push base-address loword push #0x07FF ; push IDT's segment-limit lidt [esp] ; load IDTR register-image add sp, #6 ; discard three stackwords jmpf #pm, #sel_cs ; reload register CS pm: mov ax, #sel_ss mov ss, ax ; reload register SS mov ds, ax ; reload register DS xor ax, ax ; use 'null' selector mov es, ax ; to purge invalid ES mov fs, ax ; to purge invalid FS mov gs, ax ; to purge invalid GS ret ; back to main routine ;----------------------------------------------------------------- leave_protected_mode: mov ax, ss ; address 64KB r/w segment mov ds, ax ; using DS register mov es, ax ; and ES register mov eax, cr0 ; get machine status btr eax, #0 ; reset PE-bit to 0 mov cr0, eax ; disable protection push #0x0000 ; push base-address hiword push #0x0000 ; push base-address loword push #0x03FF ; push IDT's segment-limit lidt [esp] ; load IDTR register-image add sp, #6 ; discard three stackwords jmpf #rm, #realCS ; reload register CS rm: mov ax, cs mov ss, ax ; reload register SS mov ds, ax ; reload register DS sti ; interrupts allowed ret ; back to main routine ;----------------------------------------------------------------- ;----------------------------------------------------------------- ; NOTE below that we preserve and restore a 48-bit stack-pointer. tossave: .WORD 0, 0, 0 ; save-area for stackptr ;----------------------------------------------------------------- execute_program_demo: ; save our 16bit stack's address (for return to 'main') mov tossave+0, esp ; preserve 32-bit offset mov tossave+4, ss ; plus 16-bit selector ; transfer to second-stage loader in 32-bit code-segment jmpf #load_and_exec_image, #sel_CS ; to 32-bit code ;----------------------------------------------------------------- finish_up_main_thread: ; here we restore our 16bit stack and return to 'main' CSEG ; reference this segment lss esp, tossave ; to reload SS and ESP ret ; back to 'main' routine ;----------------------------------------------------------------- ;================================================================= ;======= END OF INSTRUCTIONS FOR THE 16-BIT CODE-SEGMENT ======= ;================================================================= ;----------------------------------------------------------------- USE32 ; assemble instructions for a 32-bit code-segment ;----------------------------------------------------------------- load_and_exec_image: ; setup register TR (for ring-transitions) mov ax, #sel_ts ; selector for Task-State ltr ax ; loaded into TR register ; setup register LDTR (for ring3 segment-descriptrs) mov ax, #sel_ls ; selector for task's LDT lldt ax ; loaded in LDTR register ; initialize DS and ES for accessing ring3 data mov ax, #userDS ; address application data mov ds, ax ; with the DS register mov es, ax ; also the ES register cld ; use forward processing ; copy executable-image from 0x00011800 to 0x08049000 ; mov esi, #0x00011800 ; point DS:ESI to source ; mov edi, #0x08049000 ; point ES:EDI to dest'n ; mov ecx, #0x00000A00 ; copy 5 disk-sectors ; rep ; movsb ; copy executable-image from 0x00011800 to 0x08048000 ; mov esi, #0x00011800 ; point DS:ESI to source ; mov edi, #0x08048000 ; point ES:EDI to dest'n ; mov ecx, #0x00000A00 ; copy 5 disk-sectors ; rep ; movsb ; setup our stack for the transition to ring3 push dword #userSS ; ring3 stack-selector push dword #0x08048000 ; initial top-of-stack push dword #userCS ; ring3 code-selector push dword #0x08048074 ; initial entry-point retf ; to the ring3 program ;----------------------------------------------------------------- isrSVC: ; entry-point for any SuperVisor-Call cmp eax, #NR_SYSTEM_CALLS ; is ID-number valid? jb isok ; yes, keep ID-number xor eax, eax ; else wipe ID-number isok: CSEG ; address this segment jmp dword sys_call_table[eax*4] ; for SVC routine ;----------------------------------------------------------------- do_nothing: ; this routine is for any unimplemented system-calls mov eax, #-1 ; setup error-code in EAX iretd ; resume the calling task ;----------------------------------------------------------------- do_exit: ; here we transfer control back to our USE16 segment jmpf #finish_up_main_thread, #sel_cs ; to 16bit code ;----------------------------------------------------------------- ;----------------------------------------------------------------- do_write: ; ; Expects: EBX = device ID-number ; ECX = offset of message ; EDX = length of message ; push ebp ; preserve frame-pointer mov ebp, esp ; address stack elements pushad ; preserve cpu registers push ds push es ; check for valid device ID-number cmp ebx, #1 ; is STDOUT device? je wrok ; yes, can do write mov dword [ebp-4], #-1 ; else error-code in EAX jmp wrxx ; and bypass all writing wrok: ; fetch and process the ascii-codes mov ax, #sel_es ; address video memory mov es, ax ; with ES register cld ; do forward processing mov esi, [ebp-8] ; buffer-offset in ESI mov ecx, [ebp-12] ; buffer-length in ECX .L2: lodsb ; fetch next character call write_ascii_tty ; write that character loop .L2 ; return-value in EAX is number of characters written mov eax, [ebp-12] ; use character-count mov [ebp-4], eax ; as the return-value ; move CRT cursor to screen-location following message call sync_crt_cursor ; update hardware cursor wrxx: pop es ; restore saved registers pop ds popad mov esp, ebp ; restore frame-pointer pop ebp iretd ; return from system-call ;----------------------------------------------------------------- get_cursor_locn: ; returns cursor's (row,col) in (DH,DL) push ax push ds mov ax, #sel_bs ; address ROM-BIOS DATA mov ds, ax ; using DS register mov bl, [0x62] ; current video page movzx ebx, bl ; extended to dword mov dx, 0x50[ebx*2] ; get page's cursor pop ds pop ax ret ;----------------------------------------------------------------- ;----------------------------------------------------------------- set_cursor_locn: ; sets cursor's (row,col) from (DH,DL) push ax push ds mov ax, #sel_bs ; address ROM-BIOS DATA mov ds, ax ; using DS register mov bl, [0x62] ; current video page movzx ebx, bl ; extended to dword mov 0x50[ebx*2], dx ; set page's cursor pop ds pop ax ret ;----------------------------------------------------------------- scroll_vpage_up: ; expects video page-number in EBX pushad push ds push es ; copy rows 1 thru 24 onto rows 0 thru 23, respectively mov ax, #sel_es ; address video memory mov ds, ax ; with DS register mov es, ax ; also ES register cld imul edi, ebx, #4096 ; offset to page-origin lea esi, 160[edi] ; offset to row beneath mov ecx, #1920 ; 24 rows, 80 cells each rep movsw ; slide rows 1-24 upward ; then erase row 24 (by filling it with blank characters) mov ax, #0x0720 ; blank w/normal colors mov ecx, #80 ; one row, 80 cells rep stosw ; overwrite bottom row pop es pop ds popad ret ;----------------------------------------------------------------- compute_vram_offset: push ebx ;<-- added 5/10/2004 imul edi, ebx, #4096 ; EDI = offset to page movzx ebx, dh ; extended row-number imul ebx, #160 ; times row-length add edi, ebx ; added to page-origin movzx ebx, dl ; extended col-number imul ebx, #2 ; times col-width add edi, ebx ; added to row-origin pop ebx ;<-- added 5/10/2004 ret ;----------------------------------------------------------------- ;----------------------------------------------------------------- ASCII_BACKSPACE EQU 8 ; backspace (0x08) ASCII_CARR_RETN EQU 13 ; carriage-return (0x0D) ASCII_LINE_FEED EQU 10 ; line-feed (0x0A) ;----------------------------------------------------------------- write_ascii_tty: ; writes char from AL to cursor location call get_cursor_locn ; DH=row DL=col EBX=page ; certain ASCII control-codes receive special handling cmp al, #ASCII_CARR_RETN ; is carriage-return? je do_cr ; yes, move the cursor cmp al, #ASCII_LINE_FEED ; is line-feed? je do_lf ; yes, move the cursor cmp al, #ASCII_BACKSPACE ; is backspace? je do_bs ; yes, move the cursor ; otherwise write character and attribute to the screen call compute_vram_offset ; where to put character mov ah, #0x07 ; use normal attribute stosw ; write char/attribute ; then adjust the cursor-coordinates (row,col) in (DH,DL) inc dl ; increment column-number cmp dl, #80 ; end-of-row was reached? jb ttyxx ; no, keep column-number xor dl, dl ; else do carriage-return jmp do_lf ; followed by line-feed do_bs: or dl, dl ; column-number is zero? jz ttyxx ; yes, perform no action dec dl ; else preceeding column call compute_vram_offset ; is located on screen mov ax, #0x0720 ; and blank character stosw ; overwrites the cell jmp ttyxx ; keep that column-number do_cr: xor dl, dl ; move cursor to column 0 jmp ttyxx ; skip to rom-bios update do_lf: inc dh ; move cursor to next row xor dl, dl ; move cursor to column 0 cmp dh, #25 ; beyond bottom-of-screen? jb ttyxx ; no, keep the row-number dec dh ; else reduce row-number call scroll_vpage_up ; and scroll screen upward ttyxx: call set_cursor_locn ; update ROM-BIOS info ret ;----------------------------------------------------------------- ;----------------------------------------------------------------- sync_crt_cursor: push ax push ds mov ax, #sel_bs ; address ROM-BIOS DATA mov ds, ax ; using DS register mov bl, [0x62] ; current video page movzx ebx, bl ; extended to dword mov dx, 0x50[ebx*2] ; get page's cursor ; update hardware cursor imul bx, #2048 mov al, #80 mul dh add al, dl adc ah, #0 add bx, ax mov dx, #0x3D4 mov al, #0x0E mov ah, bh out dx, ax mov al, #0x0F mov ah, bl out dx, ax pop ds pop ax ret ;================================================================= ;======= Fault-Handler for General Protection Exceptions ======= ;================================================================= isrGPF: ; we kept this handler as it helped us during debugging pushad push dword #0 mov [esp], ds push dword #0 mov [esp], es mov ax, #sel_es ; address video memory mov es, ax ; with ES register mov edi, #80 ; offset for stackdump mov ebp, esp ; point to top element mov ecx, #14 ; setup element count .L1: mov eax, [ebp] ; get the next element call eax2hex ; display it on screen add edi, #160 ; advance to next line add ebp, #4 ; point to next element loop .L1 ; print another element br do_exit ; bail out of this demo ;----------------------------------------------------------------- ;----------------------------------------------------------------- eax2hex: ; draws register EAX in hex-format onto screen at ES:EDI pushad mov edx, eax ; transfer data to EDX cld ; do forward processing mov ax, #0x3020 ; prepend a blank space stosw ; before register value mov ecx, #8 ; setup count of nybbles .L0: rol edx, #4 ; next nybble into DL mov al, dl ; copy nybble to AL and al, #0x0F ; isolate nybble's bits cmp al, #10 ; -- Lopez algorithm -- sbb al, #0x69 ; -- converts binary -- das ; -- to hex numeral -- stosw ; store char and colors loop .L0 ; process other nybbles mov ax, #0x3020 ; append a blank space stosw ; after register value popad ret ;----------------------------------------------------------------- ;================================================================= ;======= Fault-Handler for Segment-Not-Present Exception ======= ;================================================================= isrSNP: ; validate the error-code test word [esp], #0x0003 ; lowest 2-bits clear? jnz isrGPF ; no, show crash-dump test word [esp], #0x0004 ; TI-bit is set? jz isrGPF ; no, show crash-dump push ebp ; save caller stackframe mov ebp, esp ; setup local stackframe pushad ; preserve cpu registers push ds push es ; access 'memmap' and 'theLDT' data-structures mov ax, #sel_ss ; address this segment mov ds, ax ; with DS register mov ebx, 4[ebp] ; fetch the error-code and ebx, #0xFFF8 ; isolate index-field lea esi, memmap[ebx*2] ; point to memmap record lea edi, theLDT[ebx] ; point to LDT descriptor ; mark the segment-descriptor as 'present' mov eax, 12[esi] ; use the segment-type mov 5[edi], al ; as descriptor-type ; see if copying of the memory-image is needed mov ecx, 8[esi] ; setup the copy-length jecxz nocopy ; zero? skip any copying ; perform 'loading' of the absent program-segment mov edi, 4[esi] ; setup dest'n offset mov esi, 0[esi] ; setup source offset mov ax, #sel_SS ; address flat segment mov ds, ax ; with DS register mov es, ax ; also ES register cld ; use forward direction rep movsb ; load program's segment nocopy: pop es ; restore saved registers pop ds popad mov esp, ebp ; restore caller stackframe pop ebp add esp, #4 ; discard the error-code iretd ; retry the instruction ;----------------------------------------------------------------- .ALIGN 16 ; alignment at paragraph .SPACE 512 ; reserved for stack use tos0: ; label fop top-of-stack ;----------------------------------------------------------------- END