MAG - DDJ0992.ZIP - DB.ASC

 
Output of file : DB.ASC contained in archive : DDJ0992.ZIP

_YOUR OWN PROTECTED-MODE DEBUGGER_
by Rick Knoblaugh

[Listing One]

;-----------------------------------------------------------------------------
;pass_thru - This procedure is JMPed to by any interrupt handler which wishes
; to pass control to the original ISR per the interrupt vector table. Also,
; it checks to see if there are any breakpoints set on the int. If there are,
; the int being passed through is checked to see if it matches the condition
; for the break point. If the condition for the break point is met, DR0 is
; used to cause a break at the ISR. Enter: See stack_area struc for stack
; layout. Any error code has been removed from stack. EIP on stack has been
; adjusted if necessary.
;-----------------------------------------------------------------------------
pass_thru proc near
mov bp, sp
pushad
call adjust_ustack ;adjust user stack
;returns with [esi][edx] pointing to user stack area
mov cx, [bp].s_cs ;put on user cs
mov [esi][edx].user_cs, cx
mov ecx, [bp].s_eip ;put on ip
mov [esi][edx].user_ip, cx
movzx ebx, [bp].s_pushed_int ;get int number
movzx ecx, [ebx * 4].d_offset ;offset portion
mov [bp].s_eip, ecx
mov cx, [ebx * 4].d_seg ;segment portion
mov [bp].s_cs, cx

mov cx, offset gdt_seg:sel_data
mov fs, cx
assume fs:data
push fs
cmp fs:trap_clear, TRUE ;tracing through an int?
jne short pass_thru500
mov fs:trap_clear, FALSE ;reset it
mov fs:int1_active, TRUE ;debugger active
;If tracing through a software INT instruction, don't want the INT to
;be treated as one instruction (trap flag is cleared upon entering
;interrupts). In order to single step starting with the first instruction
;of the ISR, move address of the first instruction onto the user's stack
;and make user cs:ip on PL0 stack point to debugger int 1.
sub edx, 6 ;flags, cs, ip
mov [bp].s_esp, edx ;adjust it
mov ecx, [bp].s_eflags ;put on flags
mov [esi][edx].user_flags, cx
mov cx, [bp].s_cs ;put &ISR onto user's stack
mov [esi][edx].user_cs, cx
mov ecx, [bp].s_eip
mov [esi][edx].user_ip, cx

mov cx, ZCODE
mov [bp].s_cs, cx
mov cx, offset int_1_isr
movzx ecx, cx
mov [bp].s_eip, ecx
pass_thru500:
pop ds ;get data seg (was fs)
assume ds:data

cmp int1_active, TRUE ;is debugger active?
je short pass_thru999 ;if so, don't even think
;of breaking
mov cx, num_int_bp ;number of defined int breaks
jcxz pass_thru999 ;if no int type break points
mov si, offset int_bpdat
pass_thru700:
cmp [si].int_stat, ACTIVE ;is break on int enabled?
jne short pass_thru800
dec cx
cmp [si].int_num, bl ;is this the int specified?
jne short pass_thru800
cmp [si].int_reg, NO_CONDITION ;no conditions?
je short pass_thru750 ;if none go ahead and set break
mov dx, [si].int_val ;get data for comparison
cmp [si].int_reg, INT_AL_COMP ;condition compare on al?
jne short pass_thru730
cmp al, dl ;condition met?
je pass_thru750 ;if so, go ahead and set break
jmp short pass_thru800 ;if != look for more conditions
pass_thru730:
cmp [si].int_reg, INT_AH_COMP ;condition compare on ah?
jne short pass_thru740
cmp ah, dl ;condition met
je short pass_thru750 ;if so, go ahead and set break
jmp short pass_thru800 ;if != look for more conditions
pass_thru740: ;condition compare on ax
cmp ax, dx
jne short pass_thru800 ;if != look for more conditions
pass_thru750:
mov ebx, [bp].s_eip ;get offset and
movzx edx, [bp].s_cs ;segment of ISR
shl edx, 4 ;convert to linear
add edx, ebx

mov ch, 1 ;set debug register
mov al, DEB_DAT_LEN1 ;exec breaks use 1 byte length
mov ah, DEB_TYPE_EXEC
sub cl, cl ;debug reg zero
call do_debug_reg
jmp short pass_thru999
pass_thru800:
add si, size info_int ;advance to next int break
or cl, cl ;all int breaks checked?
jnz short pass_thru700 ;if not, check the next one
pass_thru999:
popad

add sp, 2 ;get rid of int number
pop bp
iretd
pass_thru endp


[Listing Two]

;-----------------------------------------------------------------------------
;gen_prot_isr - JMP here if int 0dh. Look for software int. If a software int
; caused the exception then: If debugger is active, look for user software
; interrupts issued by PL3 layer of debugger. If int 15h function 89h deny.
; If int 15h function 87h, emulate it. If none of these, simply route the
; interrupt per the real mode interrupt vector table. If exception was not
; caused by a software int and there are breakpoints defined on I/O accesses,
; look for I/O instruction. If it is an I/O instruction, temporarily clear
; the corresponding TSS I/O permission bit map bit and set trap flag to
; single step through the instruction. If other than software int or I/O,
; display cs:ip, 0dh and then halt.
;----------------------------------------------------------------------------
gen_prot_isr proc near
pushad
;Note: Don't use DX or AX below as DX may contain an I/O port address; in the
; case of a software interrupt, AH will have a function code. Also, don't use
; SI or CX as they are inputs for extended memory block move function
mov bx, offset gdt_seg:sel_databs
mov ds, bx
movzx ebx, [bp].e_cs ;get cs of user instruction
shl ebx, 4 ;make linear
add ebx, [bp].e_eip ;add ip
mov bx, [ebx] ;get bytes at cs:ip

mov di, offset gdt_seg:sel_data
mov ds, di ;debugger's data

cmp bl, INT_OPCODE
je short gen_prot020

cmp bl, INT3_OPCODE
jne gen_prot150 ;go look for I/O instruction
mov bh, 3 ;interrupt 3
gen_prot020:
cmp trace_count, 0 ;is debugger tracing?
je short gen_prot040 ;if not, skip test below
;See if this software interrupt is the instruction through which the user
;is tracing. If it is, set flag.
mov di, [bp].e_cs
cmp di, tuser_cs
jne short gen_prot040
mov edi, [bp].e_eip
cmp di, tuser_ip
jne short gen_prot040
; Clear trap bit so that it will not be set on user stack. Note: If user is
; doing a "trace n" where n is a number of instructions exceeding the number of
; instructions in the ISR, instructions executing upon return from ISR will
; still be trapped through as the int 1 code will again set the trap flag.
btr [bp].e_eflags, trapf
mov trap_clear, TRUE
gen_prot040:
inc [bp].e_eip ;get past the 0cdh (or 0cch)
cmp bh, 3 ;int 3?
je short gen_prot060 ;if so, only 1 byte
gen_prot050:
inc [bp].e_eip
gen_prot060:

;See if the debugger is active and if this software interrupt is one of the
;ones used by the PL3 portion of the debugger to get PL0 services.
cmp int1_active, TRUE ;is debugger active?
jne short gen_prot085
;Note: In the event that an interrupt occuring while debugger is active
; (e.g. timer) actually uses these user software interrupts,
; code to verify caller would need to be added here.
cmp bh, 60h ;do debug registers?
jne short gen_prot080

popad
call do_debug_reg
jmp gen_prot299
gen_prot080:
cmp bh, 61h ;do I/O bit map?
jne short gen_prot085

popad
; Unlike accessing of debug registers, PL3 code could actually manipulate TSS
; I/O bit map directly. However, this interface keeps this in one location.
call do_bit_map
jmp gen_prot299
gen_prot085:
cmp bh, 15h ;int 15?
jne short gen_prot100
cmp ah, 89h ;request for protected mode?
jne short gen_prot090
;if so, can't allow
bts [bp].e_eflags, carry ;set carry
popad
jmp gen_prot299 ;and return
gen_prot090:
cmp ah, 87h ;request for extended move?
jne short gen_prot100
call emulate_blk_mov ;if so, we must do it
popad
mov ah, 0 ;default to success
jnz gen_prot299 ;exit if success
mov ah, 3 ;indicate a20 gate failed
jmp gen_prot299 ;and return
gen_prot100:
;Adjust stack so that error code goes away and int number retrieved from
;instruction goes in spot on stack where pushed int number is (for stacks
;with no error code). Stack will be the way pass_thru routine likes it.
mov ax, bx
mov bx, [bp].e_pushed_bp
shl ebx, 16 ;get into high word
mov bl, ah ;interrupt number
mov [bp].e_errcode, ebx

cmp bl, 1 ;software int 1?
jne short gen_prot140
; Check to see if we are already in debugger. This is to handle the unlikely
; case where there is an actual INT 1 instruction inside of an interrupt
; handler. If there is and debugger is active, instruction will be ignored.
popad
cmp int1_active, TRUE ;already in debugger?
jne short gen_prot130 ;if not, go enter int 1

;else ignore it by returning
add sp, 2 ;get rid of int number
pop bp
iretd
gen_prot130:
add sp, 4 ;error code gone
mov bp, sp
pushad
jmp int_1_210 ;go enter int 1
gen_prot140:
popad
add sp, 4 ;error code gone
jmp pass_thru ;route the int via vectors
gen_prot150:
cmp num_io_bp, 0 ;any I/O break points defined?
je short gen_prot400 ;if not, don't look for I/O

xor ah, ah ;use as string flag
cmp bl, REP_PREFIX ;rep ?
jne short gen_prot190
mov ah, STRING ;only string type use rep
mov bl, bh ;get 2nd byte
gen_prot190:
; If repeat prefix was found, ah now has a flag indicating only string type
; I/O instructions should be expected and bl now contains the byte of object
; code past the repeat prefix. Note: To be complete, this code should also
; look for the operand-size prefix and segment overrides.
mov si, offset io_table
mov cx, IO_TAB_ENTRIES
gen_prot200:
or ah, ah ;strings only?
jz short gen_prot225 ;if not, go test

test [si].io_info, ah ;if table entry is not a string
jz short gen_prot300 ;type I/O, go try next one
gen_prot225:
cmp bl, [si].io_opcode
jne short gen_prot300
mov io_instrucf, TRUE ;instruction found
mov cl, [si].io_info ;get info about instruction

mov io_inst_info, cl
test cl, CONSTANT ;port number in instruction?
jz short gen_prot250 ;if not, we have it
movzx dx, bh ;get port
gen_prot250:
mov io_inst_port, dx ;save port
mov cx, 1 ;number of bits
sub ah, ah ;indicate clear
call do_bit_map
gen_prot260:
bts [bp].e_eflags, trapf ;single step i/o
popad
gen_prot299:
add sp, 2 ;int number pushed
pop bp
add sp, 4 ;error code
iretd
gen_prot300:
add si, size io_struc ;advance to next table entry
loop gen_prot200
gen_prot400:
;Also need to add code here to check for a few other exceptions (e.g.
;the HLT instruction).
mov ax, [bp].e_cs ;get cs of user instruction
call display_it
mov eax, [bp].e_eip ;add ip
call display_it
popad
mov ax, [bp].e_pushed_int
jmp fatal_error
gen_prot_isr endp



[Listing Three]

;-----------------------------------------------------------------------------
; do_debug_reg - enable/disable debug register for break point.
; Enter: ch = 0 if clearing break point: ch = 1 if setting breakpoint;
; ch = 2 if setting up break point type and address, but not enabling yet;
; ch = 3 get bn portion of debug status register into ax
; If clearing and eax !=0, eax holds other bits to be cleared (used for
; also clearing ge or le bits). cl = debug register number (0-3) if setting
; also have: al = length (0=1 byte, 1=2 bytes, 3=4 bytes); ah = type
; (0=execution, 1=write, 3=read/write); edx = linear address for break
; Also, if al='*' simply reactivate the breakpoint keeping the existing
; type and address. Exit: if disabling, specified debug register breakpoint
; is disabled. If enabling, specified debug register is loaded and
; breakpoint is enabled. If getting debug status register, bn portion of
; DR6 is returned in AX.
; Save ebx.
;-----------------------------------------------------------------------------
do_debug_reg proc near
cmp ch, 3 ;requesting status?
jne short do_deb050 ;if not
mov eax, dr6 ;debug status register
and ax, 0fh ;isolate bn status
ret ;and return
do_deb050:
push ebx
mov ebx, dr7 ;get debug control reg
cmp ch, 1 ;determine function
jb short do_deb850 ;if clear function go do it
ja short do_deb100 ;setup, but not enable
cmp al, '*' ;simply reset?
je short do_deb850
do_deb100:
push cx ;save function/reg #
push edx ;save linear address
mov edx, 0fh ;4 on bits
shl cl, 2 ;reg # * bits associated
add cl, 16 ;upper portion of 32 bit reg
shl edx, cl
not edx ;associated bits off
and ebx, edx ;in the dr7 value
shl al, 2 ;length bits to len position
or al, ah ;put in the type
mov dl, ah ;save type
sub ah, ah
shl eax, cl ;move len/rw to position
or ebx, eax
or dl, dl ;execution type?
jz short do_deb500 ;if so, don't need ge
bts bx, ge_bit
do_deb500:
pop edx ;restore linear address
pop cx ;and debug register #
cmp cl, 1
je short do_deb600
ja short do_deb700
mov dr0, edx
jmp short do_deb800
do_deb600:
mov dr1, edx
jmp short do_deb800
do_deb700:
cmp cl, 3
je short do_deb750
mov dr2, edx
jmp short do_deb800
do_deb750:
mov dr3, edx
do_deb800:
cmp ch, 2 ;setup, but not enable?
je short do_deb900 ;if so, skip enable
do_deb850:
shl cl, 1 ;get to global enable for #
inc cl
movzx dx, cl ;bit number to turn on
bts bx, dx ;set on in dr7 value
or ch, ch ;set function?
jnz short do_deb900
btr bx, dx ;if not, disable break
or ax, ax ;clear ge or le?
jz short do_deb900 ;if not continue
btr bx, ax ;if so, clear ge or le bit
do_deb900:
mov dr7, ebx ;put adjusted value back
pop ebx
do_deb999:
ret
do_debug_reg endp

isrcode ends
end
End Listings