#ifndef CYGONCE_HAL_PCMB_INC #define CYGONCE_HAL_PCMB_INC ##============================================================================= ## ## pcmb.inc ## ## PC platform support ## ##============================================================================= #####ECOSGPLCOPYRIGHTBEGIN#### ## ------------------------------------------- ## This file is part of eCos, the Embedded Configurable Operating System. ## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. ## ## eCos is free software; you can redistribute it and/or modify it under ## the terms of the GNU General Public License as published by the Free ## Software Foundation; either version 2 or (at your option) any later version. ## ## eCos is distributed in the hope that it will be useful, but WITHOUT ANY ## WARRANTY; without even the implied warranty of MERCHANTABILITY or ## FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License ## for more details. ## ## You should have received a copy of the GNU General Public License along ## with eCos; if not, write to the Free Software Foundation, Inc., ## 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. ## ## As a special exception, if other files instantiate templates or use macros ## or inline functions from this file, or you compile this file and link it ## with other works to produce a work based on this file, this file does not ## by itself cause the resulting work to be covered by the GNU General Public ## License. However the source code for this file must still be made available ## in accordance with section (3) of the GNU General Public License. ## ## This exception does not invalidate any other reasons why a work based on ## this file might be covered by the GNU General Public License. ## ## Alternative licenses for eCos may be arranged by contacting Red Hat, Inc. ## at http://sources.redhat.com/ecos/ecos-license/ ## ------------------------------------------- #####ECOSGPLCOPYRIGHTEND#### ##============================================================================= #######DESCRIPTIONBEGIN#### ## ## Author(s): jskov ## Contributors:jskov, pjo, nickg ## Date: 1999-01-07 ## Purpose: PC platform support ## Description: This file contains any PC specific assembler macros needed to ## run eCos on a standard i386 PC. ## ## ######DESCRIPTIONEND#### ## ##============================================================================= ##============================================================================= ## CPU initialization #ifndef CYGPKG_HAL_I386_CPU_INIT_DEFINED #ifdef CYG_HAL_STARTUP_FLOPPY #define CYGPKG_HAL_I386_CPU_INIT_DEFINED .macro hal_cpu_init /* This code is loaded from a floppy disk when the PC powers up. */ .code16 .extern _end sectorsPerTrack = 18 bytesPerSector = 512 esPerSector = 32 /* = 512/16 */ cld /* always count up. */ /* Configure a stack that we can use. */ movl $_start, %eax movw %ax, %sp shr $4, %eax andl $0xF000, %eax movw %ax, %ss # Get memory size (extended mem, kB) movw $(0x0E*256+'M'), %ax /* print a 2 */ int $0x10 #define E820MAP 0x2d0 /* our map */ #define E820MAX 32 /* number of entries in E820MAP */ #define E820NR 0x1e8 /* # entries in E820MAP */ xorl %eax, %eax movl %eax, (0x1e0) movb %al, (E820NR) # Try three different memory detection schemes. First, try # e820h, which lets us assemble a memory map, then try e801h, # which returns a 32-bit memory size, and finally 88h, which # returns 0-64m # method E820H: # the memory map from hell. e820h returns memory classified into # a whole bunch of different types, and allows memory holes and # everything. We scan through this memory map and build a list # of the first 32 memory areas, which we return at [E820MAP]. # This is documented at http://www.teleport.com/~acpi/acpihtml/topic245.htm #define SMAP 0x534d4150 meme820: xorl %ebx, %ebx # continuation counter movw $E820MAP, %di # point into the whitelist # so we can have the bios # directly write into it. jmpe820: movl $0x0000e820, %eax # e820, upper word zeroed movl $SMAP, %edx # ascii 'SMAP' movl $20, %ecx # size of the e820rec pushw %ds # data record. popw %es int $0x15 # make the call jc bail820 # fall to e801 if it fails cmpl $SMAP, %eax # check the return is `SMAP' jne bail820 # fall to e801 if it fails # cmpl $1, 16(%di) # is this usable memory? # jne again820 # If this is usable memory, we save it by simply advancing %di by # sizeof(e820rec). # good820: movb (E820NR), %al # up to 32 entries cmpb $E820MAX, %al jnl bail820 incb (E820NR) movw %di, %ax addw $20, %ax movw %ax, %di movw $(0x0E*256+'G'), %ax /* print a G */ int $0x10 again820: cmpl $0, %ebx # check to see if jne jmpe820 # %ebx is set to EOF bail820: movw $(0x0E*256+'B'), %ax /* print a B */ int $0x10 # method E801H: # memory size is in 1k chunksizes, to avoid confusing loadlin. # we store the 0xe801 memory size in a completely different place, # because it will most likely be longer than 16 bits. # (use 1e0 because that's what Larry Augustine uses in his # alternative new memory detection scheme, and it's sensible # to write everything into the same place.) meme801: stc # fix to work around buggy xorw %cx,%cx # BIOSes which dont clear/set xorw %dx,%dx # carry on pass/error of # e801h memory size call # or merely pass cx,dx though # without changing them. movw $0xe801, %ax int $0x15 jc mem88 cmpw $0x0, %cx # Kludge to handle BIOSes jne e801usecxdx # which report their extended cmpw $0x0, %dx # memory in AX/BX rather than jne e801usecxdx # CX/DX. The spec I have read movw %ax, %cx # seems to indicate AX/BX movw %bx, %dx # are more reasonable anyway... e801usecxdx: andl $0xffff, %edx # clear sign extend shll $6, %edx # and go from 64k to 1k chunks movl %edx, (0x1e0) # store extended memory size andl $0xffff, %ecx # clear sign extend addl %ecx, (0x1e0) # and add lower memory into # total size. movw $(0x0E*256+'1'), %ax /* print a 1 */ int $0x10 # Ye Olde Traditional Methode. Returns the memory size (up to 16mb or # 64mb, depending on the bios) in ax. mem88: /* Ask the BIOS for info about the amount of RAM available. We push * these onto the stack for later use. */ xorl %eax, %eax movb $0x88, %ah /* Get the amount of extended memory. */ int $0x15 shl $10, %eax pushl %eax xorl %eax, %eax int $0x12 /* Get the amount of standard memory. */ shl $10, %eax pushl %eax /* reset floppy */ movb $0,%ah movb $0,%dl int $0x13 jc _error1 /* Read the rest of the image to _start. This code works by reading only one sector at a time to avoid "buffer cross 64k boundary" fatal problem... This is slow but should work in almost all situations. _start should be aligned on a 512 bytes boundary to be sure. */ /* destination pointer es:bx */ /* With correct alignement, bx should be 0 and es should be a multiple * of 32. If not it may cause the "buffer cross 64k boundary" problem * (cf above) */ movl $_start,%eax movw %ax,%bx andw $0xF,%bx shrl $4,%eax movw %ax, %es /* initials head/track/sector */ movw $0,%dx movw $1,%cx movl $_edata,%edi addl $(bytesPerSector-1),%edi shrl $4,%edi jmp _loadsector _nextsector: movw %es,%ax cmpw %di,%ax jge _endload addw $esPerSector,%ax movw %ax,%es incb %cl cmpb $sectorsPerTrack, %cl jbe _loadsector /* next head ?*/ movb $1, %cl incb %dh cmpb $1, %dh je _loadsector /* next track ? */ movb $0, %dh incb %ch _loadsector: pushw %es pushw %di movw $0x0201, %ax clc int $0x13 popw %di popw %es jc _error2 movw $(0x0E*256+'.'), %ax /* print a dot */ int $0x10 /* So go ahead and resume execution at the real starting address. This only serves to move us quickly to the real starting location; and has no effect after reading additional tracks. If we didn't jump after reading the first track, then we limit ourselves to reading images of 30k bytes max before overwriting ourselves at 0x7C00. */ ljmp $0,$_nextsector _error1: movw $(0x0E*256+'1'), %ax /* print a ! */ int $0x10 jmp _error _error2: mov %ah,%al pushw %ax shrw $4,%ax andw $15,%ax addw $0x0E41,%ax int $0x10 popw %ax andw $15,%ax addw $0x0E41,%ax int $0x10 movw $(0x0E*256+'2'), %ax /* print a ! */ int $0x10 jmp _error _error: /* halt on error */ movw $(0x0E*256+'!'), %ax /* print a ! */ int $0x10 cli hlt jmp _start /* Write the 0x55/0xAA signature at the end of the first block. Without this signature the BIOS won't consider this block to be bootable. */ . = _start + 510 .byte 0x55 .byte 0xAA _endload: 1: /* Lets be nice and wait for the diskette drive motor to go off * before continuing. */ movw $0x40, %ax movw %ax, %es movl $0x40, %ebx 2: es movb (%bx), %al cmpb $0, %al jne 2b /* Now we're all loaded up in memory. */ /* Optionally switch to a high-res video before entering */ /* protected mode. The mode is controlled by an option in */ /* the RedBoot configuration, which is not readily visible */ /* in the application configuration. Therefore RedBoot also */ /* performs some information-related BIOS calls, getting the */ /* main SVGA BIOS information and the mode-specific */ /* information. These are placed in video memory, because */ /* nothing else should be touching that and it avoids having */ /* some other special buffer shared between RedBoot and the */ /* application. The disadvantage is possibly some strange junk */ /* visible on the screen after RedBoot has started. */ #ifdef CYGNUM_HAL_I386_PC_STARTUP_VIDEO_MODE movw $0x4f02, %ax movw $ CYGNUM_HAL_I386_PC_STARTUP_VIDEO_MODE, %bx int $0x10 /* SVGA information @ 0x000A0000 */ /* Placing VBE2 at this location before the int10 gives more information */ movw $0xA000, %ax movw %ax, %es movw $0x0, %di movb $('V'), %es:0(%di) movb $('B'), %es:1(%di) movb $('E'), %es:2(%di) movb $('2'), %es:3(%di) movw $0x4f00, %ax int $0x10 /* Information about all supported modes starting @ 0x000A0400 */ /* ds:si is used to index the main mode table, offset 14 */ movw %es:14(%di),%si movw %es:16(%di),%ax movw %ax,%ds /* es:di is used for the destination. */ movw $0xA000,%ax movw %ax,%es movw $0x0400,%di modes_loop: /* The mode table is terminated by a -1 entry */ movw %ds:0(%si), %cx cmpw $0xffff,%cx je modes_done movw $0x4f01, %ax int $0x10 addw $0x0100, %di addw $2,%si jmp modes_loop modes_done: /* Information about the current mode @ 0x000A0200 */ movw $0xA000, %ax movw %ax, %es movw $0x0200, %di movw $0x4f01, %ax movw $ CYGNUM_HAL_I386_PC_STARTUP_VIDEO_MODE, %cx int $0x10 #endif /* Disable interrupt handling. */ cli /* Load GDTR and IDTR. */ lgdt %cs:gdt lidt %cs:idt /* Switch to protected mode. */ movl %cr0,%eax orb $1, %al movl %eax,%cr0 ljmp $8, $3f hlt .align 4, 0xFF gdt: .word gdtEnd - gdtStart .long gdtStart .align 4, 0xFF idt: .extern idtStart .word 0x07FF # space for 256 entries .long idtStart gdtStart: /* Selector 0x00 == invalid. */ .word 0x0000 .word 0x0000 .byte 0x00 .byte 0x00 .byte 0x00 .byte 0x00 /* Selector 0x08 == code. */ .word 0xFFFF .word 0x0000 .byte 0x00 .byte 0x9B .byte 0xCF .byte 0x00 /* Selector 0x10 == data. */ .word 0xFFFF .word 0x0000 .byte 0x00 .byte 0x93 .byte 0xCF .byte 0x00 /* Selector 0x18 == shorter code: faults any code * access 0xF0000000-0xFFFFFFFF. */ .word 0xFFFF .word 0x0000 .byte 0x00 .byte 0x9B .byte 0xC7 .byte 0x00 /* Selector 0x20 == data; faults any access 0xF0000000-0xFFFFFFFF. */ .word 0xFFFF .word 0x0000 .byte 0x00 .byte 0x93 .byte 0xC7 .byte 0x00 .align 4, 0xFF gdtEnd: .code32 3: movw $0x10, %ax movw %ax, %ds movw %ax, %es movw %ax, %fs movw %ax, %gs /* Make our new stack point to the same place as the old one. */ xorl %ebx, %ebx movw %ss, %bx shl $4, %ebx addl %esp, %ebx movw %ax, %ss movl %ebx, %esp movl $0, %ebp /* Reset the flags register. */ pushl $0 popfl hal_cpu_init_end: nop .endm /* hal_cpu_init */ #endif /* CYG_HAL_STARTUP_FLOPPY */ #endif // CYGPKG_HAL_I386_CPU_INIT_DEFINED ##============================================================================= ## Interrupt controller support #define CYGPKG_HAL_I386_INTC_INIT_DEFINED #ifndef CYG_HAL_STARTUP_RAM .macro hal_intc_init # The interrupt controller is configured so that IRQ levels 0-7 trigger # interrupt vector 32-39; levels 8-15 trigger 40-47. movb $0x11, %al outb %al, $0x20 movb $0x20, %al outb %al, $0x21 movb $0x04, %al outb %al, $0x21 movb $0x01, %al outb %al, $0x21 movb $0xFB, %al /* Mask off all interrupts except 2. */ outb %al, $0x21 movb $0x11, %al outb %al, $0xA0 movb $0x28, %al outb %al, $0xA1 movb $0x02, %al outb %al, $0xA1 movb $0x01, %al outb %al, $0xA1 movb $0xFF, %al /* Mask off all interrupts. */ outb %al, $0xA1 .endm /* hal_intc_init */ #else # No need to do any initialization in RAM startup .macro hal_intc_init .endm #endif .macro hal_intc_ack vector # Use any registers you like. movl \vector, %edx movb $0x20, %al cmpl $0x20, %edx jl 8f cmpl $0x28, %edx jl 9f outb %al, $0xA0 9: outb %al, $0x20 8: nop .endm ##============================================================================= #endif // ifndef CYGONCE_HAL_PCMB_INC ## end of pcmb.inc