]> git.saurik.com Git - apple/boot.git/blobdiff - i386/libsaio/load.c
boot-132.tar.gz
[apple/boot.git] / i386 / libsaio / load.c
index 77e6ad0a7f909a309a4c734adc34eb7eb4bed5e6..30e549d11dc5923843a3cea1a255819015619521 100644 (file)
 /*
- * Copyright (c) 1999 Apple Computer, Inc. All rights reserved.
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
  *
  * @APPLE_LICENSE_HEADER_START@
  * 
- * Portions Copyright (c) 1999 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
- * 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.
+ * The contents of this file constitute Original Code as defined in and
+ * are subject to the Apple Public 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.
  * 
- * The Original Code and all software distributed under the License are
+ * This Original Code and all software distributed under the License are
  * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE OR NON- INFRINGEMENT.  Please see the
+ * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
  * License for the specific language governing rights and limitations
  * under the License.
  * 
  * @APPLE_LICENSE_HEADER_END@
  */
 /*
- * Copyright 1993 NeXT Computer, Inc.
- * All rights reserved.
+ *  load.c - Functions for decoding a Mach-o Kernel.
+ *
+ *  Copyright (c) 1998-2003 Apple Computer, Inc.
+ *
  */
 
-#include "libsaio.h"
-#include "rcz_common.h"
-#include "rcz_decompress_file.h"
 #include <mach-o/fat.h>
+#include <mach-o/loader.h>
+#include <mach/machine/thread_status.h>
 
-static int devMajor[3] = { 6, 3, 1 };   // sd, hd, fd major dev #'s
+#include <sl.h>
 
-static int xread(int fd, char * addr, int size);
-static int loadmacho(struct mach_header * head, int dev, int io,
-                     entry_t * rentry, char ** raddr, int * rsize,
-                     int file_offset);
+static long DecodeSegment(long cmdBase, unsigned int*load_addr, unsigned int *load_size);
+static long DecodeUnixThread(long cmdBase, unsigned int *entry);
 
-//==========================================================================
-// Open a file for reading.  If the file doesn't exist,
-// try opening the compressed version.
 
-#ifdef RCZ_COMPRESSED_FILE_SUPPORT
-int openfile(const char * filename, int ignored)
-{
-    unsigned char *buf;
-    int fd, size, ret;
-    unsigned char *addr;
-
-    if ((fd = open(filename, 0)) < 0) {
-        buf = malloc(256);
-        sprintf(buf, "%s%s", filename, RCZ_EXTENSION);
-        if ((fd = open(buf, 0)) >= 0) {
-            size = rcz_file_size(fd);
-            addr = (unsigned char *)((KERNEL_ADDR + KERNEL_LEN) - size);
-            ret = rcz_decompress_file(fd, addr);
-            close(fd);
-            if (ret < 0)
-                fd = -1;
-            else
-                fd = openmem(addr, size);
-        }
-        free(buf);
-    }
-    return fd;
-}
-#else
-int openfile(char * filename, int ignored)
-{
-    return open(filename, 0);
-}
-#endif
+static unsigned long gBinaryAddress;
+BOOL   gHaveKernelCache;
 
-//==========================================================================
-// loadprog
+// Public Functions
 
-int loadprog( int dev, int fd, struct mach_header * headOut,
-              entry_t * entry,   /* entry point */
-              char **   addr,    /* load address */
-              int *     size )   /* size of loaded program */
+long ThinFatFile(void **binary, unsigned long *length)
 {
-    struct mach_header head;
-    int file_offset = 0;
-
-read_again:
-
-    /* get file header */
-    read(fd, (char *) &head, sizeof(head));
-
-    if ( headOut )
-        bcopy((char *) &head, (char *) headOut, sizeof(head));
-
-    if ( head.magic == MH_MAGIC )
-    {
-        return loadmacho(&head, dev, fd, entry, addr, size, file_offset);
+  unsigned long nfat, swapped, size = 0;
+  struct fat_header *fhp = (struct fat_header *)*binary;
+  struct fat_arch   *fap =
+    (struct fat_arch *)((unsigned long)*binary + sizeof(struct fat_header));
+  
+  if (fhp->magic == FAT_MAGIC) {
+    nfat = fhp->nfat_arch;
+    swapped = 0;
+  } else if (fhp->magic == FAT_CIGAM) {
+    nfat = OSSwapInt32(fhp->nfat_arch);
+    swapped = 1;
+  } else {
+    return -1;
+  }
+  
+  for (; nfat > 0; nfat--, fap++) {
+    if (swapped) {
+      fap->cputype = OSSwapInt32(fap->cputype);
+      fap->offset = OSSwapInt32(fap->offset);
+      fap->size = OSSwapInt32(fap->size);
     }
-    else if ( file_offset == 0 && 
-              ((head.magic == FAT_CIGAM) || (head.magic == FAT_MAGIC)) )
-    {
-        int swap = (head.magic == FAT_CIGAM) ? 1 : 0;
-        struct fat_header * fhp = (struct fat_header *) &head;
-        struct fat_arch *   fap;
-        int i, narch = swap ? NXSwapLong(fhp->nfat_arch) : fhp->nfat_arch;
-        int cpu, size;
-        char * buf;
-
-        size = sizeof(struct fat_arch) * narch;
-        buf = malloc(size);
-        b_lseek(fd, 0, 0);
-        read(fd, buf, size);
     
-        for ( i = 0, fap = (struct fat_arch *)(buf+sizeof(struct fat_header));
-              i < narch;
-              i++, fap++ )
-        {
-            cpu = swap ? NXSwapLong(fap->cputype) : fap->cputype;
-            if (cpu == CPU_TYPE_I386)
-            {
-                /* that's specific enough */
-                free(buf);
-                file_offset = swap ? NXSwapLong(fap->offset) : fap->offset;
-                b_lseek(fd, file_offset, 0);
-                goto read_again;
-            }
-        }
-        free(buf);
-        error("Fat binary file doesn't contain i386 code\n");
-        return -1;
+    if (fap->cputype == CPU_TYPE_I386) {
+      *binary = (void *) ((unsigned long)*binary + fap->offset);
+      size = fap->size;
+      break;
     }
-    error("Unrecognized binary format: %08x\n", head.magic);
-    return -1;
+  }
+  
+  if (length != 0) *length = size;
+  
+  return 0;
 }
 
-//==========================================================================
-// xread
-//
-// Read from file descriptor. addr is a physical address.
-
-static int xread( int fd, char * addr, int size )
+long DecodeMachO(void *binary, entry_t *rentry, char **raddr, int *rsize)
 {
-    char *      orgaddr = addr;
-    long        offset;
-    unsigned    count;
-    long        max;
-#define BUFSIZ 8192
-    char *      buf;
-    int         bufsize = BUFSIZ;
-
-#if 0
-    printf("xread: addr=%x, size=%x\n", addr, size);
-    sleep(1);
-#endif
-
-    buf = malloc(BUFSIZ);
-
-    // align your read to increase speed
-    offset = tell(fd) & 4095;
-    if ( offset != 0 )
-        max = 4096 - offset;
-    else
-        max = bufsize;
-
-    while ( size > 0 )
-    {
-        if ( size > max ) count = max;
-        else count = size;
-#if 0
-        printf("xread: loop size=%x, count=%x\n", size, count);
-        sleep(1);
+  struct mach_header *mH;
+  unsigned long  ncmds, cmdBase, cmd, cmdsize;
+  //  long   headerBase, headerAddr, headerSize;
+  unsigned int vmaddr = ~0;
+  unsigned int vmend = 0;
+  unsigned long  cnt;
+  long  ret = -1;
+  unsigned int entry = 0;
+  
+  gBinaryAddress = (unsigned long)binary;
+  
+  //  headerBase = gBinaryAddress;
+  cmdBase = (unsigned long)gBinaryAddress + sizeof(struct mach_header);
+  
+  mH = (struct mach_header *)(gBinaryAddress);
+  if (mH->magic != MH_MAGIC) {
+     error("Mach-O file has bad magic number\n");
+     return -1;
+  }
+  
+#if DEBUG
+  printf("magic:      %x\n", (unsigned)mH->magic);
+  printf("cputype:    %x\n", (unsigned)mH->cputype);
+  printf("cpusubtype: %x\n", (unsigned)mH->cpusubtype);
+  printf("filetype:   %x\n", (unsigned)mH->filetype);
+  printf("ncmds:      %x\n", (unsigned)mH->ncmds);
+  printf("sizeofcmds: %x\n", (unsigned)mH->sizeofcmds);
+  printf("flags:      %x\n", (unsigned)mH->flags);
+  getc();
 #endif
-
-        if ( read(fd, buf, count) != count) break;
-
-        bcopy(buf, ptov(addr), count);
-        size -= count;
-        addr += count;
-
-        max = bufsize;
-
-#if 0
-        tick += count;
-        if ( tick > (50*1024) )
-        {
-            putchar('+');
-            tick = 0;
-        }
+  
+  ncmds = mH->ncmds;
+  
+  for (cnt = 0; cnt < ncmds; cnt++) {
+    cmd = ((long *)cmdBase)[0];
+    cmdsize = ((long *)cmdBase)[1];
+    unsigned int load_addr;
+    unsigned int load_size;
+    
+    switch (cmd) {
+      
+    case LC_SEGMENT:
+      ret = DecodeSegment(cmdBase, &load_addr, &load_size);
+      if (ret == 0 && load_size != 0 && load_addr >= KERNEL_ADDR) {
+          vmaddr = min(vmaddr, load_addr);
+          vmend = max(vmend, load_addr + load_size);
+      }
+      break;
+      
+    case LC_UNIXTHREAD:
+      ret = DecodeUnixThread(cmdBase, &entry);
+      break;
+      
+    default:
+#if NOTDEF
+      printf("Ignoring cmd type %d.\n", (unsigned)cmd);
 #endif
+      break;
     }
-
-    free(buf);
-    return addr-orgaddr;
+    
+    if (ret != 0) return -1;
+    
+    cmdBase += cmdsize;
+  }
+  
+  *rentry = (entry_t)( (unsigned long) entry & 0x3fffffff );
+  *rsize = vmend - vmaddr;
+  *raddr = (char *)vmaddr;
+  
+  return ret;
 }
 
-//==========================================================================
-// loadmacho
+// Private Functions
 
-static int loadmacho( struct mach_header * head, int dev, int io,
-                      entry_t * rentry, char ** raddr, int * rsize,
-                      int file_offset )
+static long DecodeSegment(long cmdBase, unsigned int *load_addr, unsigned int *load_size)
 {
-    int          ncmds;
-    void *       cmds;
-    void *       cp;
-    unsigned int entry  = 0;
-    int          vmsize = 0;
-    unsigned int vmaddr = ~0;
-    unsigned int vmend  = 0;
-
-    struct xxx_thread_command {
-        unsigned long    cmd;
-        unsigned long    cmdsize;
-        unsigned long    flavor;
-        unsigned long    count;
-        i386_thread_state_t state;
-    } * th;
-
-    // XXX should check cputype
-    cmds = malloc(head->sizeofcmds);
-    b_lseek(io, sizeof(struct mach_header) + file_offset, 0);
-
-    if ( read(io, (char *) cmds, head->sizeofcmds) != head->sizeofcmds )
-    {
-        error("loadmacho: error reading commands\n");
-        goto shread;
-    }
-
-    for ( ncmds = head->ncmds, cp = cmds; ncmds > 0; ncmds-- )
-    {
-        unsigned int addr;
-
-#define lcp ((struct load_command *) cp)
-#define scp ((struct segment_command *) cp)
-
-        switch ( lcp->cmd )
-        {
-            case LC_SEGMENT:
-                addr = (scp->vmaddr & 0x3fffffff) + (int)*raddr;
-                if ( scp->filesize )
-                {                    
-                    vmsize += scp->vmsize;
-                    vmaddr  = min(vmaddr, addr);
-                    vmend   = max(vmend, addr + scp->vmsize);
-                    
-                    // Zero any space at the end of the segment.
-
-                    bzero((char *)(addr + scp->filesize),
-                          scp->vmsize - scp->filesize);
-                    
-                    // FIXME:  check to see if we overflow
-                    // the available space (should be passed in
-                    // as the size argument).
+  struct segment_command *segCmd;
+  unsigned long vmaddr, fileaddr;
+  long   vmsize, filesize;
+  
+  segCmd = (struct segment_command *)cmdBase;
   
-#if 0
-                    printf("LC: fileoff %x, filesize %x, off %x, addr %x\n",
-                           scp->fileoff, scp->filesize, file_offset, addr);
-                    sleep(1);
+  vmaddr = (segCmd->vmaddr & 0x3fffffff);
+  vmsize = segCmd->vmsize;
+  
+  fileaddr = (gBinaryAddress + segCmd->fileoff);
+  filesize = segCmd->filesize;
+
+  if (filesize == 0) {
+      *load_addr = ~0;
+      *load_size = 0;
+      return 0;
+  }
+  
+#if DEBUG
+  printf("segname: %s, vmaddr: %x, vmsize: %x, fileoff: %x, filesize: %x, nsects: %d, flags: %x.\n",
+        segCmd->segname, (unsigned)vmaddr, (unsigned)vmsize, (unsigned)fileaddr, (unsigned)filesize,
+         (unsigned) segCmd->nsects, (unsigned)segCmd->flags);
+  getc();
 #endif
+  
+  if (! ((vmaddr >= KERNEL_ADDR &&
+          (vmaddr + vmsize) <= (KERNEL_ADDR + KERNEL_LEN)) ||
+         (vmaddr >= HIB_ADDR &&
+          (vmaddr + vmsize) <= (HIB_ADDR + HIB_LEN)))) {
+      stop("Kernel overflows available space");
+  }
+
+  if (vmsize && (strcmp(segCmd->segname, "__PRELINK") == 0)) {
+    gHaveKernelCache = 1;
+  }
+  
+  // Copy from file load area.
+  bcopy((char *)fileaddr, (char *)vmaddr, filesize);
+  
+  // Zero space at the end of the segment.
+  bzero((char *)(vmaddr + filesize), vmsize - filesize);
 
-                    b_lseek(io, scp->fileoff + file_offset, 0);
-                    if ( xread(io, (char *)addr, scp->filesize)
-                         != scp->filesize)
-                    {
-                        error("loadmacho: error loading section\n");
-                        goto shread;
-                    }
-                }
-                break;
-
-            case LC_THREAD:
-            case LC_UNIXTHREAD:
-                th = (struct xxx_thread_command *) cp;
-                entry = th->state.eip;
-                break;
-        }
-        cp += lcp->cmdsize;
-    }
-
-    kernBootStruct->rootdev = (dev & 0xffffff00) | devMajor[B_TYPE(dev)];
-
-    free(cmds);
+  *load_addr = vmaddr;
+  *load_size = vmsize;
 
-    *rentry = (entry_t)( (int) entry & 0x3fffffff );
-    *rsize = vmend - vmaddr;
-    *raddr = (char *)vmaddr;
+  return 0;
+}
 
-    return 0;
 
-shread:
-    free(cmds);
-    return -1;
+static long DecodeUnixThread(long cmdBase, unsigned int *entry)
+{
+  i386_thread_state_t *i386ThreadState;
+  
+  i386ThreadState = (i386_thread_state_t *)
+    (cmdBase + sizeof(struct thread_command) + 8);
+  
+  *entry = i386ThreadState->eip;
+  
+  return 0;
 }
+