;//--------------------------------------------------------------- ;// tryexec2.s (a revision of our 'tryexec.s' demo) ;// ;// This demo shows how to 'load' an exectable ELF file-image ;// at its intended load-address, in extended physical memory ;// (i.e., memory above 1MB), and then execute it from a code ;// segment whose segment-limit extends to include the entire ;// 4GB address-space. The file-image is loaded from memory- ;// address 0x00011800 (where it initially gets placed by our ;// 'trackldr' boot-loader). The ELF executable image is put ;// onto our boot-disk using these commands: ;// ;// assemble with: $ as showdate.s -o showdate.o ;// and link with: $ ld showdate.o -o showdate ;// install using: $ dd if=showdate of=/dev/fd0 seek=13 ;// ;// This 'second-stage loader' program is placed on our boot- ;// disk using these commands: ;// ;// assemble with: $ as86 tryexec2.s -b tryexec2.b ;// install using: $ dd if=tryexec2.b of=/dev/fd0 seek=1 ;// ;// NOTE: This code begins executing with CS:IP = 1000:0002. ;// ;// programmer: ALLAN CRUSE ;// written on: 29 MAR 2004 ;// bug repair: 10 MAY 2004 -- to preserve EBX in subroutine ;// revised on: 16 MAY 2004 -- implements 'iopl' system-call ;//--------------------------------------------------------------- ;----------------------------------------------------------------- .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 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, 0xFA00, 0x00CF ; code descriptor .WORD 0xFFFF, 0x0000, 0xF200, 0x00CF ; data descriptor .WORD 0xFFFF, 0x0000, 0xF200, 0x00CF ; stak descriptor ;----------------------------------------------------------------- theIDT: .SPACE 2048 ; for 256 gate-descriptors ;----------------------------------------------------------------- theTSS: .LONG 0, 0x00020000, sel_SS ; 32bit Task-State Segment ;----------------------------------------------------------------- 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 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 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, #110 ; is 'sys_iopl'? ;<-- added je do_iopl ; yes, jump to routine ;<-- added 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_iopl: ; new service's entry ;<-- added push ebp ; preserve register ;<-- added mov ebp, esp ; setup frame-pointer ;<-- added mov eax, ebx ; copy argument to EAX ;<-- added and eax, #3 ; insure within bounds ;<-- added shl eax, #12 ; shift into position ;<-- added or dword [ebp+12], #0x00003000 ; set IOPL bits ;<-- added and dword [ebp+12], eax ; adjust with argument ;<-- added xor eax, eax ; service return-code ;<-- added pop ebp ; recover EBP register ;<-- added iretd ; return to user task ;<-- added ;----------------------------------------------------------------- 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: 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 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 ;----------------------------------------------------------------- .ALIGN 16 ; alignment at paragraph .SPACE 512 ; reserved for stack use tos0: ; label fop top-of-stack ;----------------------------------------------------------------- END