freebsd-skq/sys/i386/boot/dosboot/disk.c
Poul-Henning Kamp b8e4cd2bb3 This is a MS-DOS program, but is does something useful for us:
It boots FreeBSD from a running MS-DOS system.

It's compiled using some MS-DOS tools, but there is a binary
hidden in the uuencoded file.  (Go ahead, flame me if you can come up
with a solution for the problem.  Just saying "this is bad" doesn't count!)

Rod, you were right: one would have to deal with weird interfaces to the
memory managers, and it seems that Christian found them all, and made them
work.

Thanks Christian!

Reviewed by:	phk
Submitted by:	DI. Christian Gusenbauer <cg@fimp01.fim.uni-linz.ac.at>

Christians README:
------------------

Hi Everybody!

This is version 1.5 of "fbsdboot", a program that allows you to boot a kernel
from a MS-DOS partition or a FreeBSD partition. This program runs using DOS.
It works with various memory managers (like  EMM386, 386MAX) under certain
circumstances.

First, a FreeBSD kernel is always loaded to memory starting at 0x100000. To
assure that loading the kernel *does not* overwrite memory used by memory
managers, high memory for the kernel is allocated and after loading the kernel
it's moved to 0x100000.

Second, there are many ways to switch to protected mode which is necessary to
start the kernel. Each BIOS gives you the possibility to use INT15H (AH=89H)
to do that. But some memory-managers like 386max does not allow you to use
this method.

An other way to do the switch is to use DPMI services, but they do not
guarantee, that the protected mode application is executed with privilege
level 0. Therefore this method is *not* used.

VCPI services offer another way to switch to protected mode, and VCPI servers
are built into "emm386.exe", "386max" and "qemm". That's why, this method is
implemented in fbsdboot.exe.

Fbsdboot.exe tries to switch to protected mode using VCPI services. If they're
not available INT15H is used to do the switch. If that fails, it's not possible
for this version of fbsdboot.exe to boot a kernel :-(.

You can get commandline options of fbsdboot if you start it with "-?" as option!

I don't know, if fbsdboot works with QEMM, as I don't have the possibility to
test it.

Enjoy and have fun!

Christian.
cg@fimp01.fim.uni-linz.ac.at


PS: Many thanks to Bruce Evans for his assistance!
1995-02-15 04:45:50 +00:00

296 lines
7.3 KiB
C

/*
* Mach Operating System
* Copyright (c) 1992, 1991 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie Mellon
* the rights to redistribute these changes.
*
* from: Mach, Revision 2.2 92/04/04 11:35:49 rpd
* $Id: disk.c,v 1.4 1994/02/22 22:59:40 rgrimes Exp $
*/
#include <stdio.h>
#include <memory.h>
#define bcopy(a,b,c) memcpy(b,a,c)
#include "boot.h"
#ifdef DO_BAD144
#include "dkbad.h"
#endif
#include "disklabe.h"
#define BIOS_DEV_FLOPPY 0x0
#define BIOS_DEV_WIN 0x80
#define BPS 512
#define SPT(di) ((di)&0xff)
#define HEADS(di) ((((di)>>8)&0xff)+1)
static char i_buf[BPS];
#define I_ADDR ((void *) i_buf) /* XXX where all reads go */
#ifdef DO_BAD144
struct dkbad dkb;
int do_bad144;
long bsize;
#endif
static int spt, spc;
char *iodest;
struct fs *fs;
struct inode inode;
long dosdev, slice, unit, part, maj, boff, poff, bnum, cnt;
extern int biosread(int dev, int track, int head, int sector, int cnt, unsigned char far *buffer);
/*#define EMBEDDED_DISKLABEL 1*/
/*extern struct disklabel disklabel;*/
struct disklabel disklabel;
static void Bread(int dosdev, long sector);
static long badsect(int dosdev, long sector);
unsigned long get_diskinfo(int drive)
{
char dr = (char) drive;
unsigned long rt;
_asm {
mov ah,8 ; get diskinfo
mov dl,dr ; drive
int 13h
cmp ah,0
je ok
;
; Failure! We assume it's a floppy!
;
sub ax,ax
mov bh,ah
mov bl,2
mov ch,79
mov cl,15
mov dh,1
mov dl,1
ok:
mov ah,dh
mov al,cl
and al,3fh
mov word ptr rt,ax
xor bx,bx
mov bl,cl
and bl,0c0h
shl bx,2
mov bl,ch
mov word ptr rt+2,bx
}
return rt;
}
int devopen(void)
{
struct dos_partition *dptr;
struct disklabel *dl;
int dosdev = (int) inode.i_dev;
int i;
long di, sector;
di = get_diskinfo(dosdev);
spc = (spt = (int)SPT(di)) * (int)HEADS(di);
if (dosdev == 2)
{
boff = 0;
part = (spt == 15 ? 3 : 1);
}
else
{
#ifdef EMBEDDED_DISKLABEL
dl = &disklabel;
#else EMBEDDED_DISKLABEL
Bread(dosdev, 0);
dptr = (struct dos_partition *)(((char *)I_ADDR)+DOSPARTOFF);
sector = LABELSECTOR;
for (i = 0; i < NDOSPART; i++, dptr++)
if (dptr->dp_typ == DOSPTYP_386BSD) {
sector = dptr->dp_start + LABELSECTOR;
slice = i+1;
break;
}
Bread(dosdev, sector++);
dl=((struct disklabel *)I_ADDR);
disklabel = *dl; /* structure copy (maybe useful later)*/
#endif EMBEDDED_DISKLABEL
if (dl->d_magic != DISKMAGIC) {
printf("bad disklabel");
return 1;
}
if( (maj == 4) || (maj == 0) || (maj == 1)) {
if (dl->d_type == DTYPE_SCSI)
maj = 4; /* use scsi as boot dev */
else
maj = 0; /* must be ESDI/IDE */
}
boff = dl->d_partitions[part].p_offset;
#ifdef DO_BAD144
bsize = dl->d_partitions[part].p_size;
do_bad144 = 0;
if (dl->d_flags & D_BADSECT) {
/* this disk uses bad144 */
int i;
long dkbbnum;
struct dkbad *dkbptr;
/* find the first readable bad144 sector */
/* some of this code is copied from ufs/disk_subr.c */
/* read a bad sector table */
dkbbnum = dl->d_secperunit - dl->d_nsectors;
if (dl->d_secsize > DEV_BSIZE)
dkbbnum *= dl->d_secsize / DEV_BSIZE;
else
dkbbnum /= DEV_BSIZE / dl->d_secsize;
i = 0;
do_bad144 = 0;
do {
/* XXX: what if the "DOS sector" < 512 bytes ??? */
Bread(dosdev, dkbbnum + i);
dkbptr = (struct dkbad *) I_ADDR;
/* XXX why is this not in <sys/dkbad.h> ??? */
#define DKBAD_MAGIC 0x4321
if (dkbptr->bt_mbz == 0 &&
dkbptr->bt_flag == DKBAD_MAGIC) {
dkb = *dkbptr; /* structure copy */
do_bad144 = 1;
break;
}
i += 2;
} while (i < 10 && (u_long) i < dl->d_nsectors);
if (!do_bad144)
printf("Bad badsect table\n");
else
printf("Using bad144 bad sector at %ld\n", dkbbnum+i);
}
#endif
}
return 0;
}
void devread(void)
{
long offset, sector = bnum;
int dosdev = (int) inode.i_dev;
for (offset = 0; offset < cnt; offset += BPS)
{
Bread(dosdev, badsect(dosdev, sector++));
bcopy(I_ADDR, iodest+offset, BPS);
}
}
/* Read ahead buffer large enough for one track on a 1440K floppy. For
* reading from floppies, the bootstrap has to be loaded on a 64K boundary
* to ensure that this buffer doesn't cross a 64K DMA boundary.
*/
#define RA_SECTORS 18
static char ra_buf[RA_SECTORS * BPS];
static int ra_dev;
static long ra_end;
static long ra_first;
static void Bread(int dosdev, long sector)
{
if (dosdev != ra_dev || sector < ra_first || sector >= ra_end)
{
int cyl, head, sec, nsec;
cyl = (int) (sector/(long)spc);
head = (int) ((sector % (long) spc) / (long) spt);
sec = (int) (sector % (long) spt);
nsec = spt - sec;
if (nsec > RA_SECTORS)
nsec = RA_SECTORS;
if (biosread(dosdev, cyl, head, sec, nsec, ra_buf) != 0)
{
nsec = 1;
while (biosread(dosdev, cyl, head, sec, nsec, ra_buf) != 0)
printf("Error: C:%d H:%d S:%d\n", cyl, head, sec);
}
ra_dev = dosdev;
ra_first = sector;
ra_end = sector + nsec;
}
bcopy(ra_buf + (sector - ra_first) * BPS, I_ADDR, BPS);
}
static long badsect(int dosdev, long sector)
{
#ifdef DO_BAD144
int i;
if (do_bad144) {
u_short cyl;
u_short head;
u_short sec;
long newsec;
struct disklabel *dl = &disklabel;
/* XXX */
/* from wd.c */
/* bt_cyl = cylinder number in sorted order */
/* bt_trksec is actually (head << 8) + sec */
/* only remap sectors in the partition */
if (sector < boff || sector >= boff + bsize) {
goto no_remap;
}
cyl = (u_short) (sector / dl->d_secpercyl);
head = (u_short) ((sector % dl->d_secpercyl) / dl->d_nsectors);
sec = (u_short) (sector % dl->d_nsectors);
sec = (head<<8) + sec;
/* now, look in the table for a possible bad sector */
for (i=0; i<126; i++) {
if (dkb.bt_bad[i].bt_cyl == cyl) {
/* found same cylinder */
if (dkb.bt_bad[i].bt_trksec == sec) {
/* FOUND! */
break;
}
} else if (dkb.bt_bad[i].bt_cyl > cyl) {
i = 126;
break;
}
}
if (i == 126) {
/* didn't find bad sector */
goto no_remap;
}
/* otherwise find replacement sector */
newsec = dl->d_secperunit - dl->d_nsectors - i -1;
return newsec;
}
no_remap:
#endif
return sector;
}