# #include <minix/config.h> #include <minix/const.h> #include "const.h" #include "sconst.h" #include "protect.h" ! This file contains a number of assembly code utility routines needed by the ! kernel. They are: .define _monitor ! exit Minix and return to the monitor .define real2prot ! switch from real to protected mode .define prot2real ! switch from protected to real mode .define _check_mem ! check a block of memory, return the valid size .define _cp_mess ! copies messages from source to destination .define _exit ! dummy for library routines .define __exit ! dummy for library routines .define ___exit ! dummy for library routines .define .fat, .trp ! dummies for library routines .define _in_byte ! read a byte from a port and return it .define _in_word ! read a word from a port and return it .define _out_byte ! write a byte to a port .define _out_word ! write a word to a port .define _port_read ! transfer data from (disk controller) port to memory .define _port_read_byte ! likewise byte by byte .define _port_write ! transfer data from memory to (disk controller) port .define _port_write_byte ! likewise byte by byte .define _lock ! disable interrupts .define _unlock ! enable interrupts .define _enable_irq ! enable an irq at the 8259 controller .define _disable_irq ! disable an irq .define _phys_copy ! copy data from anywhere to anywhere in memory .define _mem_rdw ! copy one word from [segment:offset] .define _reset ! reset the system .define _mem_vid_copy ! copy data to video ram .define _vid_vid_copy ! move data in video ram .define _level0 ! call a function at level 0 .define klib_init_prot ! initialize klib functions for protected mode ! The routines only guarantee to preserve the registers the C compiler ! expects to be preserved (si, di, bp, sp, segment registers, and direction ! bit in the flags), though some of the older ones preserve bx, cx and dx. #define DS_286_OFFSET DS_286_INDEX*DESC_SIZE #define ES_286_OFFSET ES_286_INDEX*DESC_SIZE # define EM_XFER_FUNC 0x87 #define JMP_OPCODE 0xE9 /* opcode used for patching */ #define OFF_MASK 0x000F /* offset mask for phys_b -> hclick:offset */ #define HCHIGH_MASK 0x0F /* h/w click mask for low byte of hi word */ #define HCLOW_MASK 0xF0 /* h/w click mask for low byte of low word */ ! Imported functions .extern p_restart .extern p_save .extern _restart .extern save ! Exported variables .extern kernel_cs ! Imported variables .extern kernel_ds .extern _irq_use .extern _blank_color .extern _gdt .extern _protected_mode .extern _vid_seg .extern _vid_size .extern _vid_mask .extern _level0_func .text !*===========================================================================* !* monitor * !*===========================================================================* ! PUBLIC void monitor(); ! Return to the monitor. _monitor: call prot2real ! switch to real mode mov ax, _reboot_code+0 ! address of new parameters mov dx, _reboot_code+2 mov sp, _mon_sp ! restore monitor stack pointer mov bx, _mon_ss ! monitor data segment mov ds, bx mov es, bx mov ss, bx pop di pop si pop bp retf ! return to the monitor #if ENABLE_BIOS_WINI !*===========================================================================* !* bios13 * !*===========================================================================* ! PUBLIC void bios13(); .define _bios13 _bios13: ! make a BIOS 0x13 call for disk I/O push si push di ! save C variable registers pushf ! save flags call int13 ! make the actual call popf ! restore flags pop di ! restore C registers pop si ret ! Make a BIOS 0x13 call from protected mode p_bios13: push bp push si push di ! save C variable registers pushf ! save flags cli ! no interruptions inb INT2_CTLMASK movb ah, al inb INT_CTLMASK push ax ! save interrupt masks mov ax, _irq_use ! map of in-use IRQs and ax, #~[1<<CLOCK_IRQ] ! there is a special clock handler outb INT_CTLMASK ! enable all unused IRQs and vv. movb al, ah outb INT2_CTLMASK smsw ax push ax ! save machine status word call prot2real ! switch to real mode call int13 ! make the actual call call real2prot ! back to protected mode pop ax lmsw ax ! restore msw pop ax ! restore interrupt masks outb INT_CTLMASK movb al, ah outb INT2_CTLMASK popf ! restore flags pop di pop si pop bp ! restore C variable registers ret int13: mov ax, _Ax ! load parameters mov bx, _Bx mov cx, _Cx mov dx, _Dx mov es, _Es sti ! enable interrupts int 0x13 ! make the BIOS call cli ! disable interrupts mov _Ax, ax ! save results mov _Bx, bx mov _Cx, cx mov _Dx, dx mov _Es, es mov ax, ds mov es, ax ! restore es ret .bss .define _Ax, _Bx, _Cx, _Dx, _Es ! 8086 register variables .comm _Ax, 2 .comm _Bx, 2 .comm _Cx, 2 .comm _Dx, 2 .comm _Es, 2 .text #endif /* ENABLE_BIOS_WINI */ !*===========================================================================* !* real2prot * !*===========================================================================* ! Switch from real to protected mode. real2prot: lgdt _gdt+GDT_SELECTOR ! set global descriptor table smsw ax mov msw, ax ! save real mode msw orb al, #0x01 ! set PE (protection enable) bit lmsw ax ! set msw, enabling protected mode jmpf csinit, CS_SELECTOR ! set code segment selector csinit: mov ax, #DS_SELECTOR ! set data selectors mov ds, ax mov es, ax mov ss, ax lidt _gdt+IDT_SELECTOR ! set interrupt vectors andb _gdt+TSS_SELECTOR+DESC_ACCESS, #~0x02 ! clear TSS busy bit mov ax, #TSS_SELECTOR ltr ax ! set TSS register movb ah, #0xDF jmp gate_A20 ! enable the A20 address line !*===========================================================================* !* prot2real * !*===========================================================================* ! Switch from protected to real mode. prot2real: mov save_sp, sp ! save stack pointer cmp _processor, #386 ! is this a 386? jae p2r386 p2r286: mov _gdt+ES_286_OFFSET+DESC_BASE, #0x0400 movb _gdt+ES_286_OFFSET+DESC_BASE_MIDDLE, #0x00 mov ax, #ES_286_SELECTOR mov es, ax ! BIOS data segment eseg mov 0x0067, #real ! set return from shutdown address cseg mov ax, kernel_cs eseg mov 0x0069, ax movb al, #0x8F outb 0x70 ! select CMOS byte 0x0F (disable NMI) jmp .+2 movb al, #0x0A outb 0x71 ! set shutdown code to 0x0A "jump far" jmp p_reset ! cause a processor shutdown p2r386: lidt idt_vectors ! real mode interrupt vectors push _gdt+CS_SELECTOR+0 push _gdt+DS_SELECTOR+0 ! save CS and DS limits mov _gdt+CS_SELECTOR+0, #0xFFFF mov _gdt+DS_SELECTOR+0, #0xFFFF ! set 64k limits jmpf cs64k, CS_SELECTOR ! reload selectors cs64k: mov ax, #DS_SELECTOR mov ds, ax mov es, ax mov ss, ax pop _gdt+DS_SELECTOR+0 pop _gdt+CS_SELECTOR+0 ! restore CS and DS limits .data1 0x0F,0x20,0xC0 ! mov eax, cr0 mov ax, msw ! restore real mode (16 bits) msw .data1 0x0F,0x22,0xC0 ! mov cr0, eax .data1 0xEA ! jmpf real, "kernel_cs" .data2 real kernel_cs: .data2 0 real: cseg mov ax, kernel_ds ! reload data segment registers mov ds, ax mov es, ax mov ss, ax mov sp, save_sp ! restore stack movb ah, #0xDD !jmp gate_A20 ! disable the A20 address line ! Enable (ah = 0xDF) or disable (ah = 0xDD) the A20 address line. gate_A20: call kb_wait movb al, #0xD1 ! Tell keyboard that a command is coming outb 0x64 call kb_wait movb al, ah ! Enable or disable code outb 0x60 call kb_wait mov ax, #25 ! 25 microsec delay for slow keyboard chip 0: out 0xED ! Write to an unused port (1us) dec ax jne 0b ret kb_wait: inb 0x64 testb al, #0x02 ! Keyboard input buffer full? jnz kb_wait ! If so, wait ret !*===========================================================================* !* check_mem * !*===========================================================================* ! PUBLIC phys_bytes check_mem(phys_bytes base, phys_bytes size); ! Check a block of memory, return the amount valid. ! Only every 16th byte is checked. ! This only works in protected mode. ! An initial size of 0 means everything. ! This really should do some alias checks. PCM_DENSITY = 256 ! resolution of check ! the shift logic depends on this being 256 TEST1PATTERN = 0x55 ! memory test pattern 1 TEST2PATTERN = 0xAA ! memory test pattern 2 _check_mem: pop bx pop _gdt+DS_286_OFFSET+DESC_BASE pop ax ! pop base into base of source descriptor movb _gdt+DS_286_OFFSET+DESC_BASE_MIDDLE,al pop cx ! byte count in dx:cx pop dx sub sp,#4+4 push bx push ds sub ax,ax ! prepare for early exit test dx,#0xFF00 jnz cm_1exit ! cannot handle bases above 16M movb cl,ch ! divide size by 256 and discard high byte movb ch,dl push cx ! save divided size sub bx,bx ! test bytes at bases of segments cm_loop: mov ax,#DS_286_SELECTOR mov ds,ax movb dl,#TEST1PATTERN xchgb dl,(bx) ! write test pattern, remember original value xchgb dl,(bx) ! restore original value, read test pattern cmpb dl,#TEST1PATTERN ! must agree if good real memory jnz cm_exit ! if different, memory is unusable movb dl,#TEST2PATTERN xchgb dl,(bx) xchgb dl,(bx) cmpb dl,#TEST2PATTERN jnz cm_exit ! next segment, test for wraparound at 16M ! assuming es == old ds eseg add _gdt+DS_286_OFFSET+DESC_BASE,#PCM_DENSITY eseg adcb _gdt+DS_286_OFFSET+DESC_BASE_MIDDLE,#0 loopnz cm_loop cm_exit: pop ax sub ax,cx ! verified size in multiples of PCM_DENSITY cm_1exit: movb dl,ah ! convert to phys_bytes in dx:ax subb dh,dh movb ah,al movb al,dh pop ds ret !*===========================================================================* !* cp_mess * !*===========================================================================* ! PUBLIC void cp_mess(int src, phys_clicks src_clicks, vir_bytes src_offset, ! phys_clicks dst_clicks, vir_bytes dst_offset); ! This routine makes a fast copy of a message from anywhere in the address ! space to anywhere else. It also copies the source address provided as a ! parameter to the call into the first word of the destination message. ! ! Note that the message size, "Msize" is in WORDS (not bytes) and must be set ! correctly. Changing the definition of message in the type file and not ! changing it here will lead to total disaster. _cp_mess: cld push es ! save es push ds ! save ds mov bx,sp ! index off bx because machine cannot use sp push si ! save si push di ! save di mov ax,12(bx) ! destination click #if HCLICK_SHIFT > CLICK_SHIFT #error /* Small click sizes are not supported (right shift will lose bits). */ #endif #if HCLICK_SHIFT < CLICK_SHIFT movb cl,#CLICK_SHIFT-HCLICK_SHIFT shl ax,cl ! destination segment #endif mov es,ax mov di,14(bx) ! offset of destination message ! Be careful not to destroy ds before we are finished with the bx pointer. ! We are using bx and not the more natural bp to save pushing bp. mov ax,6(bx) ! process number of sender mov si,10(bx) ! offset of source message mov bx,8(bx) ! source click (finished with bx as a pointer) #if HCLICK_SHIFT < CLICK_SHIFT shl bx,cl ! source segment #endif mov ds,bx stos ! copy process number of sender to dest message add si,*2 ! do not copy first word mov cx,*Msize-1 ! remember, first word does not count rep ! iterate cx times to copy 11 words movs ! copy the message pop di ! restore di pop si ! restore si pop ds ! restore ds pop es ! restore es ret ! that is all folks! !*===========================================================================* !* exit * !*===========================================================================* ! PUBLIC void exit(); ! Some library routines use exit, so provide a dummy version. ! Actual calls to exit cannot occur in the kernel. ! Same for .fat & .trp. _exit: __exit: ___exit: .fat: .trp: sti jmp __exit !*===========================================================================* !* in_byte * !*===========================================================================* ! PUBLIC unsigned in_byte(port_t port); ! Read an (unsigned) byte from the i/o port port and return it. _in_byte: pop bx pop dx ! port dec sp dec sp inb ! input 1 byte subb ah,ah ! unsign extend jmp (bx) !*===========================================================================* !* in_word * !*===========================================================================* ! PUBLIC unsigned short in_word(port_t port); ! Read an (unsigned) word from the i/o port and return it. _in_word: pop bx pop dx ! port dec sp dec sp ! added to new klib.s 3/21/91 d.e.c. inw ! input 1 word no sign extend needed jmp (bx) !*===========================================================================* !* out_byte * !*===========================================================================* ! PUBLIC void out_byte(port_t port, int value); ! Write value (cast to a byte) to the I/O port port. _out_byte: pop bx pop dx ! port pop ax ! value sub sp,#2+2 outb ! output 1 byte jmp (bx) !*===========================================================================* !* out_word * !*===========================================================================* ! PUBLIC void out_word(port_t port, int value); ! Write value (cast to a word) to the I/O port port. _out_word: pop bx pop dx ! port pop ax ! value sub sp,#2+2 outw ! output 1 word jmp (bx) !*===========================================================================* !* port_read * !*===========================================================================* ! PUBLIC void port_read(port_t port, phys_bytes destination,unsigned bytcount); ! Transfer data from (hard disk controller) port to memory. _port_read: push bp mov bp,sp push di push es call portio_setup shr cx,#1 ! count in words mov di,bx ! di = destination offset mov es,ax ! es = destination segment rep ins pop es pop di pop bp ret portio_setup: mov ax,4+2(bp) ! source/destination address in dx:ax mov dx,4+2+2(bp) mov bx,ax and bx,#OFF_MASK ! bx = offset = address % 16 andb dl,#HCHIGH_MASK ! ax = segment = address / 16 % 0x10000 andb al,#HCLOW_MASK orb al,dl movb cl,#HCLICK_SHIFT ror ax,cl mov cx,4+2+4(bp) ! count in bytes mov dx,4(bp) ! port to read from cld ! direction is UP ret !*===========================================================================* !* port_read_byte * !*===========================================================================* ! PUBLIC void port_read_byte(port_t port, phys_bytes destination, ! unsigned bytcount); ! Transfer data port to memory. _port_read_byte: push bp mov bp,sp push di push es call portio_setup mov di,bx ! di = destination offset mov es,ax ! es = destination segment rep insb pop es pop di pop bp ret !*===========================================================================* !* port_write * !*===========================================================================* ! PUBLIC void port_write(port_t port, phys_bytes source, unsigned bytcount); ! Transfer data from memory to (hard disk controller) port. _port_write: push bp mov bp,sp push si push ds call portio_setup shr cx,#1 ! count in words mov si,bx ! si = source offset mov ds,ax ! ds = source segment rep outs pop ds pop si pop bp ret !*===========================================================================* !* port_write_byte * !*===========================================================================* ! PUBLIC void port_write_byte(port_t port, phys_bytes source, ! unsigned bytcount); ! Transfer data from memory to port. _port_write_byte: push bp mov bp,sp push si push ds call portio_setup mov si,bx ! si = source offset mov ds,ax ! ds = source segment rep outsb pop ds pop si pop bp ret !*===========================================================================* !* lock * !*===========================================================================* ! PUBLIC void lock(); ! Disable CPU interrupts. _lock: cli ! disable interrupts ret !*===========================================================================* !* unlock * !*===========================================================================* ! PUBLIC void unlock(); ! Enable CPU interrupts. _unlock: sti ret !*==========================================================================* !* enable_irq * !*==========================================================================*/ ! PUBLIC void enable_irq(unsigned irq) ! Enable an interrupt request line by clearing an 8259 bit. ! Equivalent code for irq < 8: ! out_byte(INT_CTLMASK, in_byte(INT_CTLMASK) & ~(1 << irq)); _enable_irq: mov bx, sp mov cx, 2(bx) ! irq pushf cli movb ah, #~1 rolb ah, cl ! ah = ~(1 << (irq % 8)) cmpb cl, #8 jae enable_8 ! enable irq >= 8 at the slave 8259 enable_0: inb INT_CTLMASK andb al, ah outb INT_CTLMASK ! clear bit at master 8259 popf ret enable_8: inb INT2_CTLMASK andb al, ah outb INT2_CTLMASK ! clear bit at slave 8259 popf ret !*==========================================================================* !* disable_irq * !*==========================================================================*/ ! PUBLIC int disable_irq(unsigned irq) ! Disable an interrupt request line by setting an 8259 bit. ! Equivalent code for irq < 8: ! out_byte(INT_CTLMASK, in_byte(INT_CTLMASK) | (1 << irq)); ! Returns true iff the interrupt was not already disabled. _disable_irq: mov bx, sp mov cx, 2(bx) ! irq pushf cli movb ah, #1 rolb ah, cl ! ah = (1 << (irq % 8)) cmpb cl, #8 jae disable_8 ! disable irq >= 8 at the slave 8259 disable_0: inb INT_CTLMASK testb al, ah jnz dis_already ! already disabled? orb al, ah outb INT_CTLMASK ! set bit at master 8259 popf mov ax, #1 ! disabled by this function ret disable_8: inb INT2_CTLMASK testb al, ah jnz dis_already ! already disabled? orb al, ah outb INT2_CTLMASK ! set bit at slave 8259 popf mov ax, #1 ! disabled by this function ret dis_already: popf xor ax, ax ! already disabled ret !*===========================================================================* !* phys_copy * !*===========================================================================* ! PUBLIC void phys_copy(phys_bytes source, phys_bytes destination, ! phys_bytes bytecount); ! Copy a block of physical memory. SRCLO = 4 SRCHI = 6 DESTLO = 8 DESTHI = 10 COUNTLO = 12 COUNTHI = 14 _phys_copy: push bp ! save only registers required by C mov bp,sp ! set bp to point to source arg less 4 push si ! save si push di ! save di push ds ! save ds push es ! save es mov ax,SRCLO(bp) ! dx:ax = source address (dx is NOT segment) mov dx,SRCHI(bp) mov si,ax ! si = source offset = address % 16 and si,#OFF_MASK andb dl,#HCHIGH_MASK ! ds = source segment = address / 16 % 0x10000 andb al,#HCLOW_MASK orb al,dl ! now bottom 4 bits of dx are in ax movb cl,#HCLICK_SHIFT ! rotate them to the top 4 ror ax,cl mov ds,ax mov ax,DESTLO(bp) ! dx:ax = destination addr (dx is NOT segment) mov dx,DESTHI(bp) mov di,ax ! di = dest offset = address % 16 and di,#OFF_MASK andb dl,#HCHIGH_MASK ! es = dest segment = address / 16 % 0x10000 andb al,#HCLOW_MASK orb al,dl ror ax,cl mov es,ax mov ax,COUNTLO(bp) ! dx:ax = remaining count mov dx,COUNTHI(bp) ! copy upwards (cannot handle overlapped copy) pc_loop: mov cx,ax ! provisional count for this iteration test ax,ax ! if count >= 0x8000, only do 0x8000 per iter js pc_bigcount ! low byte already >= 0x8000 test dx,dx jz pc_upcount ! less than 0x8000 pc_bigcount: mov cx,#0x8000 ! use maximum count per iteration pc_upcount: sub ax,cx ! update count sbb dx,#0 ! cannot underflow, so carry clear now for rcr rcr cx,#1 ! count in words, carry remembers if byte jnb pc_even ! no odd byte movb ! copy odd byte pc_even: rep ! copy 1 word at a time movs ! word copy mov cx,ax ! test if remaining count is 0 or cx,dx jnz pc_more ! more to do pop es ! restore es pop ds ! restore ds pop di ! restore di pop si ! restore si pop bp ! restore bp ret ! return to caller pc_more: sub si,#0x8000 ! adjust pointers so the offset does not mov cx,ds ! overflow in the next 0x8000 bytes add cx,#0x800 ! pointers end up same physical location mov ds,cx ! the current offsets are known >= 0x8000 sub di,#0x8000 ! since we just copied that many mov cx,es add cx,#0x800 mov es,cx jmp pc_loop ! start next iteration !*===========================================================================* !* mem_rdw * !*===========================================================================* ! PUBLIC u16_t mem_rdw(u16_t segment, u16_t *offset); ! Load and return the word at the far pointer segment:offset. _mem_rdw: mov cx,ds ! save ds pop dx ! return adr pop ds ! segment pop bx ! offset sub sp,#2+2 ! adjust for parameters popped mov ax,(bx) ! load the word to return mov ds,cx ! restore ds jmp (dx) ! return !*===========================================================================* !* reset * !*===========================================================================* ! PUBLIC void reset(); ! Reset the system. ! In real mode we simply jump to the reset address. _reset: jmpf 0,0xFFFF !*===========================================================================* !* mem_vid_copy * !*===========================================================================* ! PUBLIC void mem_vid_copy(u16 *src, unsigned dst, unsigned count); ! ! Copy count characters from kernel memory to video memory. Src, dst and ! count are character (word) based video offsets and counts. If src is null ! then screen memory is blanked by filling it with blank_color. MVC_ARGS = 2 + 2 + 2 + 2 ! 2 + 2 + 2 ! es di si ip src dst ct _mem_vid_copy: push si push di push es mov bx, sp mov si, MVC_ARGS(bx) ! source mov di, MVC_ARGS+2(bx) ! destination mov dx, MVC_ARGS+2+2(bx) ! count mov es, _vid_seg ! destination is video segment cld ! make sure direction is up mvc_loop: and di, _vid_mask ! wrap address mov cx, dx ! one chunk to copy mov ax, _vid_size sub ax, di cmp cx, ax jbe 0f mov cx, ax ! cx = min(cx, vid_size - di) 0: sub dx, cx ! count -= cx shl di, #1 ! byte address test si, si ! source == 0 means blank the screen jz mvc_blank mvc_copy: rep ! copy words to video memory movs jmp mvc_test mvc_blank: mov ax, _blank_color ! ax = blanking character rep stos ! copy blanks to video memory !jmp mvc_test mvc_test: shr di, #1 ! word addresses test dx, dx jnz mvc_loop mvc_done: pop es pop di pop si ret !*===========================================================================* !* vid_vid_copy * !*===========================================================================* ! PUBLIC void vid_vid_copy(unsigned src, unsigned dst, unsigned count); ! ! Copy count characters from video memory to video memory. Handle overlap. ! Used for scrolling, line or character insertion and deletion. Src, dst ! and count are character (word) based video offsets and counts. VVC_ARGS = 2 + 2 + 2 + 2 ! 2 + 2 + 2 ! es di si ip src dst ct _vid_vid_copy: push si push di push es mov bx, sp mov si, VVC_ARGS(bx) ! source mov di, VVC_ARGS+2(bx) ! destination mov dx, VVC_ARGS+2+2(bx) ! count mov es, _vid_seg ! use video segment cmp si, di ! copy up or down? jb vvc_down vvc_up: cld ! direction is up vvc_uploop: and si, _vid_mask ! wrap addresses and di, _vid_mask mov cx, dx ! one chunk to copy mov ax, _vid_size sub ax, si cmp cx, ax jbe 0f mov cx, ax ! cx = min(cx, vid_size - si) 0: mov ax, _vid_size sub ax, di cmp cx, ax jbe 0f mov cx, ax ! cx = min(cx, vid_size - di) 0: sub dx, cx ! count -= cx shl si, #1 shl di, #1 ! byte addresses rep eseg movs ! copy video words shr si, #1 shr di, #1 ! word addresses test dx, dx jnz vvc_uploop ! again? jmp vvc_done vvc_down: std ! direction is down add si, dx ! start copying at the top dec si add di, dx dec di vvc_downloop: and si, _vid_mask ! wrap addresses and di, _vid_mask mov cx, dx ! one chunk to copy lea ax, 1(si) cmp cx, ax jbe 0f mov cx, ax ! cx = min(cx, si + 1) 0: lea ax, 1(di) cmp cx, ax jbe 0f mov cx, ax ! cx = min(cx, di + 1) 0: sub dx, cx ! count -= cx shl si, #1 shl di, #1 ! byte addresses rep eseg movs ! copy video words shr si, #1 shr di, #1 ! word addresses test dx, dx jnz vvc_downloop ! again? cld ! C compiler expect up !jmp vvc_done vvc_done: pop es pop di pop si ret !*===========================================================================* !* level0 * !*===========================================================================* ! PUBLIC void level0(void (*func)(void)) ! Not very interesting in real mode, see p_level0. ! _level0: mov bx, sp jmp @2(bx) !*===========================================================================* !* klib_init_prot * !*===========================================================================* ! PUBLIC void klib_init_prot(); ! Initialize klib for protected mode by patching some real mode functions ! at their starts to jump to their protected mode equivalents, according to ! the patch table. Saves a lot of tests on the "protected_mode" variable. ! Note that this function must be run in real mode, for it writes the code ! segment. (One otherwise has to set up a descriptor, etc, etc.) klib_init_prot: mov si,#patch_table kip_next: lods ! original function mov bx,ax cseg movb (bx),#JMP_OPCODE ! overwrite start of function by a long jump lods ! new function - target of jump sub ax,bx ! relative jump sub ax,#3 ! adjust by length of jump instruction cseg mov 1(bx),ax ! set address cmp si,#end_patch_table ! end of table? jb kip_next kip_done: ret !*===========================================================================* !* variants for protected mode * !*===========================================================================* ! Some routines are different in protected mode. ! The only essential difference is the handling of segment registers. ! One complication is that the method of building segment descriptors is not ! reentrant, so the protected mode versions must not be called by interrupt ! handlers. !*===========================================================================* !* p_cp_mess * !*===========================================================================* ! The real mode version attempts to be efficient by passing raw segments but ! that just gets in the way here. p_cp_mess: cld pop dx pop bx ! proc pop cx ! source clicks pop ax ! source offset #if CLICK_SHIFT != HCLICK_SHIFT + 4 #error /* the only click size supported is 256, to avoid slow shifts here */ #endif addb ah,cl ! calculate source offset adcb ch,#0 ! and put in base of source descriptor mov _gdt+DS_286_OFFSET+DESC_BASE,ax movb _gdt+DS_286_OFFSET+DESC_BASE_MIDDLE,ch pop cx ! destination clicks pop ax ! destination offset addb ah,cl ! calculate destination offset adcb ch,#0 ! and put in base of destination descriptor mov _gdt+ES_286_OFFSET+DESC_BASE,ax movb _gdt+ES_286_OFFSET+DESC_BASE_MIDDLE,ch sub sp,#2+2+2+2+2 push ds push es mov ax,#DS_286_SELECTOR mov ds,ax mov ax,#ES_286_SELECTOR mov es,ax eseg mov 0,bx ! proc no. of sender from arg, not msg mov ax,si mov bx,di mov si,#2 ! src offset is now 2 relative to start of seg mov di,si ! and destination offset mov cx,#Msize-1 ! word count rep movs mov di,bx mov si,ax pop es pop ds jmp (dx) !*===========================================================================* !* p_portio_setup * !*===========================================================================* ! The port_read, port_write, etc. functions need a setup routine that uses ! a segment descriptor. p_portio_setup: mov ax,4+2(bp) ! source/destination address in dx:ax mov dx,4+2+2(bp) mov _gdt+DS_286_OFFSET+DESC_BASE,ax movb _gdt+DS_286_OFFSET+DESC_BASE_MIDDLE,dl xor bx,bx ! bx = 0 = start of segment mov ax,#DS_286_SELECTOR ! ax = segment selector mov cx,4+2+4(bp) ! count in bytes mov dx,4(bp) ! port to read from cld ! direction is UP ret !*===========================================================================* !* p_phys_copy * !*===========================================================================* p_phys_copy: cld pop dx pop _gdt+DS_286_OFFSET+DESC_BASE pop ax ! pop source into base of source descriptor movb _gdt+DS_286_OFFSET+DESC_BASE_MIDDLE,al pop _gdt+ES_286_OFFSET+DESC_BASE pop ax ! pop destination into base of dst descriptor movb _gdt+ES_286_OFFSET+DESC_BASE_MIDDLE,al pop cx ! byte count in bx:cx pop bx sub sp,#4+4+4 push di push si push es push ds sub si,si ! src offset is now 0 relative to start of seg mov di,si ! and destination offset jmp ppc_next ! It is too much trouble to align the segment bases, so word alignment is hard. ! Avoiding the book-keeping for alignment may be good anyway. ppc_large: push cx mov cx,#0x8000 ! copy a large chunk of this many words rep movs pop cx dec bx pop ds ! update the descriptors incb _gdt+DS_286_OFFSET+DESC_BASE_MIDDLE incb _gdt+ES_286_OFFSET+DESC_BASE_MIDDLE push ds ppc_next: mov ax,#DS_286_SELECTOR ! (re)load the selectors mov ds,ax mov ax,#ES_286_SELECTOR mov es,ax test bx,bx jnz ppc_large shr cx,#1 ! word count rep movs ! move any leftover words rcl cx,#1 ! restore old bit 0 rep movb ! move any leftover byte pop ds pop es pop si pop di jmp (dx) !*===========================================================================* !* p_reset * !*===========================================================================* ! Reset the system by loading IDT with offset 0 and interrupting. p_reset: lidt idt_zero int 3 ! anything goes, the 286 will not like it !*===========================================================================* !* p_level0 * !*===========================================================================* ! PUBLIC void level0(void (*func)(void)) ! Call a function at permission level 0. This allows kernel tasks to do ! things that are only possible at the most privileged CPU level. ! p_level0: mov bx, sp mov ax, 2(bx) mov _level0_func, ax int LEVEL0_VECTOR ret !*===========================================================================* !* data * !*===========================================================================* .data patch_table: ! pairs (old function, new function) #if ENABLE_BIOS_WINI .data2 _bios13, p_bios13 #endif .data2 _cp_mess, p_cp_mess .data2 _phys_copy, p_phys_copy .data2 portio_setup, p_portio_setup .data2 _reset, p_reset .data2 _level0, p_level0 .data2 _restart, p_restart ! in mpx file .data2 save, p_save ! in mpx file end_patch_table: ! end of table idt_vectors: ! limit and base of real mode interrupt vectors .data2 0x3FF idt_zero: ! zero limit IDT to cause a processor shutdown .data2 0, 0, 0 .bss save_sp: ! place to put sp when switching to real mode .space 2 msw: ! saved real mode machine status word .space 2