;//--------------------------------------------------------------- ;// trydebug3.s (modification to 'trydebug2.s') ;// ;// 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 hello.s -o hello.o ;// and link with: $ ld hello.o -o hello ;// install using: $ dd if=hello of=/dev/fd0 seek=13 ;// ;// This 'second-stage loader' program is placed on our boot- ;// disk using these commands: ;// ;// assemble with: $ as86 tryexec.s -b tryexec.b ;// install using: $ dd if=tryexec.b of=/dev/fd0 seek=1 ;// ;// NOTE: This code begins executing with CS:IP = 1000:0002. ;// ;// programmer: ALLAN CRUSE ;// written on: 29 MAR 2004 ;// revised on: 06 APR 2004 -- added 'single-step' capability ;// revised on: 07 APR 2004 -- added the display of FS and GS ;// revised on: 08 APR 2004 -- added int-0x80 breakpoint trap ;// revised on: 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 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 ; initialize IDT descriptor for gate 0x01 mov edi, #0x01 ; ID-number for the gate lea di, theIDT[edi*8] ; gate's offset-address mov 0[di], #isrDBG ; 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: ; disable any debug breakpoints ;<-- added 4/8/2004 xor eax, eax mov dr7, eax mov dr6, eax mov dr3, eax mov dr2, eax mov dr1, eax mov dr0, eax ; 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: ; turn off the diskette drive-motor(s) mov dx, #0x03F2 ; FDC Digital Output port mov al, #0xC0 ; clear the motor-on bits out dx, al ; write settings to FDC ; 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 pushfd bts dword [esp], #8 ; set image of TF-bit popfd 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 SINGLE-STEP EXCEPTIONS ========== ;================================================================= ;----------------------------------------------------------------- name: .ASCII "DR7=DR6=DR3=DR2=DR1=DR0=" .ASCII " GS= FS=" ; <------ Added 4/7/2004 .ASCII " ES= DS=" ; array of register names .ASCII "EDI=ESI=EBP=ESP=EBX=EDX=ECX=EAX=" .ASCII "EIP= CS=EFL=ESP= SS=" rnum EQU (* - name)/4 ; number of array entries info: .ASCII " nnn=xxxxxxxx " ; buffer for hex display ilen EQU * - info ; length of output buffer ;----------------------------------------------------------------- isrDBG: pushad ; push general registers mov ebp, esp ; setup stack-frame base push dword #0 ; push doubleword zero mov [esp], ds ; store DS in low word push dword #0 ; push doubleword zero mov [esp], es ; store ES in low word push dword #0 ; push doubleword zero mov [esp], fs ; store FS in low word push dword #0 ; push doubleword zero mov [esp], gs ; store GS in low word ; ok, let's display the Debug Registers, too mov eax, dr0 push eax mov eax, dr1 push eax mov eax, dr2 push eax mov eax, dr3 push eax mov eax, dr6 push eax mov eax, dr7 push eax push ds ; preserve DS and ES push es mov ax, #sel_es ; address video memory mov es, ax ; with ES register ; examing the Debug Status Register DR6 mov eax, dr6 ; read register DR6 test eax, #0x0000000F ; any breakpoints? jz nobps ; no, keep RF-flag bts dword 0x28[ebp], #16 ; else set RF-flag nobps: ; examine instruction at saved CS:[EIP] address lds esi, 0x20[ebp] ; point to instruction mov eax, [esi] ; fetch next opcode ; set breakpoint-trap after any 'int-nn' instruction cmp al, #0xCD ; is it 'int-nn'? jne nobrk ; no, skip break-set add esi, #2 ; advance past 'int-nn' mov dr0, esi ; store address in DR0 mov eax, dr7 ; get current DR7 and eax, #0xFFF0FFFC ; clear G0 and L0 bits or eax, #0x00000001 ; enable L0 breakpoint mov dr7, eax ; update register DR7 jmp dbrxx nobrk: jmp dbrxx dbrxx: ; ok, here we display our stack-frame (with labels) mov ax, #sel_ss ; address this segment mov ds, ax ; with DS register cld ; do forward processing xor ebx, ebx ; counter starts from 0 nxreg: lea esi, [info] ; point to output name mov eax, name[ebx*4] ; fetch register label mov 1[esi], eax ; store register label lea edi, 5[esi] ; point to output value ;;mov eax, [ebp+ebx*4-8] ; fetch register value ;;mov eax, [ebp+ebx*4-16] ; fetch register value <-- added 4/7/04 mov eax, [ebp+ebx*4-40] ; fetch register value <-- added 4/8/04 call reg2hex ; store value in hex imul edi, ebx, #160 ; point to screen line add edi, #100 ; indent to column 50 mov ah, #0x50 ; setup color attribute mov ecx, #ilen ; setup message length nxchr: lodsb ; fetch message character stosw ; write char + attribute loop nxchr ; transfer entire string inc ebx ; increment the iterator cmp ebx, #rnum ; maximum value reached? jl nxreg ; no, show next register ; now await the release of a user's key-press kbwait: in al, #0x64 ; poll keyboard status test al, #0x01 ; new scancode ready? jz kbwait ; no, continue polling in al, #0x60 ; input the new scancode test al, #0x80 ; was a key released? jz kbwait ; no, wait for a release ; restore the suspended task's registers, and resume pop es ; retore ES and DS pop ds mov esp, ebp ; realign stack-pointer popad ; restore registers iretd ; resume interrupted task ;----------------------------------------------------------------- reg2hex: pushad mov edx, eax mov ecx, #8 .L4: rol edx, #4 mov al, dl and al, #0x0F cmp al, #10 sbb al, #0x69 das mov [edi], al inc edi loop .L4 popad ret ;----------------------------------------------------------------- .ALIGN 16 ; alignment at paragraph .SPACE 512 ; reserved for stack use tos0: ; label fop top-of-stack ;----------------------------------------------------------------- END