Annotation of qemu/roms/ipxe/src/arch/i386/transitions/librm.S, revision 1.1.1.1

1.1       root        1: /*
                      2:  * librm: a library for interfacing to real-mode code
                      3:  *
                      4:  * Michael Brown <[email protected]>
                      5:  *
                      6:  */
                      7: 
                      8: FILE_LICENCE ( GPL2_OR_LATER )
                      9: 
                     10: /* Drag in local definitions */
                     11: #include "librm.h"
                     12: 
                     13: /* For switches to/from protected mode */
                     14: #define CR0_PE 1
                     15: 
                     16: /* Size of various C data structures */
                     17: #define SIZEOF_I386_SEG_REGS   12
                     18: #define SIZEOF_I386_REGS       32
                     19: #define SIZEOF_REAL_MODE_REGS  ( SIZEOF_I386_SEG_REGS + SIZEOF_I386_REGS )
                     20: #define SIZEOF_I386_FLAGS      4
                     21: #define SIZEOF_I386_ALL_REGS   ( SIZEOF_REAL_MODE_REGS + SIZEOF_I386_FLAGS )
                     22:        
                     23:        .arch i386
                     24: 
                     25: /****************************************************************************
                     26:  * Global descriptor table
                     27:  *
                     28:  * Call init_librm to set up the GDT before attempting to use any
                     29:  * protected-mode code.
                     30:  *
                     31:  * NOTE: This must be located before prot_to_real, otherwise gas
                     32:  * throws a "can't handle non absolute segment in `ljmp'" error due to
                     33:  * not knowing the value of REAL_CS when the ljmp is encountered.
                     34:  *
                     35:  * Note also that putting ".word gdt_end - gdt - 1" directly into
                     36:  * gdt_limit, rather than going via gdt_length, will also produce the
                     37:  * "non absolute segment" error.  This is most probably a bug in gas.
                     38:  ****************************************************************************
                     39:  */
                     40:        .section ".data16", "aw", @progbits
                     41:        .align 16
                     42: gdt:
                     43: gdtr:          /* The first GDT entry is unused, the GDTR can fit here. */
                     44: gdt_limit:             .word gdt_length - 1
                     45: gdt_base:              .long 0
                     46:                        .word 0 /* padding */
                     47: 
                     48:        .org    gdt + VIRTUAL_CS, 0
                     49: virtual_cs:    /* 32 bit protected mode code segment, virtual addresses */
                     50:        .word   0xffff, 0
                     51:        .byte   0, 0x9f, 0xcf, 0
                     52: 
                     53:        .org    gdt + VIRTUAL_DS, 0
                     54: virtual_ds:    /* 32 bit protected mode data segment, virtual addresses */
                     55:        .word   0xffff, 0
                     56:        .byte   0, 0x93, 0xcf, 0
                     57:        
                     58:        .org    gdt + PHYSICAL_CS, 0
                     59: physical_cs:   /* 32 bit protected mode code segment, physical addresses */
                     60:        .word   0xffff, 0
                     61:        .byte   0, 0x9f, 0xcf, 0
                     62: 
                     63:        .org    gdt + PHYSICAL_DS, 0
                     64: physical_ds:   /* 32 bit protected mode data segment, physical addresses */
                     65:        .word   0xffff, 0
                     66:        .byte   0, 0x93, 0xcf, 0        
                     67: 
                     68:        .org    gdt + REAL_CS, 0
                     69: real_cs:       /* 16 bit flat real mode code segment */
                     70:        .word   0xffff, 0
                     71:        .byte   0, 0x9b, 0x8f, 0
                     72: 
                     73:        .org    gdt + REAL_DS   
                     74: real_ds:       /* 16 bit flat real mode data segment */
                     75:        .word   0xffff, 0
                     76:        .byte   0, 0x93, 0x8f, 0
                     77:        
                     78: gdt_end:
                     79:        .equ    gdt_length, gdt_end - gdt
                     80: 
                     81: /****************************************************************************
                     82:  * init_librm (real-mode far call, 16-bit real-mode far return address)
                     83:  *
                     84:  * Initialise the GDT ready for transitions to protected mode.
                     85:  *
                     86:  * Parameters:
                     87:  *   %cs : .text16 segment
                     88:  *   %ds : .data16 segment
                     89:  *   %edi : Physical base of protected-mode code (virt_offset)
                     90:  ****************************************************************************
                     91:  */
                     92:        .section ".text16", "ax", @progbits
                     93:        .code16
                     94:        .globl init_librm
                     95: init_librm:
                     96:        /* Preserve registers */
                     97:        pushl   %eax
                     98:        pushl   %ebx
                     99: 
                    100:        /* Store _virt_offset and set up virtual_cs and virtual_ds segments */
                    101:        movl    %edi, %eax
                    102:        movw    $virtual_cs, %bx
                    103:        call    set_seg_base
                    104:        movw    $virtual_ds, %bx
                    105:        call    set_seg_base    
                    106:        movl    %edi, _virt_offset
                    107: 
                    108:        /* Negate virt_offset */
                    109:        negl    %edi
                    110:                
                    111:        /* Store rm_cs and _text16, set up real_cs segment */
                    112:        xorl    %eax, %eax
                    113:        movw    %cs, %ax
                    114:        movw    %ax, rm_cs
                    115:        shll    $4, %eax
                    116:        movw    $real_cs, %bx
                    117:        call    set_seg_base
                    118:        addr32 leal     (%eax, %edi), %ebx
                    119:        movl    %ebx, _text16
                    120: 
                    121:        /* Store rm_ds and _data16, set up real_ds segment */
                    122:        xorl    %eax, %eax
                    123:        movw    %ds, %ax
                    124:        movw    %ax, %cs:rm_ds
                    125:        shll    $4, %eax
                    126:        movw    $real_ds, %bx
                    127:        call    set_seg_base
                    128:        addr32 leal     (%eax, %edi), %ebx
                    129:        movl    %ebx, _data16
                    130: 
                    131:        /* Set GDT and IDT base */
                    132:        movl    %eax, gdt_base
                    133:        addl    $gdt, gdt_base
                    134:        call    idt_init
                    135: 
                    136:        /* Restore registers */
                    137:        negl    %edi
                    138:        popl    %ebx
                    139:        popl    %eax
                    140:        lret
                    141: 
                    142:        .section ".text16", "ax", @progbits
                    143:        .code16
                    144:        .weak idt_init
                    145: set_seg_base:
                    146: 1:     movw    %ax, 2(%bx)
                    147:        rorl    $16, %eax
                    148:        movb    %al, 4(%bx)
                    149:        movb    %ah, 7(%bx)
                    150:        roll    $16, %eax
                    151: idt_init: /* Reuse the return opcode here */
                    152:        ret
                    153: 
                    154: /****************************************************************************
                    155:  * real_to_prot (real-mode near call, 32-bit virtual return address)
                    156:  *
                    157:  * Switch from 16-bit real-mode to 32-bit protected mode with virtual
                    158:  * addresses.  The real-mode %ss:sp is stored in rm_ss and rm_sp, and
                    159:  * the protected-mode %esp is restored from the saved pm_esp.
                    160:  * Interrupts are disabled.  All other registers may be destroyed.
                    161:  *
                    162:  * The return address for this function should be a 32-bit virtual
                    163:  * address.
                    164:  *
                    165:  * Parameters: 
                    166:  *   %ecx : number of bytes to move from RM stack to PM stack
                    167:  *
                    168:  ****************************************************************************
                    169:  */
                    170:        .section ".text16", "ax", @progbits
                    171:        .code16
                    172: real_to_prot:
                    173:        /* Enable A20 line */
                    174:        call    enable_a20
                    175:        /* A failure at this point is fatal, and there's nothing we
                    176:         * can do about it other than lock the machine to make the
                    177:         * problem immediately visible.
                    178:         */
                    179: 1:     jc      1b
                    180: 
                    181:        /* Make sure we have our data segment available */
                    182:        movw    %cs:rm_ds, %ax
                    183:        movw    %ax, %ds
                    184: 
                    185:        /* Add _virt_offset, _text16 and _data16 to stack to be
                    186:         * copied, and also copy the return address.
                    187:         */
                    188:        pushl   _virt_offset
                    189:        pushl   _text16
                    190:        pushl   _data16
                    191:        addw    $16, %cx /* %ecx must be less than 64kB anyway */
                    192: 
                    193:        /* Real-mode %ss:%sp => %ebp:%edx and virtual address => %esi */
                    194:        xorl    %ebp, %ebp
                    195:        movw    %ss, %bp
                    196:        movzwl  %sp, %edx
                    197:        movl    %ebp, %eax
                    198:        shll    $4, %eax
                    199:        addr32 leal (%eax,%edx), %esi
                    200:        subl    _virt_offset, %esi
                    201: 
                    202:        /* Switch to protected mode */
                    203:        cli
                    204:        data32 lgdt gdtr
                    205:        data32 lidt idtr
                    206:        movl    %cr0, %eax
                    207:        orb     $CR0_PE, %al
                    208:        movl    %eax, %cr0
                    209:        data32 ljmp     $VIRTUAL_CS, $1f
                    210:        .section ".text", "ax", @progbits
                    211:        .code32
                    212: 1:
                    213:        /* Set up protected-mode data segments and stack pointer */
                    214:        movw    $VIRTUAL_DS, %ax
                    215:        movw    %ax, %ds
                    216:        movw    %ax, %es
                    217:        movw    %ax, %fs
                    218:        movw    %ax, %gs
                    219:        movw    %ax, %ss
                    220:        movl    pm_esp, %esp
                    221: 
                    222:        /* Record real-mode %ss:sp (after removal of data) */
                    223:        movw    %bp, rm_ss
                    224:        addl    %ecx, %edx
                    225:        movw    %dx, rm_sp
                    226: 
                    227:        /* Move data from RM stack to PM stack */
                    228:        subl    %ecx, %esp
                    229:        movl    %esp, %edi
                    230:        rep movsb
                    231: 
                    232:        /* Publish virt_offset, text16 and data16 for PM code to use */
                    233:        popl    data16
                    234:        popl    text16
                    235:        popl    virt_offset
                    236: 
                    237:        /* Return to virtual address */
                    238:        ret
                    239: 
                    240:        /* Default IDTR with no interrupts */
                    241:        .section ".data16", "aw", @progbits
                    242:        .weak idtr
                    243: idtr:
                    244: rm_idtr:
                    245:        .word 0xffff /* limit */
                    246:        .long 0 /* base */
                    247: 
                    248: /****************************************************************************
                    249:  * prot_to_real (protected-mode near call, 32-bit real-mode return address)
                    250:  *
                    251:  * Switch from 32-bit protected mode with virtual addresses to 16-bit
                    252:  * real mode.  The protected-mode %esp is stored in pm_esp and the
                    253:  * real-mode %ss:sp is restored from the saved rm_ss and rm_sp.  The
                    254:  * high word of the real-mode %esp is set to zero.  All real-mode data
                    255:  * segment registers are loaded from the saved rm_ds.  Interrupts are
                    256:  * *not* enabled, since we want to be able to use prot_to_real in an
                    257:  * ISR.  All other registers may be destroyed.
                    258:  *
                    259:  * The return address for this function should be a 32-bit (sic)
                    260:  * real-mode offset within .code16.
                    261:  *
                    262:  * Parameters: 
                    263:  *   %ecx : number of bytes to move from PM stack to RM stack
                    264:  *
                    265:  ****************************************************************************
                    266:  */
                    267:        .section ".text", "ax", @progbits
                    268:        .code32
                    269: prot_to_real:
                    270:        /* Add return address to data to be moved to RM stack */
                    271:        addl    $4, %ecx
                    272:        
                    273:        /* Real-mode %ss:sp => %ebp:edx and virtual address => %edi */
                    274:        movzwl  rm_ss, %ebp
                    275:        movzwl  rm_sp, %edx
                    276:        subl    %ecx, %edx
                    277:        movl    %ebp, %eax
                    278:        shll    $4, %eax
                    279:        leal    (%eax,%edx), %edi
                    280:        subl    virt_offset, %edi
                    281:        
                    282:        /* Move data from PM stack to RM stack */
                    283:        movl    %esp, %esi
                    284:        rep movsb
                    285:        
                    286:        /* Record protected-mode %esp (after removal of data) */
                    287:        movl    %esi, pm_esp
                    288: 
                    289:        /* Load real-mode segment limits */
                    290:        movw    $REAL_DS, %ax
                    291:        movw    %ax, %ds
                    292:        movw    %ax, %es
                    293:        movw    %ax, %fs
                    294:        movw    %ax, %gs
                    295:        movw    %ax, %ss
                    296:        ljmp    $REAL_CS, $1f
                    297:        .section ".text16", "ax", @progbits
                    298:        .code16
                    299: 1:
                    300:        /* Switch to real mode */
                    301:        movl    %cr0, %eax
                    302:        andb    $0!CR0_PE, %al
                    303:        movl    %eax, %cr0
                    304:        ljmp    *p2r_jump_vector
                    305: p2r_jump_target:
                    306: 
                    307:        /* Set up real-mode data segments and stack pointer */
                    308:        movw    %cs:rm_ds, %ax
                    309:        movw    %ax, %ds
                    310:        movw    %ax, %es
                    311:        movw    %ax, %fs
                    312:        movw    %ax, %gs
                    313:        movw    %bp, %ss
                    314:        movl    %edx, %esp
                    315: 
                    316:        /* Reset IDTR to the real-mode defaults */
                    317:        data32 lidt rm_idtr
                    318: 
                    319:        /* Return to real-mode address */
                    320:        data32 ret
                    321: 
                    322: 
                    323:        /* Real-mode code and data segments.  Assigned by the call to
                    324:         * init_librm.  rm_cs doubles as the segment part of the jump
                    325:         * vector used by prot_to_real.  rm_ds is located in .text16
                    326:         * rather than .data16 because code needs to be able to locate
                    327:         * the data segment.
                    328:         */
                    329:        .section ".data16", "aw", @progbits
                    330: p2r_jump_vector:
                    331:        .word   p2r_jump_target
                    332:        .globl rm_cs
                    333: rm_cs: .word 0
                    334:        .globl rm_ds
                    335:        .section ".text16.data", "aw", @progbits
                    336: rm_ds: .word 0
                    337: 
                    338: /****************************************************************************
                    339:  * prot_call (real-mode far call, 16-bit real-mode far return address)
                    340:  *
                    341:  * Call a specific C function in the protected-mode code.  The
                    342:  * prototype of the C function must be
                    343:  *   void function ( struct i386_all_regs *ix86 ); 
                    344:  * ix86 will point to a struct containing the real-mode registers
                    345:  * at entry to prot_call.  
                    346:  *
                    347:  * All registers will be preserved across prot_call(), unless the C
                    348:  * function explicitly overwrites values in ix86.  Interrupt status
                    349:  * and GDT will also be preserved.  Gate A20 will be enabled.
                    350:  *
                    351:  * Note that prot_call() does not rely on the real-mode stack
                    352:  * remaining intact in order to return, since everything relevant is
                    353:  * copied to the protected-mode stack for the duration of the call.
                    354:  * In particular, this means that a real-mode prefix can make a call
                    355:  * to main() which will return correctly even if the prefix's stack
                    356:  * gets vapourised during the Etherboot run.  (The prefix cannot rely
                    357:  * on anything else on the stack being preserved, so should move any
                    358:  * critical data to registers before calling main()).
                    359:  *
                    360:  * Parameters:
                    361:  *   function : virtual address of protected-mode function to call
                    362:  *
                    363:  * Example usage:
                    364:  *     pushl   $pxe_api_call
                    365:  *     call    prot_call
                    366:  *     addw    $4, %sp
                    367:  * to call in to the C function
                    368:  *      void pxe_api_call ( struct i386_all_regs *ix86 );
                    369:  ****************************************************************************
                    370:  */
                    371: 
                    372: #define PC_OFFSET_GDT ( 0 )
                    373: #define PC_OFFSET_IDT ( PC_OFFSET_GDT + 8 /* pad to 8 to keep alignment */ )
                    374: #define PC_OFFSET_IX86 ( PC_OFFSET_IDT + 8 /* pad to 8 to keep alignment */ )
                    375: #define PC_OFFSET_RETADDR ( PC_OFFSET_IX86 + SIZEOF_I386_ALL_REGS )
                    376: #define PC_OFFSET_FUNCTION ( PC_OFFSET_RETADDR + 4 )
                    377: #define PC_OFFSET_END ( PC_OFFSET_FUNCTION + 4 )
                    378: 
                    379:        .section ".text16", "ax", @progbits
                    380:        .code16
                    381:        .globl prot_call
                    382: prot_call:
                    383:        /* Preserve registers, flags and GDT on external RM stack */
                    384:        pushfl
                    385:        pushal
                    386:        pushw   %gs
                    387:        pushw   %fs
                    388:        pushw   %es
                    389:        pushw   %ds
                    390:        pushw   %ss
                    391:        pushw   %cs
                    392:        subw    $16, %sp
                    393:        movw    %sp, %bp
                    394:        sidt    8(%bp)
                    395:        sgdt    (%bp)
                    396: 
                    397:        /* For sanity's sake, clear the direction flag as soon as possible */
                    398:        cld
                    399: 
                    400:        /* Switch to protected mode and move register dump to PM stack */
                    401:        movl    $PC_OFFSET_END, %ecx
                    402:        pushl   $1f
                    403:        jmp     real_to_prot
                    404:        .section ".text", "ax", @progbits
                    405:        .code32
                    406: 1:
                    407:        /* Call function */
                    408:        leal    PC_OFFSET_IX86(%esp), %eax
                    409:        pushl   %eax
                    410:        call    *(PC_OFFSET_FUNCTION+4)(%esp)
                    411:        popl    %eax /* discard */
                    412: 
                    413:        /* Switch to real mode and move register dump back to RM stack */
                    414:        movl    $PC_OFFSET_END, %ecx
                    415:        pushl   $1f
                    416:        jmp     prot_to_real
                    417:        .section ".text16", "ax", @progbits
                    418:        .code16
                    419: 1:     
                    420:        /* Reload GDT and IDT, restore registers and flags and return */
                    421:        movw    %sp, %bp
                    422:        data32 lgdt (%bp)
                    423:        data32 lidt 8(%bp)
                    424:        addw    $20, %sp /* also skip %cs and %ss */
                    425:        popw    %ds
                    426:        popw    %es
                    427:        popw    %fs
                    428:        popw    %gs
                    429:        popal
                    430:        /* popal skips %esp.  We therefore want to do "movl -20(%sp),
                    431:         * %esp", but -20(%sp) is not a valid 80386 expression.
                    432:         * Fortunately, prot_to_real() zeroes the high word of %esp, so
                    433:         * we can just use -20(%esp) instead.
                    434:         */
                    435:        addr32 movl -20(%esp), %esp
                    436:        popfl
                    437:        lret
                    438: 
                    439: /****************************************************************************
                    440:  * real_call (protected-mode near call, 32-bit virtual return address)
                    441:  *
                    442:  * Call a real-mode function from protected-mode code.
                    443:  *
                    444:  * The non-segment register values will be passed directly to the
                    445:  * real-mode code.  The segment registers will be set as per
                    446:  * prot_to_real.  The non-segment register values set by the real-mode
                    447:  * function will be passed back to the protected-mode caller.  A
                    448:  * result of this is that this routine cannot be called directly from
                    449:  * C code, since it clobbers registers that the C ABI expects the
                    450:  * callee to preserve.
                    451:  *
                    452:  * librm.h defines a convenient macro REAL_CODE() for using real_call.
                    453:  * See librm.h and realmode.h for details and examples.
                    454:  *
                    455:  * Parameters:
                    456:  *   (32-bit) near pointer to real-mode function to call
                    457:  *
                    458:  * Returns: none
                    459:  ****************************************************************************
                    460:  */
                    461: 
                    462: #define RC_OFFSET_PRESERVE_REGS ( 0 )
                    463: #define RC_OFFSET_RETADDR ( RC_OFFSET_PRESERVE_REGS + SIZEOF_I386_REGS )
                    464: #define RC_OFFSET_FUNCTION ( RC_OFFSET_RETADDR + 4 )
                    465: #define RC_OFFSET_END ( RC_OFFSET_FUNCTION + 4 )
                    466: 
                    467:        .section ".text", "ax", @progbits
                    468:        .code32
                    469:        .globl real_call
                    470: real_call:
                    471:        /* Create register dump and function pointer copy on PM stack */
                    472:        pushal
                    473:        pushl   RC_OFFSET_FUNCTION(%esp)
                    474: 
                    475:        /* Switch to real mode and move register dump to RM stack  */
                    476:        movl    $( RC_OFFSET_RETADDR + 4 /* function pointer copy */ ), %ecx
                    477:        pushl   $1f
                    478:        jmp     prot_to_real
                    479:        .section ".text16", "ax", @progbits
                    480:        .code16
                    481: 1:
                    482:        /* Call real-mode function */
                    483:        popl    rc_function
                    484:        popal
                    485:        call    *rc_function
                    486:        pushal
                    487: 
                    488:        /* For sanity's sake, clear the direction flag as soon as possible */
                    489:        cld
                    490: 
                    491:        /* Switch to protected mode and move register dump back to PM stack */
                    492:        movl    $RC_OFFSET_RETADDR, %ecx
                    493:        pushl   $1f
                    494:        jmp     real_to_prot
                    495:        .section ".text", "ax", @progbits
                    496:        .code32
                    497: 1:
                    498:        /* Restore registers and return */
                    499:        popal
                    500:        ret
                    501: 
                    502: 
                    503:        /* Function vector, used because "call xx(%sp)" is not a valid
                    504:         * 16-bit expression.
                    505:         */
                    506:        .section ".data16", "aw", @progbits
                    507: rc_function:   .word 0, 0
                    508: 
                    509: /****************************************************************************
                    510:  * Stored real-mode and protected-mode stack pointers
                    511:  *
                    512:  * The real-mode stack pointer is stored here whenever real_to_prot
                    513:  * is called and restored whenever prot_to_real is called.  The
                    514:  * converse happens for the protected-mode stack pointer.
                    515:  *
                    516:  * Despite initial appearances this scheme is, in fact re-entrant,
                    517:  * because program flow dictates that we always return via the point
                    518:  * we left by.  For example:
                    519:  *    PXE API call entry
                    520:  *  1   real => prot
                    521:  *        ...
                    522:  *        Print a text string
                    523:  *         ...
                    524:  *  2       prot => real
                    525:  *            INT 10
                    526:  *  3       real => prot
                    527:  *         ...
                    528:  *        ...
                    529:  *  4   prot => real
                    530:  *    PXE API call exit
                    531:  *
                    532:  * At point 1, the RM mode stack value, say RPXE, is stored in
                    533:  * rm_ss,sp.  We want this value to still be present in rm_ss,sp when
                    534:  * we reach point 4.
                    535:  *
                    536:  * At point 2, the RM stack value is restored from RPXE.  At point 3,
                    537:  * the RM stack value is again stored in rm_ss,sp.  This *does*
                    538:  * overwrite the RPXE that we have stored there, but it's the same
                    539:  * value, since the code between points 2 and 3 has managed to return
                    540:  * to us.
                    541:  ****************************************************************************
                    542:  */
                    543:        .section ".data", "aw", @progbits
                    544:        .globl rm_sp
                    545: rm_sp: .word 0
                    546:        .globl rm_ss
                    547: rm_ss: .word 0
                    548: pm_esp:        .long _estack
                    549: 
                    550: /****************************************************************************
                    551:  * Virtual address offsets
                    552:  *
                    553:  * These are used by the protected-mode code to map between virtual
                    554:  * and physical addresses, and to access variables in the .text16 or
                    555:  * .data16 segments.
                    556:  ****************************************************************************
                    557:  */
                    558:        /* Internal copies, created by init_librm (which runs in real mode) */
                    559:        .section ".data16", "aw", @progbits
                    560: _virt_offset:  .long 0
                    561: _text16:       .long 0
                    562: _data16:       .long 0
                    563: 
                    564:        /* Externally-visible copies, created by real_to_prot */
                    565:        .section ".data", "aw", @progbits
                    566:        .globl virt_offset
                    567: virt_offset:   .long 0 
                    568:        .globl text16
                    569: text16:                .long 0
                    570:        .globl data16
                    571: data16:                .long 0

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.