btx.S revision 235264
1238384Sjkim/* 2238384Sjkim * Copyright (c) 1998 Robert Nordier 3238384Sjkim * All rights reserved. 4238384Sjkim * 5238384Sjkim * Redistribution and use in source and binary forms are freely 6238384Sjkim * permitted provided that the above copyright notice and this 7238384Sjkim * paragraph and the following disclaimer are duplicated in all 8238384Sjkim * such forms. 9238384Sjkim * 10238384Sjkim * This software is provided "AS IS" and without any express or 11238384Sjkim * implied warranties, including, without limitation, the implied 12238384Sjkim * warranties of merchantability and fitness for a particular 13238384Sjkim * purpose. 14238384Sjkim * 15238384Sjkim * $FreeBSD: head/sys/boot/pc98/btx/btx/btx.S 235264 2012-05-11 09:46:17Z avg $ 16238384Sjkim */ 17238384Sjkim 18238384Sjkim#include <bootargs.h> 19238384Sjkim 20238384Sjkim/* 21238384Sjkim * Memory layout. 22238384Sjkim */ 23238384Sjkim .set MEM_BTX,0x1000 # Start of BTX memory 24238384Sjkim .set MEM_ESP0,0x1800 # Supervisor stack 25238384Sjkim .set MEM_BUF,0x1800 # Scratch buffer 26238384Sjkim .set MEM_ESPR,0x5e00 # Real mode stack 27238384Sjkim .set MEM_IDT,0x5e00 # IDT 28238384Sjkim .set MEM_TSS,0x5f98 # TSS 29238384Sjkim .set MEM_MAP,0x6000 # I/O bit map 30238384Sjkim .set MEM_TSS_END,0x7fff # End of TSS 31238384Sjkim .set MEM_ORG,0x9000 # BTX code 32238384Sjkim .set MEM_USR,0xa000 # Start of user memory 33238384Sjkim/* 34238384Sjkim * Paging control. 35238384Sjkim */ 36238384Sjkim .set PAG_SIZ,0x1000 # Page size 37238384Sjkim .set PAG_CNT,0x1000 # Pages to map 38238384Sjkim/* 39238384Sjkim * Fields in %eflags. 40238384Sjkim */ 41238384Sjkim .set PSL_RESERVED_DEFAULT,0x00000002 42238384Sjkim .set PSL_T,0x00000100 # Trap flag 43238384Sjkim .set PSL_I,0x00000200 # Interrupt enable flag 44238384Sjkim .set PSL_VM,0x00020000 # Virtual 8086 mode flag 45238384Sjkim .set PSL_AC,0x00040000 # Alignment check flag 46238384Sjkim/* 47238384Sjkim * Segment selectors. 48238384Sjkim */ 49238384Sjkim .set SEL_SCODE,0x8 # Supervisor code 50238384Sjkim .set SEL_SDATA,0x10 # Supervisor data 51238384Sjkim .set SEL_RCODE,0x18 # Real mode code 52238384Sjkim .set SEL_RDATA,0x20 # Real mode data 53238384Sjkim .set SEL_UCODE,0x28|3 # User code 54238384Sjkim .set SEL_UDATA,0x30|3 # User data 55238384Sjkim .set SEL_TSS,0x38 # TSS 56238384Sjkim/* 57238384Sjkim * Task state segment fields. 58238384Sjkim */ 59238384Sjkim .set TSS_ESP0,0x4 # PL 0 ESP 60238384Sjkim .set TSS_SS0,0x8 # PL 0 SS 61238384Sjkim .set TSS_MAP,0x66 # I/O bit map base 62238384Sjkim/* 63238384Sjkim * System calls. 64238384Sjkim */ 65238384Sjkim .set SYS_EXIT,0x0 # Exit 66238384Sjkim .set SYS_EXEC,0x1 # Exec 67238384Sjkim/* 68238384Sjkim * Fields in V86 interface structure. 69238384Sjkim */ 70238384Sjkim .set V86_CTL,0x0 # Control flags 71238384Sjkim .set V86_ADDR,0x4 # Int number/address 72238384Sjkim .set V86_ES,0x8 # V86 ES 73238384Sjkim .set V86_DS,0xc # V86 DS 74238384Sjkim .set V86_FS,0x10 # V86 FS 75238384Sjkim .set V86_GS,0x14 # V86 GS 76238384Sjkim/* 77238384Sjkim * V86 control flags. 78238384Sjkim */ 79238384Sjkim .set V86F_ADDR,0x10000 # Segment:offset address 80238384Sjkim .set V86F_CALLF,0x20000 # Emulate far call 81238384Sjkim .set V86F_FLAGS,0x40000 # Return flags 82238384Sjkim/* 83238384Sjkim * Dump format control bytes. 84238384Sjkim */ 85238384Sjkim .set DMP_X16,0x1 # Word 86238384Sjkim .set DMP_X32,0x2 # Long 87238384Sjkim .set DMP_MEM,0x4 # Memory 88238384Sjkim .set DMP_EOL,0x8 # End of line 89238384Sjkim/* 90238384Sjkim * Screen defaults and assumptions. 91238384Sjkim */ 92238384Sjkim .set SCR_MAT,0xe1 # Mode/attribute 93238384Sjkim .set SCR_COL,0x50 # Columns per row 94238384Sjkim .set SCR_ROW,0x19 # Rows per screen 95238384Sjkim/* 96238384Sjkim * BIOS Data Area locations. 97238384Sjkim */ 98238384Sjkim .set BDA_MEM,0x501 # Free memory 99238384Sjkim .set BDA_POS,0x53e # Cursor position 100238384Sjkim/* 101238384Sjkim * Derivations, for brevity. 102238384Sjkim */ 103238384Sjkim .set _ESP0H,MEM_ESP0>>0x8 # Byte 1 of ESP0 104238384Sjkim .set _TSSIO,MEM_MAP-MEM_TSS # TSS I/O base 105238384Sjkim .set _TSSLM,MEM_TSS_END-MEM_TSS # TSS limit 106238384Sjkim .set _IDTLM,MEM_TSS-MEM_IDT-1 # IDT limit 107238384Sjkim/* 108238384Sjkim * Code segment. 109238384Sjkim */ 110238384Sjkim .globl start 111238384Sjkim .code16 112238384Sjkimstart: # Start of code 113238384Sjkim/* 114238384Sjkim * BTX header. 115238384Sjkim */ 116238384Sjkimbtx_hdr: .byte 0xeb # Machine ID 117238384Sjkim .byte 0xe # Header size 118238384Sjkim .ascii "BTX" # Magic 119238384Sjkim .byte 0x1 # Major version 120238384Sjkim .byte 0x2 # Minor version 121238384Sjkim .byte BTX_FLAGS # Flags 122238384Sjkim .word PAG_CNT-MEM_ORG>>0xc # Paging control 123238384Sjkim .word break-start # Text size 124238384Sjkim .long 0x0 # Entry address 125238384Sjkim/* 126238384Sjkim * Initialization routine. 127238384Sjkim */ 128238384Sjkiminit: cli # Disable interrupts 129238384Sjkim xor %ax,%ax # Zero/segment 130238384Sjkim mov %ax,%ss # Set up 131238384Sjkim mov $MEM_ESP0,%sp # stack 132238384Sjkim mov %ax,%es # Address 133238384Sjkim mov %ax,%ds # data 134238384Sjkim pushl $0x2 # Clear 135238384Sjkim popfl # flags 136238384Sjkim/* 137238384Sjkim * Initialize memory. 138238384Sjkim */ 139238384Sjkim mov $MEM_IDT,%di # Memory to initialize 140238384Sjkim mov $(MEM_ORG-MEM_IDT)/2,%cx # Words to zero 141238384Sjkim rep # Zero-fill 142238384Sjkim stosw # memory 143238384Sjkim/* 144238384Sjkim * Update real mode IDT for reflecting hardware interrupts. 145238384Sjkim */ 146238384Sjkim mov $intr20,%bx # Address first handler 147238384Sjkim mov $0x10,%cx # Number of handlers 148238384Sjkim mov $0x20*4,%di # First real mode IDT entry 149238384Sjkiminit.0: mov %bx,(%di) # Store IP 150238384Sjkim inc %di # Address next 151238384Sjkim inc %di # entry 152238384Sjkim stosw # Store CS 153238384Sjkim add $4,%bx # Next handler 154238384Sjkim loop init.0 # Next IRQ 155238384Sjkim/* 156238384Sjkim * Create IDT. 157238384Sjkim */ 158238384Sjkim mov $MEM_IDT,%di 159238384Sjkim mov $idtctl,%si # Control string 160238384Sjkiminit.1: lodsb # Get entry 161238384Sjkim cbw # count 162238384Sjkim xchg %ax,%cx # as word 163238384Sjkim jcxz init.4 # If done 164238384Sjkim lodsb # Get segment 165238384Sjkim xchg %ax,%dx # P:DPL:type 166238384Sjkim lodsw # Get control 167238384Sjkim xchg %ax,%bx # set 168238384Sjkim lodsw # Get handler offset 169238384Sjkim mov $SEL_SCODE,%dh # Segment selector 170238384Sjkiminit.2: shr %bx # Handle this int? 171238384Sjkim jnc init.3 # No 172238384Sjkim mov %ax,(%di) # Set handler offset 173238384Sjkim mov %dh,0x2(%di) # and selector 174238384Sjkim mov %dl,0x5(%di) # Set P:DPL:type 175238384Sjkim add $0x4,%ax # Next handler 176238384Sjkiminit.3: lea 0x8(%di),%di # Next entry 177238384Sjkim loop init.2 # Till set done 178238384Sjkim jmp init.1 # Continue 179238384Sjkim/* 180238384Sjkim * Initialize TSS. 181238384Sjkim */ 182238384Sjkiminit.4: movb $_ESP0H,TSS_ESP0+1(%di) # Set ESP0 183238384Sjkim movb $SEL_SDATA,TSS_SS0(%di) # Set SS0 184238384Sjkim movb $_TSSIO,TSS_MAP(%di) # Set I/O bit map base 185238384Sjkim/* 186238384Sjkim * Bring up the system. 187238384Sjkim */ 188238384Sjkim mov $0x2820,%bx # Set protected mode 189238384Sjkim callw setpic # IRQ offsets 190238384Sjkim lidt idtdesc # Set IDT 191238384Sjkim lgdt gdtdesc # Set GDT 192238384Sjkim mov %cr0,%eax # Switch to protected 193238384Sjkim inc %ax # mode 194238384Sjkim mov %eax,%cr0 # 195238384Sjkim ljmp $SEL_SCODE,$init.8 # To 32-bit code 196238384Sjkim .code32 197238384Sjkiminit.8: xorl %ecx,%ecx # Zero 198238384Sjkim movb $SEL_SDATA,%cl # To 32-bit 199238384Sjkim movw %cx,%ss # stack 200238384Sjkim/* 201238384Sjkim * Launch user task. 202238384Sjkim */ 203238384Sjkim movb $SEL_TSS,%cl # Set task 204238384Sjkim ltr %cx # register 205238384Sjkim movl $MEM_USR,%edx # User base address 206238384Sjkim movzwl %ss:BDA_MEM,%eax # Get free memory 207238384Sjkim andl $0x7,%eax 208238384Sjkim incl %eax 209238384Sjkim shll $0x11,%eax # To bytes 210238384Sjkim subl $ARGSPACE,%eax # Less arg space 211238384Sjkim subl %edx,%eax # Less base 212238384Sjkim movb $SEL_UDATA,%cl # User data selector 213238384Sjkim pushl %ecx # Set SS 214238384Sjkim pushl %eax # Set ESP 215238384Sjkim push $0x202 # Set flags (IF set) 216238384Sjkim push $SEL_UCODE # Set CS 217238384Sjkim pushl btx_hdr+0xc # Set EIP 218238384Sjkim pushl %ecx # Set GS 219238384Sjkim pushl %ecx # Set FS 220238384Sjkim pushl %ecx # Set DS 221238384Sjkim pushl %ecx # Set ES 222238384Sjkim pushl %edx # Set EAX 223238384Sjkim movb $0x7,%cl # Set remaining 224238384Sjkiminit.9: push $0x0 # general 225238384Sjkim loop init.9 # registers 226238384Sjkim#ifdef BTX_SERIAL 227238384Sjkim call sio_init # setup the serial console 228238384Sjkim#endif 229238384Sjkim popa # and initialize 230238384Sjkim popl %es # Initialize 231238384Sjkim popl %ds # user 232238384Sjkim popl %fs # segment 233238384Sjkim popl %gs # registers 234238384Sjkim iret # To user mode 235238384Sjkim/* 236238384Sjkim * Exit routine. 237238384Sjkim */ 238238384Sjkimexit: cli # Disable interrupts 239238384Sjkim movl $MEM_ESP0,%esp # Clear stack 240238384Sjkim/* 241238384Sjkim * Turn off paging. 242238384Sjkim */ 243238384Sjkim movl %cr0,%eax # Get CR0 244238384Sjkim andl $~0x80000000,%eax # Disable 245238384Sjkim movl %eax,%cr0 # paging 246238384Sjkim xorl %ecx,%ecx # Zero 247238384Sjkim movl %ecx,%cr3 # Flush TLB 248238384Sjkim/* 249238384Sjkim * Restore the GDT in case we caught a kernel trap. 250238384Sjkim */ 251238384Sjkim lgdt gdtdesc # Set GDT 252238384Sjkim/* 253238384Sjkim * To 16 bits. 254238384Sjkim */ 255238384Sjkim ljmpw $SEL_RCODE,$exit.1 # Reload CS 256238384Sjkim .code16 257238384Sjkimexit.1: mov $SEL_RDATA,%cl # 16-bit selector 258238384Sjkim mov %cx,%ss # Reload SS 259238384Sjkim mov %cx,%ds # Load 260238384Sjkim mov %cx,%es # remaining 261238384Sjkim mov %cx,%fs # segment 262238384Sjkim mov %cx,%gs # registers 263238384Sjkim/* 264238384Sjkim * To real-address mode. 265238384Sjkim */ 266238384Sjkim dec %ax # Switch to 267238384Sjkim mov %eax,%cr0 # real mode 268238384Sjkim ljmp $0x0,$exit.2 # Reload CS 269238384Sjkimexit.2: xor %ax,%ax # Real mode segment 270238384Sjkim mov %ax,%ss # Reload SS 271238384Sjkim mov %ax,%ds # Address data 272238384Sjkim mov $0x1008,%bx # Set real mode 273238384Sjkim callw setpic # IRQ offsets 274238384Sjkim lidt ivtdesc # Set IVT 275238384Sjkim/* 276238384Sjkim * Reboot or await reset. 277238384Sjkim */ 278238384Sjkim sti # Enable interrupts 279238384Sjkim testb $0x1,btx_hdr+0x7 # Reboot? 280238384Sjkimexit.3: jz exit.3 # No 281238384Sjkim movb $0xa0,%al 282238384Sjkim outb %al,$0x35 283238384Sjkim movb $0x00,%al 284238384Sjkim outb %al,$0xf0 # reboot the machine 285238384Sjkimexit.4: jmp exit.4 286238384Sjkim/* 287238384Sjkim * Set IRQ offsets by reprogramming 8259A PICs. 288238384Sjkim */ 289238384Sjkimsetpic: in $0x02,%al # Save master 290238384Sjkim push %ax # IMR 291238384Sjkim in $0x0a,%al # Save slave 292238384Sjkim push %ax # IMR 293238384Sjkim movb $0x11,%al # ICW1 to 294238384Sjkim outb %al,$0x00 # master, 295238384Sjkim outb %al,$0x08 # slave 296238384Sjkim movb %bl,%al # ICW2 to 297238384Sjkim outb %al,$0x02 # master 298238384Sjkim movb %bh,%al # ICW2 to 299238384Sjkim outb %al,$0x0a # slave 300238384Sjkim movb $0x80,%al # ICW3 to 301238384Sjkim outb %al,$0x02 # master 302238384Sjkim movb $0x7,%al # ICW3 to 303238384Sjkim outb %al,$0x0a # slave 304238384Sjkim movb $0x1d,%al # ICW4 to 305238384Sjkim outb %al,$0x02 # master, 306238384Sjkim movb $0x9,%al # ICW4 to 307238384Sjkim outb %al,$0x0a # slave 308238384Sjkim pop %ax # Restore slave 309238384Sjkim outb %al,$0x0a # IMR 310238384Sjkim pop %ax # Restore master 311238384Sjkim outb %al,$0x02 # IMR 312238384Sjkim retw # To caller 313238384Sjkim .code32 314238384Sjkim/* 315238384Sjkim * Exception jump table. 316238384Sjkim */ 317238384Sjkimintx00: push $0x0 # Int 0x0: #DE 318238384Sjkim jmp ex_noc # Divide error 319238384Sjkim push $0x1 # Int 0x1: #DB 320238384Sjkim jmp ex_noc # Debug 321238384Sjkim push $0x3 # Int 0x3: #BP 322238384Sjkim jmp ex_noc # Breakpoint 323238384Sjkim push $0x4 # Int 0x4: #OF 324238384Sjkim jmp ex_noc # Overflow 325238384Sjkim push $0x5 # Int 0x5: #BR 326238384Sjkim jmp ex_noc # BOUND range exceeded 327238384Sjkim push $0x6 # Int 0x6: #UD 328238384Sjkim jmp ex_noc # Invalid opcode 329238384Sjkim push $0x7 # Int 0x7: #NM 330238384Sjkim jmp ex_noc # Device not available 331 push $0x8 # Int 0x8: #DF 332 jmp except # Double fault 333 push $0xa # Int 0xa: #TS 334 jmp except # Invalid TSS 335 push $0xb # Int 0xb: #NP 336 jmp except # Segment not present 337 push $0xc # Int 0xc: #SS 338 jmp except # Stack segment fault 339 push $0xd # Int 0xd: #GP 340 jmp except # General protection 341 push $0xe # Int 0xe: #PF 342 jmp except # Page fault 343intx10: push $0x10 # Int 0x10: #MF 344 jmp ex_noc # Floating-point error 345/* 346 * Save a zero error code. 347 */ 348ex_noc: pushl (%esp,1) # Duplicate int no 349 movb $0x0,0x4(%esp,1) # Fake error code 350/* 351 * Handle exception. 352 */ 353except: cld # String ops inc 354 pushl %ds # Save 355 pushl %es # most 356 pusha # registers 357 pushl %gs # Set GS 358 pushl %fs # Set FS 359 pushl %ds # Set DS 360 pushl %es # Set ES 361 cmpw $SEL_SCODE,0x44(%esp,1) # Supervisor mode? 362 jne except.1 # No 363 pushl %ss # Set SS 364 jmp except.2 # Join common code 365except.1: pushl 0x50(%esp,1) # Set SS 366except.2: pushl 0x50(%esp,1) # Set ESP 367 push $SEL_SDATA # Set up 368 popl %ds # to 369 pushl %ds # address 370 popl %es # data 371 movl %esp,%ebx # Stack frame 372 movl $dmpfmt,%esi # Dump format string 373 movl $MEM_BUF,%edi # Buffer 374 pushl %eax 375 pushl %edx 376wait.1: inb $0x60,%al 377 testb $0x04,%al 378 jz wait.1 379 movb $0xe0,%al 380 outb %al,$0x62 381wait.2: inb $0x60,%al 382 testb $0x01,%al 383 jz wait.2 384 xorl %edx,%edx 385 inb $0x62,%al 386 movb %al,%dl 387 inb $0x62,%al 388 movb %al,%dh 389 inb $0x62,%al 390 inb $0x62,%al 391 inb $0x62,%al 392 movl %edx,%eax 393 shlw $1,%ax 394 movl $BDA_POS,%edx 395 movw %ax,(%edx) 396 popl %edx 397 popl %eax 398 pushl %edi # Dump to 399 call dump # buffer 400 popl %esi # and 401 call putstr # display 402 leal 0x18(%esp,1),%esp # Discard frame 403 popa # Restore 404 popl %es # registers 405 popl %ds # saved 406 cmpb $0x3,(%esp,1) # Breakpoint? 407 je except.3 # Yes 408 cmpb $0x1,(%esp,1) # Debug? 409 jne except.2a # No 410 testl $PSL_T,0x10(%esp,1) # Trap flag set? 411 jnz except.3 # Yes 412except.2a: jmp exit # Exit 413except.3: leal 0x8(%esp,1),%esp # Discard err, int no 414 iret # From interrupt 415 416/* 417 * Reboot the machine by setting the reboot flag and exiting 418 */ 419reboot: orb $0x1,btx_hdr+0x7 # Set the reboot flag 420 jmp exit # Terminate BTX and reboot 421 422/* 423 * Protected Mode Hardware interrupt jump table. 424 */ 425intx20: push $0x8 # Int 0x20: IRQ0 426 jmp int_hw # V86 int 0x8 427 push $0x9 # Int 0x21: IRQ1 428 jmp int_hw # V86 int 0x9 429 push $0xa # Int 0x22: IRQ2 430 jmp int_hw # V86 int 0xa 431 push $0xb # Int 0x23: IRQ3 432 jmp int_hw # V86 int 0xb 433 push $0xc # Int 0x24: IRQ4 434 jmp int_hw # V86 int 0xc 435 push $0xd # Int 0x25: IRQ5 436 jmp int_hw # V86 int 0xd 437 push $0xe # Int 0x26: IRQ6 438 jmp int_hw # V86 int 0xe 439 push $0xf # Int 0x27: IRQ7 440 jmp int_hw # V86 int 0xf 441 push $0x10 # Int 0x28: IRQ8 442 jmp int_hw # V86 int 0x10 443 push $0x11 # Int 0x29: IRQ9 444 jmp int_hw # V86 int 0x11 445 push $0x12 # Int 0x2a: IRQ10 446 jmp int_hw # V86 int 0x12 447 push $0x13 # Int 0x2b: IRQ11 448 jmp int_hw # V86 int 0x13 449 push $0x14 # Int 0x2c: IRQ12 450 jmp int_hw # V86 int 0x14 451 push $0x15 # Int 0x2d: IRQ13 452 jmp int_hw # V86 int 0x15 453 push $0x16 # Int 0x2e: IRQ14 454 jmp int_hw # V86 int 0x16 455 push $0x17 # Int 0x2f: IRQ15 456 jmp int_hw # V86 int 0x17 457 458/* 459 * Invoke real mode interrupt/function call from user mode with arguments. 460 */ 461intx31: pushl $-1 # Dummy int no for btx_v86 462/* 463 * Invoke real mode interrupt/function call from protected mode. 464 * 465 * We place a trampoline on the user stack that will return to rret_tramp 466 * which will reenter protected mode and then finally return to the user 467 * client. 468 * 469 * Kernel frame %esi points to: Real mode stack frame at MEM_ESPR: 470 * 471 * -0x00 user %ss -0x04 kernel %esp (with full frame) 472 * -0x04 user %esp -0x08 btx_v86 pointer 473 * -0x08 user %eflags -0x0c flags (only used if interrupt) 474 * -0x0c user %cs -0x10 real mode CS:IP return trampoline 475 * -0x10 user %eip -0x12 real mode flags 476 * -0x14 int no -0x16 real mode CS:IP (target) 477 * -0x18 %eax 478 * -0x1c %ecx 479 * -0x20 %edx 480 * -0x24 %ebx 481 * -0x28 %esp 482 * -0x2c %ebp 483 * -0x30 %esi 484 * -0x34 %edi 485 * -0x38 %gs 486 * -0x3c %fs 487 * -0x40 %ds 488 * -0x44 %es 489 * -0x48 zero %eax (hardware int only) 490 * -0x4c zero %ecx (hardware int only) 491 * -0x50 zero %edx (hardware int only) 492 * -0x54 zero %ebx (hardware int only) 493 * -0x58 zero %esp (hardware int only) 494 * -0x5c zero %ebp (hardware int only) 495 * -0x60 zero %esi (hardware int only) 496 * -0x64 zero %edi (hardware int only) 497 * -0x68 zero %gs (hardware int only) 498 * -0x6c zero %fs (hardware int only) 499 * -0x70 zero %ds (hardware int only) 500 * -0x74 zero %es (hardware int only) 501 */ 502int_hw: cld # String ops inc 503 pusha # Save gp regs 504 pushl %gs # Save 505 pushl %fs # seg 506 pushl %ds # regs 507 pushl %es 508 push $SEL_SDATA # Set up 509 popl %ds # to 510 pushl %ds # address 511 popl %es # data 512 leal 0x44(%esp,1),%esi # Base of frame 513 movl %esp,MEM_ESPR-0x04 # Save kernel stack pointer 514 movl -0x14(%esi),%eax # Get Int no 515 cmpl $-1,%eax # Hardware interrupt? 516 jne intusr.1 # Yes 517/* 518 * v86 calls save the btx_v86 pointer on the real mode stack and read 519 * the address and flags from the btx_v86 structure. For interrupt 520 * handler invocations (VM86 INTx requests), disable interrupts, 521 * tracing, and alignment checking while the handler runs. 522 */ 523 movl $MEM_USR,%ebx # User base 524 movl %ebx,%edx # address 525 addl -0x4(%esi),%ebx # User ESP 526 movl (%ebx),%ebp # btx_v86 pointer 527 addl %ebp,%edx # Flatten btx_v86 ptr 528 movl %edx,MEM_ESPR-0x08 # Save btx_v86 ptr 529 movl V86_ADDR(%edx),%eax # Get int no/address 530 movl V86_CTL(%edx),%edx # Get control flags 531 movl -0x08(%esi),%ebx # Save user flags in %ebx 532 testl $V86F_ADDR,%edx # Segment:offset? 533 jnz intusr.4 # Yes 534 andl $~(PSL_I|PSL_T|PSL_AC),%ebx # Disable interrupts, tracing, 535 # and alignment checking for 536 # interrupt handler 537 jmp intusr.3 # Skip hardware interrupt 538/* 539 * Hardware interrupts store a NULL btx_v86 pointer and use the 540 * address (interrupt number) from the stack with empty flags. Also, 541 * push a dummy frame of zeros onto the stack for all the general 542 * purpose and segment registers and clear %eflags. This gives the 543 * hardware interrupt handler a clean slate. 544 */ 545intusr.1: xorl %edx,%edx # Control flags 546 movl %edx,MEM_ESPR-0x08 # NULL btx_v86 ptr 547 movl $12,%ecx # Frame is 12 dwords 548intusr.2: pushl $0x0 # Fill frame 549 loop intusr.2 # with zeros 550 movl $PSL_RESERVED_DEFAULT,%ebx # Set clean %eflags 551/* 552 * Look up real mode IDT entry for hardware interrupts and VM86 INTx 553 * requests. 554 */ 555intusr.3: shll $0x2,%eax # Scale 556 movl (%eax),%eax # Load int vector 557 jmp intusr.5 # Skip CALLF test 558/* 559 * Panic if V86F_CALLF isn't set with V86F_ADDR. 560 */ 561intusr.4: testl $V86F_CALLF,%edx # Far call? 562 jnz intusr.5 # Ok 563 movl %edx,0x30(%esp,1) # Place VM86 flags in int no 564 movl $badvm86,%esi # Display bad 565 call putstr # VM86 call 566 popl %es # Restore 567 popl %ds # seg 568 popl %fs # regs 569 popl %gs 570 popal # Restore gp regs 571 jmp ex_noc # Panic 572/* 573 * %eax now holds the segment:offset of the function. 574 * %ebx now holds the %eflags to pass to real mode. 575 * %edx now holds the V86F_* flags. 576 */ 577intusr.5: movw %bx,MEM_ESPR-0x12 # Pass user flags to real mode 578 # target 579/* 580 * If this is a v86 call, copy the seg regs out of the btx_v86 structure. 581 */ 582 movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr 583 jecxz intusr.6 # Skip for hardware ints 584 leal -0x44(%esi),%edi # %edi => kernel stack seg regs 585 pushl %esi # Save 586 leal V86_ES(%ecx),%esi # %esi => btx_v86 seg regs 587 movl $4,%ecx # Copy seg regs 588 rep # from btx_v86 589 movsl # to kernel stack 590 popl %esi # Restore 591intusr.6: movl -0x08(%esi),%ebx # Copy user flags to real 592 movl %ebx,MEM_ESPR-0x0c # mode return trampoline 593 movl $rret_tramp,%ebx # Set return trampoline 594 movl %ebx,MEM_ESPR-0x10 # CS:IP 595 movl %eax,MEM_ESPR-0x16 # Real mode target CS:IP 596 ljmpw $SEL_RCODE,$intusr.7 # Change to 16-bit segment 597 .code16 598intusr.7: movl %cr0,%eax # Leave 599 dec %al # protected 600 movl %eax,%cr0 # mode 601 ljmpw $0x0,$intusr.8 602intusr.8: xorw %ax,%ax # Reset %ds 603 movw %ax,%ds # and 604 movw %ax,%ss # %ss 605 lidt ivtdesc # Set IVT 606 popl %es # Restore 607 popl %ds # seg 608 popl %fs # regs 609 popl %gs 610 popal # Restore gp regs 611 movw $MEM_ESPR-0x16,%sp # Switch to real mode stack 612 iret # Call target routine 613/* 614 * For the return to real mode we setup a stack frame like this on the real 615 * mode stack. Note that callf calls won't pop off the flags, but we just 616 * ignore that by repositioning %sp to be just above the btx_v86 pointer 617 * so it is aligned. The stack is relative to MEM_ESPR. 618 * 619 * -0x04 kernel %esp 620 * -0x08 btx_v86 621 * -0x0c %eax 622 * -0x10 %ecx 623 * -0x14 %edx 624 * -0x18 %ebx 625 * -0x1c %esp 626 * -0x20 %ebp 627 * -0x24 %esi 628 * -0x28 %edi 629 * -0x2c %gs 630 * -0x30 %fs 631 * -0x34 %ds 632 * -0x38 %es 633 * -0x3c %eflags 634 */ 635rret_tramp: movw $MEM_ESPR-0x08,%sp # Reset stack pointer 636 pushal # Save gp regs 637 pushl %gs # Save 638 pushl %fs # seg 639 pushl %ds # regs 640 pushl %es 641 pushfl # Save %eflags 642 cli # Disable interrupts 643 std # String ops dec 644 xorw %ax,%ax # Reset seg 645 movw %ax,%ds # regs 646 movw %ax,%es # (%ss is already 0) 647 lidt idtdesc # Set IDT 648 lgdt gdtdesc # Set GDT 649 mov %cr0,%eax # Switch to protected 650 inc %ax # mode 651 mov %eax,%cr0 # 652 ljmp $SEL_SCODE,$rret_tramp.1 # To 32-bit code 653 .code32 654rret_tramp.1: xorl %ecx,%ecx # Zero 655 movb $SEL_SDATA,%cl # Setup 656 movw %cx,%ss # 32-bit 657 movw %cx,%ds # seg 658 movw %cx,%es # regs 659 movl MEM_ESPR-0x04,%esp # Switch to kernel stack 660 leal 0x44(%esp,1),%esi # Base of frame 661 andb $~0x2,tss_desc+0x5 # Clear TSS busy 662 movb $SEL_TSS,%cl # Set task 663 ltr %cx # register 664/* 665 * Now we are back in protected mode. The kernel stack frame set up 666 * before entering real mode is still intact. For hardware interrupts, 667 * leave the frame unchanged. 668 */ 669 cmpl $0,MEM_ESPR-0x08 # Leave saved regs unchanged 670 jz rret_tramp.3 # for hardware ints 671/* 672 * For V86 calls, copy the registers off of the real mode stack onto 673 * the kernel stack as we want their updated values. Also, initialize 674 * the segment registers on the kernel stack. 675 * 676 * Note that the %esp in the kernel stack after this is garbage, but popa 677 * ignores it, so we don't have to fix it up. 678 */ 679 leal -0x18(%esi),%edi # Kernel stack GP regs 680 pushl %esi # Save 681 movl $MEM_ESPR-0x0c,%esi # Real mode stack GP regs 682 movl $8,%ecx # Copy GP regs from 683 rep # real mode stack 684 movsl # to kernel stack 685 movl $SEL_UDATA,%eax # Selector for data seg regs 686 movl $4,%ecx # Initialize %ds, 687 rep # %es, %fs, and 688 stosl # %gs 689/* 690 * For V86 calls, copy the saved seg regs on the real mode stack back 691 * over to the btx_v86 structure. Also, conditionally update the 692 * saved eflags on the kernel stack based on the flags from the user. 693 */ 694 movl MEM_ESPR-0x08,%ecx # Get btx_v86 ptr 695 leal V86_GS(%ecx),%edi # %edi => btx_v86 seg regs 696 leal MEM_ESPR-0x2c,%esi # %esi => real mode seg regs 697 xchgl %ecx,%edx # Save btx_v86 ptr 698 movl $4,%ecx # Copy seg regs 699 rep # from real mode stack 700 movsl # to btx_v86 701 popl %esi # Restore 702 movl V86_CTL(%edx),%edx # Read V86 control flags 703 testl $V86F_FLAGS,%edx # User wants flags? 704 jz rret_tramp.3 # No 705 movl MEM_ESPR-0x3c,%eax # Read real mode flags 706 movw %ax,-0x08(%esi) # Update user flags (low 16) 707/* 708 * Return to the user task 709 */ 710rret_tramp.3: popl %es # Restore 711 popl %ds # seg 712 popl %fs # regs 713 popl %gs 714 popal # Restore gp regs 715 addl $4,%esp # Discard int no 716 iret # Return to user mode 717 718/* 719 * System Call. 720 */ 721intx30: cmpl $SYS_EXEC,%eax # Exec system call? 722 jne intx30.1 # No 723 pushl %ss # Set up 724 popl %es # all 725 pushl %es # segment 726 popl %ds # registers 727 pushl %ds # for the 728 popl %fs # program 729 pushl %fs # we're 730 popl %gs # invoking 731 movl $MEM_USR,%eax # User base address 732 addl 0xc(%esp,1),%eax # Change to user 733 leal 0x4(%eax),%esp # stack 734 popl %eax # Call 735 call *%eax # program 736intx30.1: orb $0x1,%ss:btx_hdr+0x7 # Flag reboot 737 jmp exit # Exit 738/* 739 * Dump structure [EBX] to [EDI], using format string [ESI]. 740 */ 741dump.0: stosb # Save char 742dump: lodsb # Load char 743 testb %al,%al # End of string? 744 jz dump.10 # Yes 745 testb $0x80,%al # Control? 746 jz dump.0 # No 747 movb %al,%ch # Save control 748 movb $'=',%al # Append 749 stosb # '=' 750 lodsb # Get offset 751 pushl %esi # Save 752 movsbl %al,%esi # To 753 addl %ebx,%esi # pointer 754 testb $DMP_X16,%ch # Dump word? 755 jz dump.1 # No 756 lodsw # Get and 757 call hex16 # dump it 758dump.1: testb $DMP_X32,%ch # Dump long? 759 jz dump.2 # No 760 lodsl # Get and 761 call hex32 # dump it 762dump.2: testb $DMP_MEM,%ch # Dump memory? 763 jz dump.8 # No 764 pushl %ds # Save 765 testl $PSL_VM,0x50(%ebx) # V86 mode? 766 jnz dump.3 # Yes 767 verr 0x4(%esi) # Readable selector? 768 jnz dump.3 # No 769 ldsl (%esi),%esi # Load pointer 770 jmp dump.4 # Join common code 771dump.3: lodsl # Set offset 772 xchgl %eax,%edx # Save 773 lodsl # Get segment 774 shll $0x4,%eax # * 0x10 775 addl %edx,%eax # + offset 776 xchgl %eax,%esi # Set pointer 777dump.4: movb $2,%dl # Num lines 778dump.4a: movb $0x10,%cl # Bytes to dump 779dump.5: lodsb # Get byte and 780 call hex8 # dump it 781 decb %cl # Keep count 782 jz dump.6a # If done 783 movb $'-',%al # Separator 784 cmpb $0x8,%cl # Half way? 785 je dump.6 # Yes 786 movb $' ',%al # Use space 787dump.6: stosb # Save separator 788 jmp dump.5 # Continue 789dump.6a: decb %dl # Keep count 790 jz dump.7 # If done 791 movb $0xa,%al # Line feed 792 stosb # Save one 793 movb $7,%cl # Leading 794 movb $' ',%al # spaces 795dump.6b: stosb # Dump 796 decb %cl # spaces 797 jnz dump.6b 798 jmp dump.4a # Next line 799dump.7: popl %ds # Restore 800dump.8: popl %esi # Restore 801 movb $0xa,%al # Line feed 802 testb $DMP_EOL,%ch # End of line? 803 jnz dump.9 # Yes 804 movb $' ',%al # Use spaces 805 stosb # Save one 806dump.9: jmp dump.0 # Continue 807dump.10: stosb # Terminate string 808 ret # To caller 809/* 810 * Convert EAX, AX, or AL to hex, saving the result to [EDI]. 811 */ 812hex32: pushl %eax # Save 813 shrl $0x10,%eax # Do upper 814 call hex16 # 16 815 popl %eax # Restore 816hex16: call hex16.1 # Do upper 8 817hex16.1: xchgb %ah,%al # Save/restore 818hex8: pushl %eax # Save 819 shrb $0x4,%al # Do upper 820 call hex8.1 # 4 821 popl %eax # Restore 822hex8.1: andb $0xf,%al # Get lower 4 823 cmpb $0xa,%al # Convert 824 sbbb $0x69,%al # to hex 825 das # digit 826 orb $0x20,%al # To lower case 827 stosb # Save char 828 ret # (Recursive) 829/* 830 * Output zero-terminated string [ESI] to the console. 831 */ 832putstr.0: call putchr # Output char 833putstr: lodsb # Load char 834 testb %al,%al # End of string? 835 jnz putstr.0 # No 836 ret # To caller 837#ifdef BTX_SERIAL 838 .set SIO_PRT,SIOPRT # Base port 839 .set SIO_FMT,SIOFMT # 8N1 840 .set SIO_DIV,(115200/SIOSPD) # 115200 / SPD 841 842/* 843 * void sio_init(void) 844 */ 845sio_init: movw $SIO_PRT+0x3,%dx # Data format reg 846 movb $SIO_FMT|0x80,%al # Set format 847 outb %al,(%dx) # and DLAB 848 pushl %edx # Save 849 subb $0x3,%dl # Divisor latch reg 850 movw $SIO_DIV,%ax # Set 851 outw %ax,(%dx) # BPS 852 popl %edx # Restore 853 movb $SIO_FMT,%al # Clear 854 outb %al,(%dx) # DLAB 855 incl %edx # Modem control reg 856 movb $0x3,%al # Set RTS, 857 outb %al,(%dx) # DTR 858 incl %edx # Line status reg 859 860/* 861 * void sio_flush(void) 862 */ 863sio_flush.0: call sio_getc.1 # Get character 864sio_flush: call sio_ischar # Check for character 865 jnz sio_flush.0 # Till none 866 ret # To caller 867 868/* 869 * void sio_putc(int c) 870 */ 871sio_putc: movw $SIO_PRT+0x5,%dx # Line status reg 872 xor %ecx,%ecx # Timeout 873 movb $0x40,%ch # counter 874sio_putc.1: inb (%dx),%al # Transmitter 875 testb $0x20,%al # buffer empty? 876 loopz sio_putc.1 # No 877 jz sio_putc.2 # If timeout 878 movb 0x4(%esp,1),%al # Get character 879 subb $0x5,%dl # Transmitter hold reg 880 outb %al,(%dx) # Write character 881sio_putc.2: ret $0x4 # To caller 882 883/* 884 * int sio_getc(void) 885 */ 886sio_getc: call sio_ischar # Character available? 887 jz sio_getc # No 888sio_getc.1: subb $0x5,%dl # Receiver buffer reg 889 inb (%dx),%al # Read character 890 ret # To caller 891 892/* 893 * int sio_ischar(void) 894 */ 895sio_ischar: movw $SIO_PRT+0x5,%dx # Line status register 896 xorl %eax,%eax # Zero 897 inb (%dx),%al # Received data 898 andb $0x1,%al # ready? 899 ret # To caller 900 901/* 902 * Output character AL to the serial console. 903 */ 904putchr: pusha # Save 905 cmpb $10, %al # is it a newline? 906 jne putchr.1 # no?, then leave 907 push $13 # output a carriage 908 call sio_putc # return first 909 movb $10, %al # restore %al 910putchr.1: pushl %eax # Push the character 911 # onto the stack 912 call sio_putc # Output the character 913 popa # Restore 914 ret # To caller 915#else 916/* 917 * Output character AL to the console. 918 */ 919putchr: pusha # Save 920 xorl %ecx,%ecx # Zero for loops 921 movb $SCR_MAT,%ah # Mode/attribute 922 movl $BDA_POS,%ebx # BDA pointer 923 movw (%ebx),%dx # Cursor position 924 movl $0xa0000,%edi 925putchr.1: cmpb $0xa,%al # New line? 926 je putchr.2 # Yes 927 movw %dx,%cx 928 movb %al,(%edi,%ecx,1) # Write char 929 addl $0x2000,%ecx 930 movb %ah,(%edi,%ecx,1) # Write attr 931 addw $0x02,%dx 932 jmp putchr.3 933putchr.2: movw %dx,%ax 934 movb $SCR_COL*2,%dl 935 div %dl 936 incb %al 937 mul %dl 938 movw %ax,%dx 939putchr.3: cmpw $SCR_ROW*SCR_COL*2,%dx 940 jb putchr.4 # No 941 leal 2*SCR_COL(%edi),%esi # New top line 942 movw $(SCR_ROW-1)*SCR_COL/2,%cx # Words to move 943 rep # Scroll 944 movsl # screen 945 movb $0x20,%al # Space 946 xorb %ah,%ah 947 movb $SCR_COL,%cl # Columns to clear 948 rep # Clear 949 stosw # line 950 movw $(SCR_ROW-1)*SCR_COL*2,%dx 951putchr.4: movw %dx,(%ebx) # Update position 952 popa # Restore 953 ret # To caller 954#endif 955 956 .code16 957/* 958 * Real Mode Hardware interrupt jump table. 959 */ 960intr20: push $0x8 # Int 0x20: IRQ0 961 jmp int_hwr # V86 int 0x8 962 push $0x9 # Int 0x21: IRQ1 963 jmp int_hwr # V86 int 0x9 964 push $0xa # Int 0x22: IRQ2 965 jmp int_hwr # V86 int 0xa 966 push $0xb # Int 0x23: IRQ3 967 jmp int_hwr # V86 int 0xb 968 push $0xc # Int 0x24: IRQ4 969 jmp int_hwr # V86 int 0xc 970 push $0xd # Int 0x25: IRQ5 971 jmp int_hwr # V86 int 0xd 972 push $0xe # Int 0x26: IRQ6 973 jmp int_hwr # V86 int 0xe 974 push $0xf # Int 0x27: IRQ7 975 jmp int_hwr # V86 int 0xf 976 push $0x10 # Int 0x28: IRQ8 977 jmp int_hwr # V86 int 0x10 978 push $0x11 # Int 0x29: IRQ9 979 jmp int_hwr # V86 int 0x11 980 push $0x12 # Int 0x2a: IRQ10 981 jmp int_hwr # V86 int 0x12 982 push $0x13 # Int 0x2b: IRQ11 983 jmp int_hwr # V86 int 0x13 984 push $0x14 # Int 0x2c: IRQ12 985 jmp int_hwr # V86 int 0x14 986 push $0x15 # Int 0x2d: IRQ13 987 jmp int_hwr # V86 int 0x15 988 push $0x16 # Int 0x2e: IRQ14 989 jmp int_hwr # V86 int 0x16 990 push $0x17 # Int 0x2f: IRQ15 991 jmp int_hwr # V86 int 0x17 992/* 993 * Reflect hardware interrupts in real mode. 994 */ 995int_hwr: push %ax # Save 996 push %ds # Save 997 push %bp # Save 998 mov %sp,%bp # Address stack frame 999 xchg %bx,6(%bp) # Swap BX, int no 1000 xor %ax,%ax # Set %ds:%bx to 1001 shl $2,%bx # point to 1002 mov %ax,%ds # IDT entry 1003 mov (%bx),%ax # Load IP 1004 mov 2(%bx),%bx # Load CS 1005 xchg %ax,4(%bp) # Swap saved %ax,%bx with 1006 xchg %bx,6(%bp) # CS:IP of handler 1007 pop %bp # Restore 1008 pop %ds # Restore 1009 lret # Jump to handler 1010 1011 .p2align 4 1012/* 1013 * Global descriptor table. 1014 */ 1015gdt: .word 0x0,0x0,0x0,0x0 # Null entry 1016 .word 0xffff,0x0,0x9a00,0xcf # SEL_SCODE 1017 .word 0xffff,0x0,0x9200,0xcf # SEL_SDATA 1018 .word 0xffff,0x0,0x9a00,0x0 # SEL_RCODE 1019 .word 0xffff,0x0,0x9200,0x0 # SEL_RDATA 1020 .word 0xffff,MEM_USR,0xfa00,0xcf# SEL_UCODE 1021 .word 0xffff,MEM_USR,0xf200,0xcf# SEL_UDATA 1022tss_desc: .word _TSSLM,MEM_TSS,0x8900,0x0 # SEL_TSS 1023gdt.1: 1024/* 1025 * Pseudo-descriptors. 1026 */ 1027gdtdesc: .word gdt.1-gdt-1,gdt,0x0 # GDT 1028idtdesc: .word _IDTLM,MEM_IDT,0x0 # IDT 1029ivtdesc: .word 0x400-0x0-1,0x0,0x0 # IVT 1030/* 1031 * IDT construction control string. 1032 */ 1033idtctl: .byte 0x10, 0x8e # Int 0x0-0xf 1034 .word 0x7dfb,intx00 # (exceptions) 1035 .byte 0x10, 0x8e # Int 0x10 1036 .word 0x1, intx10 # (exception) 1037 .byte 0x10, 0x8e # Int 0x20-0x2f 1038 .word 0xffff,intx20 # (hardware) 1039 .byte 0x1, 0xee # int 0x30 1040 .word 0x1, intx30 # (system call) 1041 .byte 0x2, 0xee # Int 0x31-0x32 1042 .word 0x1, intx31 # (V86, null) 1043 .byte 0x0 # End of string 1044/* 1045 * Dump format string. 1046 */ 1047dmpfmt: .byte '\n' # "\n" 1048 .ascii "int" # "int=" 1049 .byte 0x80|DMP_X32, 0x40 # "00000000 " 1050 .ascii "err" # "err=" 1051 .byte 0x80|DMP_X32, 0x44 # "00000000 " 1052 .ascii "efl" # "efl=" 1053 .byte 0x80|DMP_X32, 0x50 # "00000000 " 1054 .ascii "eip" # "eip=" 1055 .byte 0x80|DMP_X32|DMP_EOL,0x48 # "00000000\n" 1056 .ascii "eax" # "eax=" 1057 .byte 0x80|DMP_X32, 0x34 # "00000000 " 1058 .ascii "ebx" # "ebx=" 1059 .byte 0x80|DMP_X32, 0x28 # "00000000 " 1060 .ascii "ecx" # "ecx=" 1061 .byte 0x80|DMP_X32, 0x30 # "00000000 " 1062 .ascii "edx" # "edx=" 1063 .byte 0x80|DMP_X32|DMP_EOL,0x2c # "00000000\n" 1064 .ascii "esi" # "esi=" 1065 .byte 0x80|DMP_X32, 0x1c # "00000000 " 1066 .ascii "edi" # "edi=" 1067 .byte 0x80|DMP_X32, 0x18 # "00000000 " 1068 .ascii "ebp" # "ebp=" 1069 .byte 0x80|DMP_X32, 0x20 # "00000000 " 1070 .ascii "esp" # "esp=" 1071 .byte 0x80|DMP_X32|DMP_EOL,0x0 # "00000000\n" 1072 .ascii "cs" # "cs=" 1073 .byte 0x80|DMP_X16, 0x4c # "0000 " 1074 .ascii "ds" # "ds=" 1075 .byte 0x80|DMP_X16, 0xc # "0000 " 1076 .ascii "es" # "es=" 1077 .byte 0x80|DMP_X16, 0x8 # "0000 " 1078 .ascii " " # " " 1079 .ascii "fs" # "fs=" 1080 .byte 0x80|DMP_X16, 0x10 # "0000 " 1081 .ascii "gs" # "gs=" 1082 .byte 0x80|DMP_X16, 0x14 # "0000 " 1083 .ascii "ss" # "ss=" 1084 .byte 0x80|DMP_X16|DMP_EOL,0x4 # "0000\n" 1085 .ascii "cs:eip" # "cs:eip=" 1086 .byte 0x80|DMP_MEM|DMP_EOL,0x48 # "00 00 ... 00 00\n" 1087 .ascii "ss:esp" # "ss:esp=" 1088 .byte 0x80|DMP_MEM|DMP_EOL,0x0 # "00 00 ... 00 00\n" 1089 .asciz "BTX halted\n" # End 1090/* 1091 * Bad VM86 call panic 1092 */ 1093badvm86: .asciz "Invalid VM86 Request\n" 1094 1095/* 1096 * End of BTX memory. 1097 */ 1098 .p2align 4 1099break: 1100