Annotation of qemu/roms/ipxe/src/arch/i386/transitions/librm.S, revision 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.