/*
- * 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.
* 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
}
//==========================================================================
-// 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);
-}