Goto sanos source index

;
; ldrinit.asm
;
; OS loader real mode startup
;
; Copyright (C) 2002 Michael Ringgaard. All rights reserved.
;
; Redistribution and use in source and binary forms, with or without
; modification, are permitted provided that the following conditions
; are met:
; 
; 1. Redistributions of source code must retain the above copyright 
;    notice, this list of conditions and the following disclaimer.  
; 2. Redistributions in binary form must reproduce the above copyright
;    notice, this list of conditions and the following disclaimer in the
;    documentation and/or other materials provided with the distribution.  
; 3. Neither the name of the project nor the names of its contributors
;    may be used to endorse or promote products derived from this software
;    without specific prior written permission. 
; 
; THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
; ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
; IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
; ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
; FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
; DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
; OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
; HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
; LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
; OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
; SUCH DAMAGE.
; 

;
; The ldrinit is called by the bootstrap (bootsector) after 
; the os loader has been loaded. The ldrinit uses a simplified 
; DOS .EXE format. It is attached to osldr.dll as the DOS stub
; for the DLL.
;

; The os loader is loaded at the fixed address 0x90000. 
; This allows for 56K for the loader, data area and stack.

OSLDRSEG    equ 0x9000
OSLDRBASE   equ (OSLDRSEG * 16)

; Put the stack 8K below the 640K border to allow room for
; the Extended BIOS Data Area

STACKTOP    equ 0x9e000

        BITS    16
        SECTION .text

;
; EXE file header
;

textstart:

header_start:
        db      0x4d, 0x5a          ; EXE file signature
        dw      textsize % 512
        dw      (textsize + 511) / 512
        dw      0                   ; Relocation information: none
        dw      (header_end - header_start) / 16 ; Header size in paragraphs
        dw      0                   ; Min extra mem
        dw      0xffff              ; Max extra mem
        dw      0                   ; Initial SS (before fixup)
        dw      0                   ; Initial SP 
        dw      0                   ; (no) Checksum
        dw      start               ; Initial IP
        dw      0                   ; Initial CS (before fixup)
        dw      header_end          ; File offset to relocation table: none
        dw      krnlopts            ; Overlay number (offset for kernel options)
        align   64, db 0
header_end:

;
; Entry point from bootstrap
;

start:
        ; Setup initial environment
        mov     ax, cs
        mov     ds, ax
        mov     es, ax

        ; Save boot drive and boot image
        mov     [bootdrv], dl
        mov     [bootimg], ebx

        ; Display boot message
        mov     si, osldrmsg
        call    print

        ; Try to get system memory map from BIOS
        call    getmemmap

        ; Check for APM BIOS
        call    apmbioscheck

        ; Move system into 32-bit protected mode
        cli                         ; no interrupts allowed

        ; Enable A20
        call    empty8042
        mov     al, 0xd1            ; command write
        out     0x64, al
        call    empty8042
        mov     al,0xdf             ; A20 on
        out     0x60, al
        call    empty8042

        ; Load idt and gdt
        lidt    [idtsel]            ; load idt with 0,0
        lgdt    [gdtsel]            ; load gdt with whatever appropriate

        ; Switch to protected mode
        mov     ax, 0x0001
        lmsw    ax

        ; Initialize segment registers
        mov     ax, flat_data - gdt
        mov     ds, ax
        mov     es, ax
        mov     fs, ax
        mov     gs, ax
        mov     ss, ax

        ; Set code segment and clear prefetch
        jmp     dword (flat_code - gdt):start32 + OSLDRBASE

start32:
        BITS    32
        ; Setup stack
        mov     esp, STACKTOP
        
        ; Add room for protected mode stack pointer
        push    esp

        ; Clear flags
        push    dword 2
        popfd

        ; Calculate entrypoint
        mov     eax, [OSLDRBASE + 0x3c]
        mov     eax, [eax + OSLDRBASE + 0x28]
        add     eax, OSLDRBASE

        ; Push os loader load address and boot parameter block
        push    dword 0
        push    dword OSLDRBASE + bootparams
        push    dword OSLDRBASE

        ; Call startup code in os loader
        call    eax

        ; We never return here
        cli
        hlt

        BITS    16

;
; Print string to console
; si = ptr to first character of a null terminated string
;

print:
        push    ax
        cld
nextchar:
        mov     al, [si]
        cmp     al, 0
        je      printdone
        call    printchar
        inc     si
        jmp     nextchar
printdone:
        pop     ax
        ret

;
; Print a single character to the console
;   al = character to be printed
;

printchar:
        mov     ah, 0x0e
        int     0x10
        ret

;
; Empty keyboard command queue
;

empty8042:
        call    delay
        in      al,0x64         ; 8042 status port
        test    al,2            ; is input buffer full?
        jnz     empty8042       ; yes - loop
        ret

        ; Delay is needed after doing i/o
delay:
        dw      0x00eb, 0x00eb
        ret

;
; Check for APM BIOS
;

apmbioscheck:
        mov     ax, 0x5300          ; APM BIOS installation check
        xor     bx, bx
        int     0x15
        jc      apmdone

        cmp     bx, 0x504d          ; Check for "PM" signature
        jne     apmdone

        and     cx, 0x02            ; Is 32-bit supported?
        jz      apmdone
        
        mov     [apmversion], ax    ; Record the APM BIOS version
        mov     [apmflags], cx      ; and flags

        mov     ax, 0x5304          ; Disconnect first just in case
        xor     bx, bx
        int     0x15                ; Ignore return code

        mov     ax, 0x5303          ; APM BIOS 32-bit Connect
        xor     ebx, ebx
        xor     cx, cx
        xor     dx, dx
        xor     esi, esi
        xor     di, di
        int     0x15
        jc      no32apmbios
        
        mov     [apmcseg32], ax
        mov     [apmentry], ebx
        mov     [apmcseg16], cx
        mov     [apmdseg], dx
        mov     [apmcseg32len], si
        shr     esi, 16
        mov     [apmcseg16len], si
        mov     [apmdseglen], di
        
        mov     ax, 0x5300          ; Redo APM BIOS installation check
        xor     bx, bx
        xor     cx, cx
        int     0x15
        jc      apmdisconnect

;       cmp     bx, 0x504d          ; Check for "PM" signature
;       jne     apmdisconnect

;       mov     [apmversion], ax    ; Record the APM BIOS version
;       mov     [apmflags], cx      ; and flags

        jmp     apmdone

apmdisconnect:
        mov     ax, 0x5304          ; Disconnect
        xor     bx, bx
        int     0x15
        jmp     apmdone
        
no32apmbios:
        mov     cx, 0xfffd          ; Remove 32 bit support bit
        and     [apmflags], cx

apmdone:
        ret

;
; Get memory map from BIOS
;

SMAP       equ 0x534d4150
MAXMEMRECS equ 32
MEMRECSIZ  equ 20
   
getmemmap:
        ; Get memory map using int 15 ax=0xe80
        xor     ebx, ebx            ; Continuation value
        mov     di, memrecs         ; Pointer to memmap

e820loop:
        ; Get next memory map entry
        mov     eax, 0x0000e820     ; eax = E820
        mov     edx, SMAP           ; edx = ASCII 'SMAP'
        mov     ecx, MEMRECSIZ      ; ecx = Size of the mementry
        push    ds                  ; es:di = Address of memory map
        pop     es
        int     0x15
        jc      e820fail

        cmp     eax, SMAP           ; Check the return is `SMAP'
        jne     e820fail

        ; Save new memory entry
        mov     eax, [nrmemrecs]    ; Check for max number of memory record
        cmp     eax, MAXMEMRECS
        jnl     e820fail

        inc     eax                 ; Move forward to next record in memory map
        mov     [nrmemrecs], eax
        add     di, MEMRECSIZ
        
        ; Check for more memory records
        cmp     ebx, 0
        jne     e820loop

        ret

e820fail:
        xor     eax, eax            ; Reset memory map
        mov     [nrmemrecs], eax
        ret

;
; Global descriptor table
;

D_DATA          equ     0x1000
D_CODE          equ     0x1800

D_WRITE         equ     0x200
D_READ          equ     0x200
D_CONFORM       equ     0x400

D_BIG           equ     0x40
D_BIG_LIM       equ     0x80

D_PRESENT       equ     0x8000

%macro segdesc 3
        dw      (%2 & 0xffff)
        dw      (%1 & 0xffff)
        db      (%1) >> 16
        db      ((%3) | D_PRESENT) >> 8
        db      ((%3) & 0xff) | ((%2) >> 16)
        db      (%1) >> 24
%endmacro

idtsel:
        dw      0               ; idt limit = 0
        dw      0,0             ; idt base = 0L

gdtsel:
        dw      gdtlen
        dd      gdt + OSLDRBASE

        align   8
gdt:

null_desc       segdesc 0,0,0
flat_code       segdesc 0, 0x0fffff, D_CODE | D_READ | D_BIG | D_BIG_LIM
flat_data       segdesc 0, 0x0fffff, D_DATA | D_WRITE | D_BIG | D_BIG_LIM
real_code       segdesc OSLDRBASE, 0x0ffff, D_CODE | D_READ | D_CONFORM
real_data       segdesc OSLDRBASE, 0x0ffff, D_DATA | D_WRITE

gdtlen          equ $ - gdt - 1

;
; Boot Parameter Block
;

bootparams:

bootdrv      dd    0        ; Boot drive
bootimg      dd    0        ; RAM disk image address

krnlopts     times 128 db 0 ; Kernel options

apmversion   dw    0        ; APM version (BCD format)
apmflags     dw    0        ; APM flags from install check
apmcseg32    dw    0        ; APM 32-bit code segment (real mode segment base address)
apmentry     dw    0        ; Offset of the entry point into the APM BIOS
apmcseg16    dw    0        ; APM 16-bit code segment (real mode segment base address)
apmdseg      dw    0        ; APM data segment (real mode segment base address)
apmcseg32len dw    0        ; APM BIOS 32-bit code segment length
apmcseg16len dw    0        ; APM BIOS 16-bit code segment length
apmdseglen   dw    0        ; APM BIOS data segment length

nrmemrecs    dd    0        ; System memory map
memrecs      times (MAXMEMRECS * MEMRECSIZ) db 0

;
; Strings
;

osldrmsg:    db    ', ldrinit', 0

textend:

textsize equ (textend - textstart)