/joh'liks/ n.,adj. 386BSD

Porting Unix to the 386: A Practical Approach



William & Lynne Jolitz


DOS C program for loading 386BSD kernel from DOS, and executing it, taking over the machine.

William & Lynne:
"Note the date on this program. This dates the beginning of testing 386BSD .

First PC was a 386DX 16Mhz. We still have the chip!

More on execve() in the book "Source Code Secrets: The Basic Kernel", available from Jolix"



Unix Kernel Load Program

Unix Kernel Load Program

Listing 1 is the boot.c program which resolves these three areas. Note that it is no longer a simple eight-line program. Ah well, life is never simple.

Listing 1: Unix Kernel Load Utility: 'boot.exe' (file: boot.c)

/* Copyright (c) 1989, 1990 William Jolitz. All rights reserved.
 * Written by William Jolitz 7/89
 * Redistribution and use in source and binary forms are freely permitted
 * provided that the above copyright notice and attribution and date of work
 * and this paragraph are duplicated in all such forms.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 * This program allows the bootstrap load of GCC cross compiled
 * 32 bit protected mode absolute programs onto the obtuse architecture
 * of PC AT/386, destroying the running DOS in the process.
 * Currently works with TURBO C 1.5 & MASM 5.0, relies on farmalloc().
 */

#pragma inline
#include <io.h>
#include <fcntl.h>
#include <alloc.h>

#include <dos.h>
#include <sys\stat.h>
#include "exec.h"

#define PGSIZE  4096
#define CLOFSET (PGSIZE - 1)     /* 386 page roundup */

/* Header record of BSD UNIX executable file */
struct exec exec;

long far_read(), to_long();
char far *to_far();
char far *add_to_far(char far *p, long n);

/* Get a file we can open, attempt to load it */
main(argc, argv)
char *argv[];
{   int fd, i;
        long addr, totalsz;
   char far *base;

   if (argc != 2) {
      printf("usage: boot <filename>\n");
      exit(1);
   }
   fd = open(argv[1], O_BINARY);
   if (fd < 0) {
           printf("boot: Cannot open file \"%s\" \n", argv[1]);
          exit(1);
   }

   /* Reasonable file to load? */
   i = read(fd, (char *)&exec, sizeof exec);
   if (i != sizeof exec ||
      (exec.a_magic != OMAGIC && exec.a_magic != NMAGIC
            && exec.a_magic != ZMAGIC)) {
            printf("Not a recognized object file format\n");
            exit(1);
   }

        /* Allocate buffer for temporary copy of protected mode executable
        Buffer space requirements: |<--a.out------------>| pageroundup heap */
        totalsz = exec.a_text + exec.a_data + exec.a_bss + 4096L + 20*1024L;

        /* Pad with trailing portion to put protected mode entry code in */
   base = farmalloc(totalsz + 64*1024L);
   if (base == 0) {
        printf("Cannot allocate enough memory\n");
        exit(1);
   }
        /* Load Instruction (e.g. text) portion of file */
   printf("Text %ld", exec.a_text);
   if (far_read(fd, base, exec.a_text) != exec.a_text)
          goto eof;
        /* Load Data portion of file */
   addr = exec.a_text;

        /* Adjust for page alignment for pure procedure format */
   if (exec.a_magic == NMAGIC && (addr & (PGSIZE-1)))
          while (addr & CLOFSET)
           * add_to_far(base, addr++) = 0;
   printf("\nData %ld", exec.a_data);
   if (far_read(fd, add_to_far(base,addr), exec.a_data) != exec.a_data)
         goto eof;
        /*  Clear Uninitialized data (BSS) space */
   addr += exec.a_data;
   printf("\nBss %ld", exec.a_bss);
   for ( ; addr < totalsz; )
       * add_to_far(base,addr++) = 0;
        if(exec.a_entry)
           printf("\nStart 0x%lx", exec.a_entry);
#ifdef  CKSUM

    /* Optionally calculate checksum to validate against cross host's copy. */
        far_cksum(base, addr-1L);
#endif  CKSUM

        printf("\n");

        /* Effect copydown to absolute 0 and entry into protected mode at
        location "a_entry". */
        transfer(base, totalsz, exec.a_entry);
        /* NOTREACHED */
eof:
        printf(" - File incomplete, load aborted\n");
        exit(1);
}

/* We use the routines below to always keep far pointers normalized
 * to simplify comparision/subtraction. */
char far *to_far(l) long l; {
        unsigned seg, offs;
        seg = l>>4;
        offs = l & 0xf;
        return(MK_FP(seg,offs));
}

long to_long(f) char far *f; {
        unsigned long l;
        l = FP_SEG(f)*16L + (unsigned long)FP_OFF(f);
        return(l);
}

char far *add_to_far(f,l) char far *f; long l; {
        return(to_far(to_long(f)+l));
}

char far *normalize(f) char far *f; {
        unsigned seg,offs ;

        /* add in offset */
        seg =  FP_SEG(f); offs =  FP_OFF(f);
        seg += (offs >> 4) ; offs &= 0xf ;
        return(MK_FP (seg, offs));
}

/* read() that works anywhere in DOS address space for any size data,
 * works via bounce buffer. Not designed for speed or elegance.  */
long far_read(io, base, len) int io; long len; char far *base; {
        char far *fp;

        /* normalize far pointer to handle segment rollover case */
        fp = base = normalize(base);
        while (len) {
                static char dbuf[PGSIZE];
                long rlen,tlen;

                /* bounce buffer between my data segment and ultimate dest */
                tlen = (len > PGSIZE)? PGSIZE : len;
                if ((rlen = read (io, dbuf, tlen)) < 0) return (rlen);

                /* shoot into place */
                movedata (_DS, (unsigned)dbuf, FP_SEG(fp), FP_OFF(fp), rlen);

                /* update transfer address and count */
                fp = add_to_far(fp, rlen);
                len -= rlen ;
                if (tlen != rlen) break ;
        }
        return (to_long(fp) - to_long(base));
}

extern far protentry(); /* known to be less than 0x200 bytes long */
extern far gatea20();

/* set up to transfer to 386 program; call protentry to do the dirty work.  */
transfer(base, len, entry) char far *base; long len, entry; {
        unsigned seg,offs ;
        long rbase;
        char far *fp;

         /* Copy code to top of the system and execute there. This keeps it
             from getting stepped on. */
        /* make 32 bit address */
        rbase = to_long(base);
        fp = add_to_far(base,len);
        seg =  FP_SEG(fp); offs =  FP_OFF(fp);

     /* protect possible conflict of top paragraph of bss */
     if (offs) seg++ ;

     /* force to protentry's offset so offsets agree */
     offs =  FP_OFF(&protentry);
     movedata (FP_SEG(&protentry), offs, seg, offs, PGSIZE);

    /* degate A20 - from now on, full physical memory address bus */
    gatea20();

    /* enter prot_entry program, relocated to top of loaded program, via
    intersegment return */
    asm push word ptr rbase+2 ;
    asm push word ptr rbase ;
    asm push word ptr len+2 ;
    asm push word ptr len ;
    asm push word ptr entry+2 ;
    asm push word ptr entry ;
    asm push word ptr seg;
    asm push word ptr offs;
    asm db 0cbh;    /* lret - intersegment return */

    /* within protentry: go into 32 bit mode, copy entire system to 0 with
    single string instruction, intrasegment jump to entry point */
    printf("protentry returned?!?\n");
    exit(1);

   /* NOTREACHED */
}

#ifdef  CKSUM
/* 16 bit checksum of program. */
far_cksum(base, len) long len; char far *base; {
    char far *tmp;
    unsigned seg,offs ;
    long nbytes,sum, tlen;
    tmp = base;
    sum = 0;
    nbytes = 0;
    while (len) {
               /* normalize far pointer to handle segment rollover case */
          tmp = normalize(tmp);

               /* Do a page at a time */
           tlen = (len > PGSIZE)? PGSIZE : len;
          len -= tlen ;
          while (tlen--) {
         nbytes++;
         if (sum&01)
            sum = (sum>>1) + 0x8000;
         else
            sum >>= 1;
         sum += *tmp++ ;
         sum &= 0xFFFF;
           }
   }
    printf("\nChecksum %05lu%6ld ", sum, (nbytes+CLSIZE)/PGSIZE);
}
#endif CKSUM
[ Note that this first program is a lot like one of our last - in describing the execve() system call implementation, part of the Missing Pieces from the NET/2 tape. Ironically, during the USL/UC lawsuit, this area was a "big deal", however this article (and others similar to it) were never mentioned by either side. This article had been in print for well over a year.

Why? Neither side could land a "knock out" punch, so they were content to joust with non issues, feeling out the other side for weakness. Pity the judge, who has to rely on experts he has no way of gauging the quality of. -wfj ]




<<BACK NEXT >>



Copyright 1989, 1990, 2006 TeleMuse Partners, William Jolitz and Lynne Jolitz