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 ]
|
|