X-Git-Url: https://git.saurik.com/apple/boot.git/blobdiff_plain/14c7c974991c850bfa0348affbd4bee3223205ee..bba600dda0ea8a76d875db7308f372bfc43f8506:/i386/boot2/boot.c diff --git a/i386/boot2/boot.c b/i386/boot2/boot.c index 94aa760..d935082 100644 --- a/i386/boot2/boot.c +++ b/i386/boot2/boot.c @@ -1,12 +1,12 @@ /* - * Copyright (c) 1999 Apple Computer, Inc. All rights reserved. + * Copyright (c) 1999-2003 Apple Computer, Inc. All rights reserved. * * @APPLE_LICENSE_HEADER_START@ * - * Portions Copyright (c) 1999 Apple Computer, Inc. All Rights + * Portions Copyright (c) 1999-2003 Apple Computer, Inc. All Rights * Reserved. This file contains Original Code and/or Modifications of * Original Code as defined in and that are subject to the Apple Public - * Source License Version 1.1 (the "License"). You may not use this file + * Source License Version 2.0 (the "License"). You may not use this file * except in compliance with the License. Please obtain a copy of the * License at http://www.apple.com/publicsource and read it before using * this file. @@ -48,193 +48,142 @@ * Completely reworked by Sam Streeper (sam_s@NeXT.com) * Reworked again by Curtis Galloway (galloway@NeXT.com) */ -#include "libsa.h" -#include "memory.h" -#include "saio.h" -#include "libsaio.h" -#include "kernBootStruct.h" + + #include "boot.h" -#include "drivers.h" -#include "nbp.h" +#include "bootstruct.h" +#include "sl.h" +#include "libsa.h" -/* - * True if using default.table - */ -static BOOL useDefaultConfig; -/* - * Name of the kernel image file to load. - * This is specified in the config file, or may be - * overridden by the user at the boot prompt. - */ -static char gKernelName[BOOT_STRING_LEN]; +long gBootMode; /* defaults to 0 == kBootModeNormal */ +BOOL gOverrideKernel; +static char gBootKernelCacheFile[512]; +static char gCacheNameAdler[64 + 256]; +char *gPlatformName = gCacheNameAdler; +char gRootDevice[512]; +char gMKextName[512]; +BVRef gBootVolume; -/* - * The user asked for boot graphics. - */ -static BOOL gWantBootGraphics = NO; +static +unsigned long Adler32(unsigned char *buffer, long length); -/* - * The device that the booter was loaded from. - */ -int gBootDev; - -extern char * gFilename; -extern BOOL sysConfigValid; -extern char bootPrompt[]; // In prompt.c -extern BOOL errors; -extern BOOL verbose_mode; -extern BOOL gSilentBoot; - -#if MULTIPLE_DEFAULTS -char * default_names[] = { - "$LBL", -}; -#define NUM_DEFAULT_NAMES (sizeof(default_names)/sizeof(char *)) -int current_default = 0; -#else -#define DEFAULT_NAME "$LBL" -#endif + +static BOOL gUnloadPXEOnExit = 0; /* - * Prototypes. + * How long to wait (in seconds) to load the + * kernel after displaying the "boot:" prompt. */ -static void getBootString(); +#define kBootErrorTimeout 5 /* - * Message/Error logging macros. + * Default path to kernel cache file */ -#define PRINT(x) { printf x } - -#ifdef DEBUG -#define DPRINT(x) { printf x; } -#define DSPRINT(x) { printf x; sleep(2); } -#else -#define DPRINT(x) -#define DSPRINT(x) -#endif +#define kDefaultCachePath "/System/Library/Caches/com.apple.kernelcaches/kernelcache" //========================================================================== // Zero the BSS. -static void -zeroBSS() +static void zeroBSS() { extern char _DATA__bss__begin, _DATA__bss__end; extern char _DATA__common__begin, _DATA__common__end; bzero( &_DATA__bss__begin, (&_DATA__bss__end - &_DATA__bss__begin) ); - + bzero( &_DATA__common__begin, (&_DATA__common__end - &_DATA__common__begin) ); } //========================================================================== -// execKernel - Load the kernel image file and jump to its entry point. +// Malloc error function -static int -execKernel(int fd, int installMode) +static void malloc_error(char *addr, size_t size) +{ + printf("\nMemory allocation error (0x%x, 0x%x)\n", + (unsigned)addr, (unsigned)size); + asm volatile ("hlt"); +} + +//========================================================================== +// execKernel - Load the kernel image (mach-o) and jump to its entry point. + +static int ExecKernel(void *binary) { - register KERNBOOTSTRUCT * kbp = kernBootStruct; - register char * src = gFilename; - register char * dst = kbp->boot_file; - char * val; - static struct mach_header head; entry_t kernelEntry; - int ret, size; -#ifdef DISABLED - char * linkerPath; - int loadDrivers; -#endif + int ret; + BOOL bootGraphics; + + bootArgs->kaddr = bootArgs->ksize = 0; - /* Copy the space/tab delimited word pointed by src (gFilename) to - * kbp->boot_file. - */ - while (*src && (*src != ' ' && *src != '\t')) - *dst++ = *src++; - *dst = 0; - - verbose("Loading %s\n", kbp->boot_file); - - /* perform the actual load */ - kbp->kaddr = kbp->ksize = 0; - ret = loadprog(kbp->kernDev, - fd, - &head, - &kernelEntry, - (char **) &kbp->kaddr, - &kbp->ksize); - close(fd); + ret = DecodeKernel(binary, + &kernelEntry, + (char **) &bootArgs->kaddr, + (int *)&bootArgs->ksize ); if ( ret != 0 ) return ret; - /* Clear memory that might be used for loaded drivers - * because the standalone linker doesn't zero - * memory that is used later for BSS in the drivers. - */ - { - long addr = kbp->kaddr + kbp->ksize; - bzero((char *)addr, RLD_MEM_ADDR - addr); - } + // Reserve space for boot args + reserveKernBootStruct(); - clearActivityIndicator(); - printf("\n"); - - if ((getValueForKey("Kernel Flags", &val, &size)) && size) { - int oldlen, len1; - char * cp = kbp->bootString; - oldlen = len1 = strlen(cp); - - // move out the user string - for(; len1 >= 0; len1--) - cp[size + len1] = cp[len1 - 1]; - strncpy(cp,val,size); - if (oldlen) cp[strlen(cp)] = ' '; - } + // Load boot drivers from the specifed root path. - if (errors) { - printf("Errors encountered while starting up the computer.\n"); - printf("Pausing %d seconds...\n", BOOT_TIMEOUT); - sleep(BOOT_TIMEOUT); + if (!gHaveKernelCache) { + LoadDrivers("/"); } - message("Starting Darwin Intel", 0); - - if (kbp->eisaConfigFunctions) - kbp->first_addr0 = EISA_CONFIG_ADDR + - (kbp->eisaConfigFunctions * sizeof(EISA_func_info_t)); - clearActivityIndicator(); - turnOffFloppy(); - - if ( getBoolForKey("APM") ) - { - if ( APMPresent() ) APMConnect32(); + if (gErrors) { + printf("Errors encountered while starting up the computer.\n"); + printf("Pausing %d seconds...\n", kBootErrorTimeout); + sleep(kBootErrorTimeout); } - // Cleanup the PXE base code. + printf("Starting Darwin/x86"); - if ( gBootDev == kBootDevNetwork ) - { + // Cleanup the PXE base code. + + if ( (gBootFileType == kNetworkDeviceType) && gUnloadPXEOnExit ) { if ( (ret = nbpUnloadBaseCode()) != nbpStatusSuccess ) { - printf("nbpUnloadBaseCode error %d\n", (int) ret); sleep(2); + printf("nbpUnloadBaseCode error %d\n", (int) ret); + sleep(2); } } - // Switch to graphics mode just before starting the kernel. + // Unless Boot Graphics = No, Always switch to graphics mode + // just before starting the kernel. - if ( gWantBootGraphics ) - { - setMode(GRAPHICS_MODE); + if (!getBoolForKey(kBootGraphicsKey, &bootGraphics)) { + bootGraphics = YES; } + if (bootGraphics) { + if (bootArgs->Video.v_display == VGA_TEXT_MODE) { + // If we were in text mode, switch to graphics mode. + // This will draw the boot graphics unless we are in + // verbose mode. + setVideoMode( GRAPHICS_MODE ); + } else { + // If we were already in graphics mode, clear the screen. + drawBootGraphics(); + } + } else { + // Always set text mode to initialize video fields + // in the boot args structure. + setVideoMode( VGA_TEXT_MODE ); + setCursorType( kCursorTypeHidden ); + } + + finalizeBootStruct(); + // Jump to kernel's entry point. There's no going back now. - startprog(kernelEntry); + startprog( kernelEntry, bootArgs ); // Not reached @@ -242,345 +191,259 @@ execKernel(int fd, int installMode) } //========================================================================== -// Scan and record the system's PCI bus information. -// +// Scan and record the system's hardware information. static void scanHardware() { -extern int ReadPCIBusInfo(PCI_bus_info_t *pp); -extern void PCI_Bus_Init(PCI_bus_info_t *); - - KERNBOOTSTRUCT * kbp = KERNSTRUCT_ADDR; + extern int ReadPCIBusInfo(PCI_bus_info_t *); + extern void PCI_Bus_Init(PCI_bus_info_t *); - ReadPCIBusInfo( &kbp->pciInfo ); - PCI_Bus_Init( &kbp->pciInfo ); + ReadPCIBusInfo( &bootInfo->pciInfo ); + + // + // Initialize PCI matching support in the booter. + // Not used, commented out to minimize code size. + // + // PCI_Bus_Init( &bootInfo->pciInfo ); } //========================================================================== -// The 'main' function for the booter. This function is called by the -// assembly routine init(), which is in turn called by boot1 or by -// NBP. +// The 'main' function for the booter. Called by boot0 when booting +// from a block device, or by the network booter. // // arguments: -// bootdev - Value passed from boot1/NBP to specify the device -// that the booter was loaded from. See boot.h for the list -// of allowable values. +// biosdev - Value passed from boot1/NBP to specify the device +// that the booter was loaded from. // -// If bootdev is kBootDevNetwork, then this function will return if +// If biosdev is kBIOSDevNetwork, then this function will return if // booting was unsuccessful. This allows the PXE firmware to try the -// next bootable device on its list. If bootdev is not kBootDevNetwork, -// this function will not return control back to the caller. +// next boot device on its list. + +#define DLOG(x) outb(0x80, (x)) -void -boot(int bootdev) +void boot(int biosdev) { - register KERNBOOTSTRUCT * kbp = kernBootStruct; - int fd, size; - char * val; - int installMode = 0; + int status; + char *bootFile; + unsigned long adler32; + BOOL quiet; + BOOL firstRun = YES; + BVRef bvChain; zeroBSS(); - // Enable A20 gate to be able to access memory above 1 MB. + // Initialize malloc + malloc_init(0, 0, 0, malloc_error); + + // Enable A20 gate before accessing memory above 1Mb. enableA20(); - // Remember the device that the booter was loaded from. + // Set reminder to unload the PXE base code. Neglect to unload + // the base code will result in a hang or kernel panic. + + gUnloadPXEOnExit = 1; - gBootDev = bootdev; + // Record the device that the booter was loaded from. + + gBIOSDev = biosdev & kBIOSDevMask; // Initialize boot info structure. - initKernBootStruct(); + initKernBootStruct( gBIOSDev ); // Setup VGA text mode. + // Not sure if it is safe to call setVideoMode() before the + // config table has been loaded. Call video_mode() instead. - setMode(TEXT_MODE); - - // Initialize the malloc area to the top of conventional memory. +#if DEBUG + printf("before video_mode\n"); +#endif + video_mode( 2 ); // 80x25 mono text mode. +#if DEBUG + printf("after video_mode\n"); +#endif - malloc_init( (char *) ZALLOC_ADDR, - (kbp->convmem * 1024) - ZALLOC_ADDR, - ZALLOC_NODES ); + // Check to see that this hardware is supported. + status = checkForSupportedHardware(); + if (status != 0) { + stop("This hardware configuration is not supported by Darwin/x86. (%d)", status); + } // Scan hardware configuration. - scanHardware(); - // Display initial banner. + // First get info for boot volume. + bvChain = scanBootVolumes(gBIOSDev, 0); + + // Record default boot device. + gBootVolume = selectBootVolume(bvChain); + bootInfo->kernDev = MAKEKERNDEV(gBIOSDev, + BIOS_DEV_UNIT(gBootVolume), + gBootVolume->part_no ); - printf( bootPrompt, kbp->convmem, kbp->extmem ); - printf( "Darwin Intel will start up in %d seconds, or you can:\n" - " Type -v and press Return to start up Darwin Intel with " - "diagnostic messages\n" - " Type ? and press Return to learn about advanced startup " - "options\n" - " Type any other character to stop Darwin Intel from " - "starting up automatically\n", - BOOT_TIMEOUT ); + // Load default config file from boot device. + status = loadSystemConfig(0, 0); + + if ( getBoolForKey( kQuietBootKey, &quiet ) && quiet ) { + gBootMode |= kBootModeQuiet; + } // Parse args, load and start kernel. while (1) { - // Initialize globals. + const char *val; + int len; + int trycache; + long flags, cachetime, kerneltime, exttime; + int ret = -1; + void *binary = (void *)kLoadAddr; - sysConfigValid = 0; - useDefaultConfig = 0; - errors = 0; + // Initialize globals. - // Make sure we are in VGA text mode. + sysConfigValid = 0; + gErrors = 0; - setMode(TEXT_MODE); + status = getBootOptions(firstRun); + firstRun = NO; + if (status == -1) continue; - // Set up kbp->kernDev to reflect the boot device. + status = processBootOptions(); + if ( status == 1 ) break; + if ( status == -1 ) continue; - if ( bootdev == kBootDevHardDisk ) - { - if (kbp->numIDEs > 0) - { - kbp->kernDev = DEV_HD; - } - else - { - kbp->kernDev = DEV_SD; - } - } - else if ( bootdev == kBootDevFloppyDisk ) - { - kbp->kernDev = DEV_FLOPPY; - } - else - { - kbp->kernDev = DEV_EN; - } - flushdev(); - -#if 0 // XXX - $LBL -#if MULTIPLE_DEFAULTS - strcpy(gKernelName, default_names[current_default]); - if (++current_default == NUM_DEFAULT_NAMES) - current_default = 0; -#else - strcpy(gKernelName, DEFAULT_NAME); -#endif -#endif + // Found and loaded a config file. Proceed with boot. - // Display boot prompt and get user supplied boot string. - getBootString(); + // Reset cache name. + bzero(gCacheNameAdler + 64, sizeof(gCacheNameAdler) - 64); - if ( bootdev != kBootDevNetwork ) - { - // To force loading config file off same device as kernel, - // open kernel file to force device change if necessary. + sprintf(gCacheNameAdler + 64, "%s,%s", gRootDevice, bootInfo->bootFile); - fd = open(gKernelName, 0); - if (fd >= 0) - close(fd); - } + adler32 = Adler32((unsigned char *)gCacheNameAdler, sizeof(gCacheNameAdler)); - if ( sysConfigValid == 0 ) - { - val = 0; - getValueForBootKey(kbp->bootString, - "config", &val, &size); - - DSPRINT(("sys config was not valid trying alt\n")); - useDefaultConfig = loadSystemConfig(val, size); - - if ( sysConfigValid == 0 ) - { - DSPRINT(("sys config is not valid\n")); - if (kbp->kernDev == DEV_EN) - break; // return control back to PXE - else - continue; // keep looping - } + if (getValueForKey(kKernelCacheKey, &val, &len) == YES) { + strlcpy(gBootKernelCacheFile, val, len+1); + } else { + sprintf(gBootKernelCacheFile, "%s.%08lX", kDefaultCachePath, adler32); } - // Found and loaded a config file. Proceed with boot. + // Check for cache file. - gWantBootGraphics = getBoolForKey("Boot Graphics"); - gSilentBoot = getBoolForKey("Silent Boot"); + trycache = (((gBootMode & kBootModeSafe) == 0) && + (gOverrideKernel == NO) && + (gBootFileType == kBlockDeviceType) && + (gMKextName[0] == '\0') && + (gBootKernelCacheFile[0] != '\0')); - message("Loading Darwin Intel", 0); + printf("Loading Darwin/x86\n"); - if ( (fd = openfile(gKernelName, 0)) >= 0 ) - { - DSPRINT(("calling exec kernel\n")); - execKernel(fd, installMode); + if (trycache) do { + + // if we haven't found the kernel yet, don't use the cache + ret = GetFileInfo(NULL, bootInfo->bootFile, &flags, &kerneltime); + if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeFlat)) { + trycache = 0; + break; + } + ret = GetFileInfo(NULL, gBootKernelCacheFile, &flags, &cachetime); + if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeFlat) + || (cachetime < kerneltime)) { + trycache = 0; + break; + } + ret = GetFileInfo("/System/Library/", "Extensions", &flags, &exttime); + if ((ret == 0) && ((flags & kFileTypeMask) == kFileTypeDirectory) + && (cachetime < exttime)) { + trycache = 0; + break; + } + if (kerneltime > exttime) { + exttime = kerneltime; + } + if (cachetime != (exttime + 1)) { + trycache = 0; + break; + } + } while (0); + + do { + if (trycache) { + bootFile = gBootKernelCacheFile; + verbose("Loading kernel cache %s\n", bootFile); + ret = LoadFile(bootFile); + binary = (void *)kLoadAddr; + if (ret >= 0) { + break; + } + } + bootFile = bootInfo->bootFile; + verbose("Loading kernel %s\n", bootFile); + ret = LoadThinFatFile(bootFile, &binary); + } while (0); + + clearActivityIndicator(); +#if DEBUG + printf("Pausing..."); + sleep(8); +#endif - // If execKernel() returns, kernel load failed. - } - else - { - error("Can't find %s\n", gKernelName); + if (ret < 0) { + error("Can't find %s\n", bootFile); - if ( bootdev == kBootDevFloppyDisk ) - { - // floppy in drive, but failed to load kernel. - bootdev = kBootDevHardDisk; - message("Couldn't start up the computer using this " - "floppy disk.", 0); - } - else if ( bootdev == kBootDevNetwork ) + if ( gBootFileType == kNetworkDeviceType ) { - break; // Return control back to PXE. + // Return control back to PXE. Don't unload PXE base code. + gUnloadPXEOnExit = 0; + break; } + } else { + /* Won't return if successful. */ + ret = ExecKernel(binary); } - } /* while(1) */ -} - -//========================================================================== -// Skip spaces/tabs characters. -static inline void -skipblanks(char ** cp) -{ - while ( **(cp) == ' ' || **(cp) == '\t' ) - ++(*cp); -} - -//========================================================================== -// Load the help file and display the file contents on the screen. - -static void showHelp() -{ - int fd; - char * help = makeFilePath("BootHelp.txt"); - - if ( (fd = open(help, 0)) >= 0 ) - { - char * buffer = malloc( file_size(fd) ); - read(fd, buffer, file_size(fd) - 1); - close(fd); - printf("%s", buffer); - free(buffer); + } /* while(1) */ + + if ((gBootFileType == kNetworkDeviceType) && gUnloadPXEOnExit) { + nbpUnloadBaseCode(); } } -//========================================================================== -// Returns 1 if the string pointed by 'cp' contains an user-specified -// kernel image file name. Used by getBootString() function. - -static inline int -containsKernelName(char * cp) -{ - register char c; - - skipblanks(&cp); - - // Convert everything to lower case. - - c = *cp | 0x20; - - // Must start with a letter or a '/'. - - if ( (c < 'a' || c > 'z') && ( c != '/' ) ) - return 0; +#define BASE 65521L /* largest prime smaller than 65536 */ +#define NMAX 5000 +// NMAX (was 5521) the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 - // Keep consuming characters until we hit a separator. +#define DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); - while ( *cp && (*cp != '=') && (*cp != ' ') && (*cp != '\t') ) - cp++; - - // Only SPACE or TAB separator is accepted. - // Reject everything else. - - if (*cp == '=') - return 0; - - return 1; -} - -//========================================================================== -// Display the "boot:" prompt and copies the user supplied string to -// kernBootStruct->bootString. The kernel image file name is written -// to the gKernelName buffer. - -static void -getBootString() +unsigned long Adler32(unsigned char *buf, long len) { - char line[BOOT_STRING_LEN]; - char * cp; - char * val; - int count; - static int timeout = BOOT_TIMEOUT; - -top: - line[0] = '\0'; - cp = &line[0]; - - /* If there have been problems, don't go on. */ - if ( errors ) timeout = 0; - errors = 0; - - // Print the boot prompt and wait a few seconds for user input. - - printf("\n"); - count = Gets(line, sizeof(line), timeout, "boot: ", ""); - flushdev(); - - // If something was typed, don't use automatic boot again. - // The boot: prompt will not timeout and go away until - // the user hits the return key. - - if ( count ) timeout = 0; - - skipblanks(&cp); - - // If user typed '?', then display the usage message. - - if ( *cp == '?' ) - { - showHelp(); - goto top; - } - - // Did the user specify a kernel file name at the boot prompt? - - if ( containsKernelName(cp) == 0 ) - { - // User did not type a kernel image file name on the boot prompt. - // This is fine, read the default kernel file name from the - // config table. - - printf("\n"); - - val = 0; - getValueForBootKey(cp, "config", &val, &count); - - useDefaultConfig = loadSystemConfig(val, count); - - if ( !sysConfigValid ) - goto top; - - // Get the kernel name from the config table file. - - if ( getValueForKey( "Kernel", &val, &count) ) - { - strncpy(gKernelName, val, count); + unsigned long s1 = 1; // adler & 0xffff; + unsigned long s2 = 0; // (adler >> 16) & 0xffff; + unsigned long result; + int k; + + while (len > 0) { + k = len < NMAX ? len : NMAX; + len -= k; + while (k >= 16) { + DO16(buf); + buf += 16; + k -= 16; } + if (k != 0) do { + s1 += *buf++; + s2 += s1; + } while (--k); + s1 %= BASE; + s2 %= BASE; } - else - { - // Get the kernel name from the user-supplied boot string, - // and copy the name to the buffer provided. - - char * namep = gKernelName; - - while ( *cp && !(*cp == ' ' || *cp == '\t') ) - *namep++ = *cp++; - - *namep = '\0'; - } - - // Verbose flag specified. - - verbose_mode = getValueForBootKey(cp, "-v", &val, &count); + result = (s2 << 16) | s1; + return OSSwapHostToBigInt32(result); +} - // Save the boot string in kernBootStruct. - strcpy(kernBootStruct->bootString, cp); -}