]> git.saurik.com Git - apple/boot.git/blobdiff - i386/boot2/boot.c
boot-111.1.tar.gz
[apple/boot.git] / i386 / boot2 / boot.c
index 72f1be522c6994eb2ad581ac25632b7e7939bb52..48145f415d0635a303eee2fabc88e948bb854c07 100644 (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 "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;
@@ -100,47 +88,52 @@ zeroBSS()
            (&_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();
 
@@ -151,26 +144,23 @@ execKernel(int fd)
         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
 
@@ -178,314 +168,179 @@ execKernel(int fd)
 }
 
 //==========================================================================
-// 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();
     }
 }