* 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 "nbp.h"
+#include "bootstruct.h"
+#include "sl.h"
/*
* The user asked for boot graphics.
*/
-static BOOL gWantBootGraphics = NO;
-
-/*
- * The device that the booter was loaded from.
- */
-int gBootDev;
+BOOL gBootGraphics = NO;
+long gBootMode = kBootModeNormal;
+static char gBootKernelCacheFile[512];
-extern char * gFilename;
-extern BOOL sysConfigValid;
-extern char bootPrompt[];
-extern BOOL errors;
-extern BOOL gVerboseMode;
-extern BOOL gSilentBoot;
+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
/*
- * How long to wait (in seconds) to load the
- * kernel after displaying the "boot:" prompt.
+ * Default path to kernel cache file
*/
-#define kBootTimeout 10
+#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;
(&_DATA__common__end - &_DATA__common__begin) );
}
+//==========================================================================
+// Malloc error function
+
+static void malloc_error(char *addr, size_t size)
+{
+ printf("\nMemory allocation error (0x%x, 0x%x)\n",
+ (unsigned)addr, (unsigned)size);
+ asm("hlt");
+}
+
//==========================================================================
// execKernel - Load the kernel image (mach-o) and jump to its entry point.
-static int
-execKernel(int fd)
+static int ExecKernel(void *binary)
{
- register KERNBOOTSTRUCT * kbp = kernBootStruct;
- static struct mach_header head;
entry_t kernelEntry;
int ret;
- verbose("Loading kernel %s\n", kbp->bootFile);
-
- // Perform the actual load.
+ bootArgs->kaddr = bootArgs->ksize = 0;
- kbp->kaddr = kbp->ksize = 0;
-
- ret = loadprog( kbp->kernDev,
- fd,
- &head,
- &kernelEntry,
- (char **) &kbp->kaddr,
- &kbp->ksize );
- close(fd);
- clearActivityIndicator();
+ ret = DecodeKernel(binary,
+ &kernelEntry,
+ (char **) &bootArgs->kaddr,
+ &bootArgs->ksize );
if ( ret != 0 )
return ret;
- // Load boot drivers from the specifed root.
+ // Reserve space for boot args
+ reserveKernBootStruct();
+
+ // Load boot drivers from the specifed root path.
+
+ if (!gHaveKernelCache) {
+ LoadDrivers("/");
+ }
- LoadDrivers("/");
clearActivityIndicator();
- if (errors) {
+ if (gErrors) {
printf("Errors encountered while starting up the computer.\n");
- printf("Pausing %d seconds...\n", kBootTimeout);
- sleep(kBootTimeout);
+ printf("Pausing %d seconds...\n", kBootErrorTimeout);
+ sleep(kBootErrorTimeout);
}
- message("Starting Darwin/x86", 0);
+ printf("Starting Darwin/x86");
turnOffFloppy();
if ( APMPresent() ) APMConnect32();
}
- // Cleanup the PXE base code.
+ // Cleanup the PXE base code.
- if ( gBootDev == kBootDevNetwork )
- {
+ 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.
+ // Switch to desired video mode just before starting the kernel.
- if ( gWantBootGraphics )
- {
- setMode(GRAPHICS_MODE);
- }
+ setVideoMode( gBootGraphics ? GRAPHICS_MODE : TEXT_MODE );
// 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 *);
-extern void PCI_Bus_Init(PCI_bus_info_t *);
-
- ReadPCIBusInfo( &kernBootStruct->pciInfo );
- PCI_Bus_Init( &kernBootStruct->pciInfo );
+ extern int ReadPCIBusInfo(PCI_bus_info_t *);
+ extern void PCI_Bus_Init(PCI_bus_info_t *);
+
+ ReadPCIBusInfo( &bootArgs->pciInfo );
+
+ //
+ // Initialize PCI matching support in the booter.
+ // Not used, commented out to minimize code size.
+ //
+ // PCI_Bus_Init( &bootArgs->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.
-void
-boot(int bootdev)
+void boot(int biosdev)
{
- register KERNBOOTSTRUCT * kbp = kernBootStruct;
- int fd;
+ int status;
+ char *bootFile;
zeroBSS();
+ // 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);
+ video_mode( 2 ); // 80x25 mono text mode.
// Scan hardware configuration.
scanHardware();
- // Display boot prompt.
+ // Display banner and show hardware info.
- printf( bootPrompt, kbp->convmem, kbp->extmem, kBootTimeout );
+ setCursorPosition( 0, 0, 0 );
+ printf( bootBanner, bootArgs->convmem, bootArgs->extmem );
+ printVBEInfo();
// Parse args, load and start kernel.
while (1)
{
+ const char *val;
+ int len, trycache;
+ long flags, cachetime, time;
+ int ret = -1;
+
// Initialize globals.
sysConfigValid = 0;
- errors = 0;
+ gErrors = 0;
- // Make sure we are in VGA text mode.
+ // Reset config space.
+ bootArgs->configEnd = bootArgs->config;
- setMode(TEXT_MODE);
+ getBootOptions();
+ status = processBootOptions();
+ if ( status == 1 ) break;
+ if ( status == -1 ) continue;
- // Set up kbp->kernDev to reflect the boot device.
-
- 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();
-
- // Display boot prompt and get user supplied boot string.
-
- getBootString();
+ // Found and loaded a config file. Proceed with boot.
- if ( bootdev != kBootDevNetwork )
- {
- // To force loading config file off same device as kernel,
- // open kernel file to force device change if necessary.
+ // Check for cache file.
- fd = open(kbp->bootFile, 0);
- if (fd >= 0) close(fd);
+ if (getValueForKey(kKernelCacheKey, &val, &len)) {
+ strncpy(gBootKernelCacheFile, val, len);
+ gBootKernelCacheFile[len] = '\0';
+ } else {
+ strcpy(gBootKernelCacheFile, kDefaultCachePath);
}
- if ( sysConfigValid == 0 )
- {
- if (kbp->kernDev == DEV_EN)
- break; // return control back to PXE
- else
- continue; // keep looping
- }
+ trycache = (((gBootMode & kBootModeSafe) == 0) &&
+ (gBootFileType == kBlockDeviceType) &&
+ (gBootKernelCacheFile[0] != '\0'));
- // Found and loaded a config file. Proceed with boot.
+ printf("Loading Darwin/x86\n");
- gWantBootGraphics = getBoolForKey( kBootGraphicsKey );
- gSilentBoot = getBoolForKey( kQuietBootKey );
+ if (trycache) do {
+
+ // if we haven't found the kernel yet, don't use the cache
+ ret = GetFileInfo(NULL, bootArgs->bootFile, &flags, &time);
+ if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeFlat)) {
+ trycache = 0;
+ break;
+ }
+ ret = GetFileInfo(NULL, gBootKernelCacheFile, &flags, &cachetime);
+ if ((ret != 0) || ((flags & kFileTypeMask) != kFileTypeFlat)
+ || (cachetime < time)) {
+ trycache = 0;
+ break;
+ }
+ ret = GetFileInfo("/System/Library/", "Extensions", &flags, &time);
+ if ((ret == 0) && ((flags & kFileTypeMask) == kFileTypeDirectory)
+ && (cachetime < time)) {
+ trycache = 0;
+ break;
+ }
+ } while (0);
+
+ do {
+ if (trycache) {
+ bootFile = gBootKernelCacheFile;
+ verbose("Loading kernel cache %s\n", bootFile);
+ ret = LoadFile(bootFile);
+ if (ret >= 0) {
+ break;
+ }
+ }
+ bootFile = bootArgs->bootFile;
+ verbose("Loading kernel %s\n", bootFile);
+ ret = LoadFile(bootFile);
+ } while (0);
- message("Loading Darwin/x86", 0);
+ clearActivityIndicator();
- if ( (fd = openfile(kbp->bootFile, 0)) >= 0 )
- {
- execKernel(fd); // will not return on success
- }
- else
- {
- error("Can't find %s\n", kbp->bootFile);
+ if (ret < 0) {
+ error("Can't find %s\n", bootFile);
- if ( bootdev == kBootDevFloppyDisk )
+ if ( gBootFileType == kBIOSDevTypeFloppy )
{
// floppy in drive, but failed to load kernel.
- bootdev = kBootDevHardDisk;
- message("Couldn't start up the computer using this "
- "floppy disk.", 0);
+ gBIOSDev = kBIOSDevTypeHardDrive;
+ initKernBootStruct( gBIOSDev );
+ printf("Attempt to load from hard drive.");
}
- else if ( bootdev == kBootDevNetwork )
+ else 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((void *)kLoadAddr);
}
- } /* 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()
-{
-#define BOOT_DIR_DISK "/usr/standalone/i386/"
-#define BOOT_DIR_NET ""
-#define makeFilePath(x) \
- (gBootDev == kBootDevNetwork) ? BOOT_DIR_NET x : BOOT_DIR_DISK x
-
- 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);
- }
-}
-
-//==========================================================================
-// Returns 1 if the string pointed by 'cp' contains an user-specified
-// kernel image file name. Used by getBootString() function.
-
-static int
-containsKernelName(const char * cp)
-{
- register char c;
-
- skipblanks(&cp);
-
- // Convert char to lower case.
-
- c = *cp | 0x20;
-
- // Must start with a letter or a '/'.
-
- if ( (c < 'a' || c > 'z') && ( c != '/' ) )
- return 0;
-
- // Keep consuming characters until we hit a separator.
-
- 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 kernBootStruct->bootFile.
-
-static void
-getBootString()
-{
- char line[BOOT_STRING_LEN];
- char * cp;
- char * val;
- int count;
- static int timeout = kBootTimeout;
-
- do {
- line[0] = '\0';
- cp = &line[0];
-
- // If there were errors, don't timeout on boot prompt since
- // the same error is likely to occur again.
-
- 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();
- continue;
- }
-
- // Load config table file specified by the user, or fallback
- // to the default one.
-
- val = 0;
- getValueForBootKey(cp, "config", &val, &count);
- loadSystemConfig(val, count);
- if ( !sysConfigValid )
- continue;
- }
- while ( 0 );
-
- // 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.
-
- if ( getValueForKey(kKernelNameKey, &val, &count) )
- {
- strncpy(kernBootStruct->bootFile, val, count);
- }
- }
- else
- {
- // Get the kernel name from the user-supplied boot string,
- // and copy the name to the buffer provided.
-
- char * namep = kernBootStruct->bootFile;
-
- while ( *cp && !(*cp == ' ' || *cp == '\t') )
- *namep++ = *cp++;
-
- *namep = '\0';
- }
- // Verbose flag specified.
-
- gVerboseMode = getValueForBootKey(cp, "-v", &val, &count);
-
- // Save the boot string in kernBootStruct->bootString.
-
- if ( getValueForKey(kKernelFlagsKey, &val, &count) && count )
- {
- strncpy( kernBootStruct->bootString, val, count );
- }
- if ( strlen(cp) )
- {
- strcat(kernBootStruct->bootString, " ");
- strcat(kernBootStruct->bootString, cp);
+ } /* while(1) */
+
+ if ((gBootFileType == kNetworkDeviceType) && gUnloadPXEOnExit) {
+ nbpUnloadBaseCode();
}
}