/* sbc_mediagx_boot.c - boot kernel from flash. Copyright (C) 2000 Arcom Control System Ltd This program 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 of the License, or (at your option) any later version. This program 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 this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA $Id: sbc_gxx_boot.c,v 1.1 2001/05/09 10:28:06 ajlennon Exp $ A boot loader which runs and loads the kernel directly from flash. The kernel command line is also read from flash. Currently setup for an Arcom Control Systems SBC-MediaGX as follows Flash address 00000 This boot loader BIOS extension 20000 Start of command line 20800 Start of kernel image (only zImage supported) 9FFFF End of boot partition 25/04/2001 AJL (Arcom Control Systems) - Added bzImage support for systems with fast A20 gating support (SBC-MediaGX, SBC-GXm, SBC-GX1) */ // Includes #include "local.h" #include #include // Defines #define VERSION 1 #define ISSUE 02 #define BOARD_NAME "SBC-GXx Series Boards" // - Do we support bzImages ? #define SUPPORT_BZIMAGES // - Where we can find the kernel #define CMDLINE_START 128*1024 #define CMDLINE_SIZE 2*1024 #define KERNEL_START (CMDLINE_START+CMDLINE_SIZE) // - Hardware specifics #define PORT92 0x92 #define WINDOW_START 0xdc000 /* Number of bits in offset. */ #define WINDOW_SHIFT 14 #define WINDOW_LENGTH (1 << WINDOW_SHIFT) /* The bits for the offset into the window. */ #define WINDOW_MASK (WINDOW_LENGTH-1) #define PAGE_IO 0x258 #define PAGE_IO_SIZE 2 /* bit 7 of 0x259 must be 1 to enable device. */ #define DEVICE_ENABLE 0x8000 // Defines for the linux loader #define SETUP_SIZE_OFF 497 #define SECTSIZE 512 #define SETUP_VERSION 0x0201 #define SETUP_HIGH 0x01 #define DEF_BOOTLSEG 0x9020 #define LOADER_TYPE 0x40 // Structures // - From etherboot, this is the header to the image startup code struct setup_header { __u8 jump[2]; __u8 magic[4]; /* "HdrS" */ __u16 version; /* >= 0x0201 for initrd */ __u8 realmode_swtch[4]; __u16 start_sys_seg; __u16 kernel_version; /* note: above part of header is compatible with loadlin-1.5 (header v1.5),*/ /* must not change it */ __u8 type_of_loader; __u8 loadflags; __u16 setup_move_size; unsigned long code32_start; unsigned long ramdisk_image; unsigned long ramdisk_size; unsigned long bootsect_kludge; __u16 heap_end_ptr; }; // - Current page in window static int iCurPage = -1; // ------------------------------------------------------------------- // Board Specific Access Routines // ------------------------------------------------------------------- /* * Function: vPropagateA20( void ) * * Do a PORT20 A20 propogation assertion to ensure we * can talk to high memory */ void vPropagateA20( void ) { outb( inb(PORT92) | 0x02, PORT92); } /* * Function: sbc_gxx_flash_page( unsigned long ofs ) * * Description: Pages the SBC-GXm / SBC-GX1 flash window * to make the byte at flash offset 'ofs' * visible *somewhere* in the window * * Parameters: unsigned long ofs - 32-bit flash offset * * Returns: nothing * */ static inline void sbc_gxx_flash_page(unsigned long ulOfs) { int iPage = (int)(ulOfs >> WINDOW_SHIFT); if( iPage != iCurPage ) { outw( ((unsigned int)iPage) | DEVICE_ENABLE, PAGE_IO ); iCurPage = iPage; } } /* * Function: sbc_gxx_flash_copy( void *pTo, unsigned long pFrom, unsigned long ulLen) * * Description: Copy bytes from (windowed) flash array to low memory * * Parameters: void *pTo - Pointer to start of destination buffer * * unsigned long pFrom - Pointer to start flash offset * * unsigned long ulLen - Length (bytes) to copy * * Returns: nothing * */ static void sbc_gxx_flash_copy(void *pTo, unsigned long ulFrom, unsigned long ulLen) { while(ulLen) { unsigned long ulThisLen = ulLen; if (ulLen > (WINDOW_LENGTH - (ulFrom & WINDOW_MASK))) ulThisLen = WINDOW_LENGTH - (ulFrom & WINDOW_MASK); sbc_gxx_flash_page(ulFrom); memcpy(pTo, (__u8*)(WINDOW_START + (ulFrom & WINDOW_MASK)), ulThisLen); (__u8*)pTo += ulThisLen; ulFrom += ulThisLen; ulLen -= ulThisLen; } } // ------------------------------------------------------------------- // Generic Functions // ------------------------------------------------------------------- /* * Function: iReadKernelImage( void ) * * Description: Read in the kernel image from a region of flash * * Parameters: none * * Notes: * * This creates the same load map that the etherboot linux loader uses, * 0x10000-0x8FFFF 512kB kernel and part of kernel setup * 0x90000-0x901FF 0.5kB linux floppy boot sector * 0x90200-0x911FF 8kB first sectors of kernel setup * 0x92200-0x931FF 4kB primary boot loader * 0x93200-0x933FF 0.5kB load header * 0x93400-0x93BFF 2kB default command line * * Returns: == 0 - Success * != 0 - Failure */ int iReadKernelImage( void ) { unsigned char *pTmp; unsigned short *pTmpS; unsigned long ulSetupSize; struct setup_header *pHdr; // Read the floppy loader to 0x90000 pTmp = (unsigned char *)(DEF_INITSEG << 4); sbc_gxx_flash_copy(pTmp,0+KERNEL_START,512); // Check end of sector for the magic numbers if (pTmp[510] != 0x55 || pTmp[511] != 0xAA) { printk("Bad loader code\n"); return -1; } /* 1 byte value at the end of the loader is the number of sectors of setup code */ ulSetupSize = ((unsigned long)pTmp[SETUP_SIZE_OFF]) * SECTSIZE; // Read the setup code pTmp = (unsigned char *)(DEF_SETUPSEG << 4); sbc_gxx_flash_copy(pTmp,512+KERNEL_START, ulSetupSize); // Check the setup header pHdr = (struct setup_header *)pTmp; // Are magic number and version OK ? if (memcmp(pHdr->magic,"HdrS",4) != 0 || pHdr->version < SETUP_VERSION) { printk("Bad setup code\n"); return -1; } // Set kernel destination pTmp = (unsigned char *)(pHdr->code32_start); // Dump kernel destination to screen printf("Loading kernel to 0x%X\n", (unsigned long)pTmp); // Is this a Big Kernel? (bzImage) if ((pHdr->loadflags & SETUP_HIGH) == SETUP_HIGH) { #ifndef SUPPORT_BZIMAGES printf( "Can't handle big images.\n" ); return -1; #else // Make sure we diddle the type of loader so kernel setup will play with us... pHdr->type_of_loader = LOADER_TYPE; // (emulate ETHERBOOT version 0) /* Read the kernel EEK! How long is the kernel ? - we've increased the max size from (512-10)K up to (640-10K). Uses more flash, but what can we do...*/ sbc_gxx_flash_copy(pTmp,512+ulSetupSize+KERNEL_START, (640-2)*1024); #endif } else { pTmp = (unsigned char *)(DEF_SYSSEG << 4); sbc_gxx_flash_copy(pTmp,512+ulSetupSize+KERNEL_START,(512-10)*1024); } /* Prepare the command line 0x90020-0x90021 2 bytes command line magic number 0x90022-0x90023 2 bytes command line offs. relative to floppy boot sec 0x901FA-0x901FB 2 bytes video mode */ pTmpS = (unsigned short *)(DEF_INITSEG << 4); pTmpS[0x10] = 0xA33F; pTmpS[0x11] = 0x93400 - 0x90000; pTmp = (unsigned char *)(0x93400); sbc_gxx_flash_copy( pTmp, CMDLINE_START, CMDLINE_SIZE ); printk( "Command line: %s\n", pTmp ); return 0; } int main() { // Turn on A20 line propagation (so 1Mb accesses don't wrap) vPropagateA20(); // Signon - NOTE: Important - Due to the way the putchar() function is // implemented in start32.S we will be in PROTECTED MODE // on our return. This is a *good* thing for loading in // bzImages high printf("Arcom Control Systems Embedded Linux flash boot loader\n"); printf("Version %d.%d - Built for %s\n\n", VERSION, ISSUE, BOARD_NAME); // Read in the kernel... if (iReadKernelImage() != 0) { printk("Failed while reading image\n"); return 0; } // Start the kernel... xstart(DEF_BOOTLSEG << 16,0,0); return 0; }