root/sys/arch/i386/stand/dosboot/start_dos.S
/*      $NetBSD: start_dos.S,v 1.2 2024/09/11 20:15:36 andvar Exp $     */
        
/*
 * startup for DOS .COM programs
 * with input from:
 * netbsd:sys/arch/i386/boot/start.S
 * Tor Egge's patches for NetBSD boot (pr port-i386/1002)
 * freebsd:sys/i386/boot/netboot/start2.S
 */

/*
 * Ported to boot 386BSD by Julian Elischer (julian@tfs.com) Sept 1992
 *
 * Mach Operating System
 * Copyright (c) 1992, 1991 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */

/*
  Copyright 1988, 1989, 1990, 1991, 1992 
   by Intel Corporation, Santa Clara, California.

                All Rights Reserved

Permission to use, copy, modify, and distribute this software and
its documentation for any purpose and without fee is hereby
granted, provided that the above copyright notice appears in all
copies and that both the copyright notice and this permission notice
appear in supporting documentation, and that the name of Intel
not be used in advertising or publicity pertaining to distribution
of the software without specific, written prior permission.

INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

#include <machine/asm.h>


CR0_PE          =       0x1

        .data
        .globl _C_LABEL(ourseg)
_C_LABEL(ourseg):
        .long   0

/**************************************************************************
GLOBAL DESCRIPTOR TABLE
**************************************************************************/
#ifdef __ELF__
        .align  16
#else
        .align  4
#endif
gdt:
        .word   0, 0
        .byte   0, 0x00, 0x00, 0

#ifdef SUPPORT_LINUX    /* additional dummy */
        .word   0, 0
        .byte   0, 0x00, 0x00, 0
#endif

        /* kernel code segment */
        .globl flatcodeseg
flatcodeseg = . - gdt
        .word   0xffff, 0
        .byte   0, 0x9f, 0xcf, 0

        /* kernel data segment */
        .globl flatdataseg
flatdataseg = . - gdt
        .word   0xffff, 0
        .byte   0, 0x93, 0xcf, 0

        /* boot code segment, base will be patched */
bootcodeseg = . - gdt
        .word   0xffff, 0
        .byte   0, 0x9e, 0x40, 0

        /* boot data segment, base will be patched */
bootdataseg = . - gdt
#ifdef HEAP_BELOW_64K
        .word   0xffff, 0
        .byte   0, 0x92, 0x00, 0
#else
        .word   0xffff, 0
        .byte   0, 0x92, 0x4f, 0
#endif

        /* 16 bit real mode, base will be patched */
bootrealseg = . - gdt
        .word   0xffff, 0
        .byte   0, 0x9e, 0x00, 0

        /* limits (etc) for data segment in real mode */
bootrealdata = . - gdt
        .word   0xffff, 0
        .byte   0, 0x92, 0x00, 0
gdtlen = . - gdt

#ifdef __ELF__
        .align  16
#else
        .align  4
#endif
gdtarg:
        .word   gdtlen-1                /* limit */
        .long   0                       /* addr, will be inserted */

        .text
ENTRY(start)
        .code16

        # Check we are in real mode
        movl    %cr0, %eax
        testl   $CR0_PE, %eax
        jz      2f
        mov     $1f, %si
        call    message
        ret
1:      .asciz  "must be in real mode\r\n"
2:

        xorl    %eax, %eax
        mov     %cs, %ax
        mov     %ax, %ds
        mov     %ax, %es
        movl    %eax, _C_LABEL(ourseg)
#ifdef STACK_START
        add     $STACK_START / 16, %ax
        mov     %ax, %ss
        mov     $0xfffc, %sp
#endif

        /* fix up GDT entries for bootstrap */
#define FIXUP(gdt_index) \
        movw    %ax, gdt+gdt_index+2;   \
        movb    %bl, gdt+gdt_index+4

        mov     %cs, %ax
        shll    $4, %eax
        shldl   $16, %eax, %ebx
        FIXUP(bootcodeseg)
        FIXUP(bootrealseg)
        FIXUP(bootdataseg)

        /* fix up GDT pointer */
        addl    $gdt, %eax
        movl    %eax, gdtarg+2

        /* change to protected mode */
        calll   _C_LABEL(real_to_prot)
        .code32

        /* clear the bss */
        movl    $_C_LABEL(edata), %edi
        movl    $_C_LABEL(end), %ecx
        subl    %edi, %ecx
        xorb    %al, %al
        rep
        stosb

        call    _C_LABEL(doscommain)
ENTRY(_rtt)
        call    _C_LABEL(prot_to_real)
        .code16
ENTRY(exit16)
        sti
        movb    $0x4c, %ah              /* return */
        int     $0x21

/*
 * real_to_prot()
 *      transfer from real mode to protected mode.
 */
ENTRY(real_to_prot)
        .code16
        pushl   %eax
        # guarantee that interrupt is disabled when in prot mode
        cli

        # load the gdtr
        lgdtl   %cs:gdtarg

        # set the PE bit of CR0
        movl    %cr0, %eax
        orl     $CR0_PE, %eax
        movl    %eax, %cr0 

        # make intrasegment jump to flush the processor pipeline and
        # reload CS register
        ljmp    $bootcodeseg, $xprot

xprot:
        .code32
        # we are in USE32 mode now
        # set up the protected mode segment registers : DS, SS, ES
        movl    $bootdataseg, %eax
        mov     %ax, %ds
        mov     %ax, %es
        mov     %ax, %ss
#ifdef STACK_START
        addl    $STACK_START, %esp
#endif

        popl    %eax
        ret

/*
 * prot_to_real()
 *      transfer from protected mode to real mode
 */
ENTRY(prot_to_real)
        .code32
        pushl   %eax
        # set up a dummy stack frame for the second seg change.
        # Adjust the intersegment jump instruction following 
        # the clearing of protected mode bit.
        # This is self-modifying code, but we need a writable
        # code segment, and an intersegment return does not give us that.

        movl    _C_LABEL(ourseg), %eax
        movw    %ax, xreal-2

        /*
         * Load the segment registers while still in protected mode.
         * Otherwise the control bits don't get changed.
         * The correct values are loaded later.
         */
        movw    $bootrealdata, %ax
        movw    %ax, %ds
        movw    %ax, %es
        movw    %ax, %ss

        # Change to use16 mode.
        ljmp    $bootrealseg, $x16

x16:
        .code16
        # clear the PE bit of CR0
        movl    %cr0, %eax
        andl    $~CR0_PE, %eax
        movl    %eax, %cr0

        # Here we have an 16 bits intersegment jump.
        ljmp    $0, $xreal              /* segment patched above */

xreal:
        # we are in real mode now
        # set up the real mode segment registers : DS, SS, ES
        mov     %cs, %ax
        mov     %ax, %ds
        mov     %ax, %es
#ifdef STACK_START
        add     $STACK_START / 16, %ax
        mov     %ax, %ss
        subl    $STACK_START, %esp
#else
        mov     %ax, %ss
#endif
        push    %bp
        movw    %sp, %bp
        /* check we are returning to an address below 64k */
        movw    2/*bp*/ + 4/*eax*/ + 2(%bp), %ax        /* high bits ret addr */
        test    %ax, %ax
        jne     1f
        pop     %bp

        sti
        popl    %eax
        retl

1:      movw    $2f, %si
        call    message
        movl    2/*bp*/ + 4/*eax*/(%bp), %eax           /*  return address */
        call    dump_eax
        jmp     exit16
2:      .asciz  "prot_to_real can't return to "


/**************************************************************************
___MAIN - Dummy to keep GCC happy
**************************************************************************/
ENTRY(__main)
        ret

/*
 * pbzero(dst, cnt)
 *      where dst is a physical address and cnt is the length
 */
ENTRY(pbzero)
        .code32
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %es
        pushl   %edi

        cld

        # set %es to point at the flat segment
        movl    $flatdataseg, %eax
        mov     %ax, %es

        movl    8(%ebp), %edi           # destination
        movl    12(%ebp), %ecx          # count
        xorl    %eax, %eax              # value

        rep
        stosb

        popl    %edi
        popl    %es
        popl    %ebp
        ret

/*
 * vpbcopy(src, dst, cnt)
 *      where src is a virtual address and dst is a physical address
 */
ENTRY(vpbcopy)
        .code32
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %es
        pushl   %esi
        pushl   %edi

        cld

        # set %es to point at the flat segment
        movl    $flatdataseg, %eax
        mov     %ax, %es

        movl    8(%ebp), %esi           # source
        movl    12(%ebp), %edi          # destination
        movl    16(%ebp), %ecx          # count

        rep
        movsb

        popl    %edi
        popl    %esi
        popl    %es
        popl    %ebp
        ret

/*
 * pvbcopy(src, dst, cnt)
 *      where src is a physical address and dst is a virtual address
 */
ENTRY(pvbcopy)
        .code32
        pushl   %ebp
        movl    %esp, %ebp
        pushl   %ds
        pushl   %esi
        pushl   %edi

        cld

        # set %ds to point at the flat segment
        movl    $flatdataseg, %eax
        mov     %ax, %ds

        movl    8(%ebp), %esi           # source
        movl    12(%ebp), %edi          # destination
        movl    16(%ebp), %ecx          # count

        rep
        movsb

        popl    %edi
        popl    %esi
        popl    %ds
        popl    %ebp
        ret

ENTRY(vtophys)
        .code32
        movl    _C_LABEL(ourseg), %eax
        shll    $4, %eax
        addl    4(%esp), %eax
        ret

message:
        .code16
        pushal
message_1:
        cld
1:      lodsb
        testb   %al, %al
        jz      2f
        movb    $0xe, %ah
        movw    $1, %bx
        int     $0x10
        jmp     1b

2:      movb    $0x86, %ah
        mov     $16, %cx
        int     $0x15                   /* delay about a second */
        popal
        ret

/* These are useful for debugging
 */
        .data
eax_buf:
        .long   0, 0, 0, 0
        .text
ENTRY(dump_eax)
        .code16
        pushal
        movw    $eax_buf, %si
        mov     %si, %di
        movw    $8, %cx
1:      roll    $4, %eax
        mov     %ax, %bx
        andb    $0x0f, %al
        addb    $0x30, %al                      /* 30..3f - clear AF */
#if 1 /* 5 bytes to generate real hex... */
        daa                                     /* 30..39, 40..45 */
        addb    $0xc0, %al                      /* f0..f9, 00..05 */
        adcb    $0x40, %al                      /* 30..39, 41..45 */
#endif
        movb    %al, (%di)                      /* %es != %ds, so can't ... */
        inc     %di                             /* ... use stosb */
        mov     %bx, %ax
        loop    1b
        movw    $0x20, %ax                      /* space + null */
        movw    %ax, (%di)
        jmp     message_1

        .globl  _C_LABEL(trace_word)
_C_LABEL(trace_word):
        .code32
        movl    4(%esp), %edx

        call    _C_LABEL(prot_to_real)
        .code16
        movl    %edx, %eax
        call    dump_eax
        calll   _C_LABEL(real_to_prot)
        .code32
        ret

        .globl  _C_LABEL(trace_str)
_C_LABEL(trace_str):
        .code32
        pushl   %esi

        call    _C_LABEL(prot_to_real)
        .code16
        mov     %sp, %si
        mov     8(%si), %si
        call    message
        calll   _C_LABEL(real_to_prot)
        .code32
        popl    %esi
        ret