c38b4f3b91
This is the long-threatened ISO 9660 CD-ROM bootstrap code. This work has been sponsored by Plutotech International, Inc (who paid the initial work), and interface business GmbH (where i did most of the work). A big thanks also goes to Bruce Evans, for his continuing help and answering my stupid questions. The code is basically functioning, with the following caveats: . Rock Ridge attributes are not yet supported. . Only SCSI CD-ROMs are supported, since i fail to see any possibility to determine the drive type using BIOS functions. (Even for hard disks, this determination is done by a big hack only.) . El Torito specifies a lot of crap and useless misfeatures, but crucial things like the ability to figure out the CD TOC have been ``forgotten''. Thus, if you wanna boot a multisession CD, you need to know at which CD block your session starts, and need to speciffy it using the @ clause. . None of the CD-ROM controllers i've seen so far implements the full El Torito specification at all. Adaptec is probably the closest, but they miss on non-emulation booting (which would be the most logical choice for us). Thus, the current code bloats the 7.5 KB boot code up to 1.44 MB, in order to fake a `floppy' image. If you wanna use it, specify this file as the boot image on the command-line of the mksiosfs command (option -b). Caveat emptor: some versions of the Adaptec BIOS might even fail to access the CD-ROM at all, using the BIOS functions. I think i've notice this for ver 1.26, the code has been tested with ver 1.23. The boot string is as follows: [@sess-start] [filename] [-flags] sess-start Extend # where the last session starts, measured in CD-ROM blocks. filename As usual, but the input is case-insensitive by now (since we don't grok RR anyway). flags As usual, but -C (use CDROM root f/s) is default, so specifying -C will decactivate this option (which is probably not what you want :). A lot of cleanup work is probably required, and some of the files could/should be merged back to biosboot, perhaps made conditional on some #ifdef. The malloc implementation that comes with cdboot might also be useful for kzipboot. (I needed a malloc() since the root dir ain't fixed in size on a CD.) I've been testing all this with a 2.2-STABLE as the base for biosboot. I don't expect too many surprises, although i know the biosboot stuff has been changed a lot in -current lately. I'm sure Bruce will comment on all this here anyway. :-)
357 lines
7.9 KiB
C
357 lines
7.9 KiB
C
/*
|
|
* Copyright © 1997 Pluto Technologies International, Inc. Boulder CO
|
|
* Copyright © 1997 interface business GmbH, Dresden.
|
|
* All rights reserved.
|
|
*
|
|
* This code was written by Jörg Wunsch, Dresden.
|
|
* Direct comments to <joerg_wunsch@interface-business.de>.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
|
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
|
|
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
|
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
|
|
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
*
|
|
* $Id$
|
|
*/
|
|
|
|
|
|
#include "boot.h"
|
|
|
|
#include <isofs/cd9660/iso.h>
|
|
|
|
#define BLKSIZE 2048 /* CD-ROM data block size */
|
|
#define BIOSSEC 512 /* BIOS sector size */
|
|
|
|
#define CD2LBA(rba) ((rba) << 2) /* CD-ROM relative block to BIOS LBA */
|
|
|
|
u_int32_t sessionstart;
|
|
|
|
static struct iso_primary_descriptor pdesc;
|
|
|
|
static char *rootdirbuf;
|
|
static size_t rootdirsize;
|
|
static char xbuf[BLKSIZE];
|
|
static u_int32_t curblk, startblk, filesize, offset;
|
|
|
|
static int bread(u_int32_t rba, size_t nblks, void *buf);
|
|
static void badread(const char *msg, u_int32_t blkno);
|
|
static struct iso_directory_record *find(const char *path, int list_only);
|
|
static int iread(u_char *buf, size_t len,
|
|
void (*copyfun)(const void *src, void *dst, size_t size));
|
|
|
|
static struct daddrpacket dpkt = { 0x10 };
|
|
|
|
int
|
|
devopen(u_int32_t session)
|
|
{
|
|
int rv;
|
|
u_int32_t rootdirblk;
|
|
struct iso_directory_record *rootdirp;
|
|
|
|
if ((rv = bread(session + 16, 1, &pdesc)) != 0) {
|
|
printf("Error reading primary ISO descriptor: %d\n", rv);
|
|
return -1;
|
|
}
|
|
rootdirp = (struct iso_directory_record *)pdesc.root_directory_record;
|
|
rootdirblk = isonum_733(rootdirp->extent);
|
|
rootdirsize = isonum_733(rootdirp->size);
|
|
|
|
/* just in case, round up */
|
|
rootdirsize = (rootdirsize + BLKSIZE - 1) & ~(BLKSIZE - 1);
|
|
|
|
if (rootdirbuf != NULL)
|
|
free(rootdirbuf);
|
|
if ((rootdirbuf = malloc(rootdirsize)) == 0) {
|
|
printf("Cannot allocate memory for the root "
|
|
"directory buffer.\n");
|
|
return -1;
|
|
}
|
|
if ((rv = bread(rootdirblk, rootdirsize / BLKSIZE, rootdirbuf))
|
|
!= 0) {
|
|
printf("Error reading root directory: %d\n", rv);
|
|
return -1;
|
|
}
|
|
|
|
DPRINTF(("Root directory is 0x%x bytes @ %d\n",
|
|
rootdirsize, rootdirblk));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
bread(u_int32_t rba, size_t nblks, void *buf)
|
|
{
|
|
int i, rv;
|
|
|
|
for (i = 0, rv = -1; rv != 0 && i < 3; i++) {
|
|
dpkt.nblocks = nblks * (BLKSIZE / BIOSSEC);
|
|
dpkt.boffs = (u_int16_t)((int)buf & 0xffff);
|
|
dpkt.bseg = BOOTSEG;
|
|
dpkt.lba = CD2LBA(rba);
|
|
|
|
#ifdef DEBUG_VERBOSE
|
|
DPRINTF(("Calling biosreadlba(%d blocks, lba %d) = ",
|
|
dpkt.nblocks, dpkt.lba));
|
|
#endif
|
|
|
|
rv = biosreadlba(&dpkt);
|
|
|
|
#ifdef DEBUG_VERBOSE
|
|
DPRINTF(("%d\n", rv));
|
|
#endif
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
|
|
void
|
|
seek(u_int32_t offs)
|
|
{
|
|
offset = offs;
|
|
}
|
|
|
|
static void
|
|
badread(const char *msg, u_int32_t blkno)
|
|
{
|
|
printf("Error reading block %d from CD-ROM: %s\n",
|
|
blkno, msg);
|
|
}
|
|
|
|
static __inline size_t
|
|
minlen(size_t a, size_t b)
|
|
{
|
|
return a < b? a: b;
|
|
}
|
|
|
|
/*
|
|
* Internal form of read()/xread().
|
|
*/
|
|
static int
|
|
iread(u_char *buf, size_t len,
|
|
void (*copyfun)(const void *src, void *dst, size_t size))
|
|
{
|
|
u_int32_t newblk, ptr;
|
|
size_t bsize;
|
|
|
|
newblk = offset / BLKSIZE + startblk;
|
|
|
|
if (newblk != curblk) {
|
|
if (offset + len >= filesize) {
|
|
badread("access beyond file limit", newblk);
|
|
return -1;
|
|
}
|
|
if (bread(newblk, 1, xbuf)) {
|
|
badread("BIOS read error", newblk);
|
|
return -1;
|
|
}
|
|
curblk = newblk;
|
|
}
|
|
ptr = offset & (BLKSIZE - 1);
|
|
if (ptr > 0) {
|
|
/* initial short transfer */
|
|
bsize = minlen(BLKSIZE - ptr, len);
|
|
copyfun(xbuf + ptr, buf, bsize);
|
|
buf += bsize;
|
|
len -= bsize;
|
|
offset += bsize;
|
|
}
|
|
for (; len > 0; len -= bsize) {
|
|
bsize = minlen(len, BLKSIZE);
|
|
newblk = offset / BLKSIZE + startblk;
|
|
|
|
if (newblk != curblk) {
|
|
if (offset + bsize > filesize) {
|
|
badread("access beyond file limit", newblk);
|
|
return -1;
|
|
}
|
|
if (bread(newblk, 1, xbuf)) {
|
|
badread("BIOS read error", newblk);
|
|
return -1;
|
|
}
|
|
curblk = newblk;
|
|
}
|
|
copyfun(xbuf, buf, bsize);
|
|
buf += bsize;
|
|
offset += bsize;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
read(u_char *buf, size_t len)
|
|
{
|
|
DPRINTF(("read(0x%x, %d)\n", (int)buf, len));
|
|
return iread(buf, len, bcopy);
|
|
}
|
|
|
|
int
|
|
xread(u_char *buf, size_t len)
|
|
{
|
|
DPRINTF(("xread(0x%x, %d)\n", (int)buf, len));
|
|
return iread(buf, len, pcpy);
|
|
}
|
|
|
|
/*
|
|
* XXX Todo:
|
|
* Use RR attributes if present
|
|
*/
|
|
static struct iso_directory_record *
|
|
find(const char *path, int list_only)
|
|
{
|
|
struct iso_directory_record *dirp;
|
|
char *ptr;
|
|
size_t len, entrylen;
|
|
char namebuf[256];
|
|
int i;
|
|
|
|
while (*path && *path == '/')
|
|
path++;
|
|
|
|
for (ptr = rootdirbuf, i = 1;
|
|
ptr < rootdirbuf + rootdirsize;
|
|
ptr += entrylen, i++) {
|
|
dirp = (struct iso_directory_record *)ptr;
|
|
entrylen = (u_char)dirp->length[0];
|
|
len = (u_char)dirp->name_len[0];
|
|
|
|
DPRINTF(("# %d: offset 0x%x, length 0x%x = %d, ",
|
|
i, (int)(ptr - rootdirbuf), entrylen, entrylen));
|
|
|
|
if (entrylen == 0) {
|
|
DPRINTF(("EOD\n"));
|
|
break;
|
|
}
|
|
if (len == 0) {
|
|
DPRINTF(("name_len 0\n"));
|
|
continue;
|
|
}
|
|
if (len == 1 &&
|
|
(dirp->name[0] == '\0' || dirp->name[1] == '\1')) {
|
|
DPRINTF(("dot/dot-dot entry\n"));
|
|
continue;
|
|
}
|
|
/* don't consider directories */
|
|
if (dirp->flags[0] & 2) {
|
|
DPRINTF(("directory\n"));
|
|
continue;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
bcopy(dirp->name, namebuf, len);
|
|
namebuf[len] = 0;
|
|
DPRINTF(("name `%s'\n", namebuf));
|
|
#else /* !DEBUG */
|
|
if (list_only) {
|
|
bcopy(dirp->name, namebuf, len);
|
|
namebuf[len] = 0;
|
|
printf("%s ", namebuf);
|
|
}
|
|
#endif /* DEBUG */
|
|
|
|
if (!list_only &&
|
|
strncasecmp(path, dirp->name, len) == 0)
|
|
return dirp;
|
|
}
|
|
#ifndef DEBUG
|
|
if (list_only)
|
|
printf("\n");
|
|
#endif
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
openrd(char *name)
|
|
{
|
|
char *cp;
|
|
const char *fname;
|
|
u_int32_t oldsession;
|
|
int session, list_only;
|
|
struct iso_directory_record *dirp;
|
|
|
|
session = 0;
|
|
fname = name;
|
|
|
|
/*
|
|
* We accept the following boot string:
|
|
*
|
|
* [@sessionstart] name
|
|
*/
|
|
for (cp = name; *cp; cp++)
|
|
switch (*cp) {
|
|
/* we don't support filenames with spaces */
|
|
case ' ': case '\t':
|
|
break;
|
|
|
|
case '@':
|
|
if (session) {
|
|
printf("Syntax error\n");
|
|
return -1;
|
|
}
|
|
session++;
|
|
oldsession = sessionstart;
|
|
sessionstart = 0;
|
|
break;
|
|
|
|
case '0': case '1': case '2':
|
|
case '3': case '4': case '5':
|
|
case '6': case '7': case '8':
|
|
case '9':
|
|
if (session == 1) {
|
|
sessionstart *= 10;
|
|
sessionstart += *cp - '0';
|
|
}
|
|
break;
|
|
|
|
default:
|
|
if (session == 1) {
|
|
session++;
|
|
fname = cp;
|
|
}
|
|
}
|
|
|
|
if (session && devopen(sessionstart) == -1) {
|
|
(void)devopen(oldsession);
|
|
sessionstart = oldsession;
|
|
}
|
|
if (session == 1)
|
|
/* XXX no filename, only session arg */
|
|
return -1;
|
|
|
|
list_only = fname[0] == '?' && fname[1] == 0;
|
|
|
|
DPRINTF(("Calling find(%s, %d):\n", fname, list_only));
|
|
dirp = find(fname, list_only);
|
|
DPRINTF(("find() returned 0x%x\n", (int)dirp));
|
|
|
|
if (list_only)
|
|
return -1;
|
|
if (dirp == 0)
|
|
return 1;
|
|
|
|
startblk = isonum_733(dirp->extent);
|
|
filesize = isonum_733(dirp->size);
|
|
|
|
DPRINTF(("startblk = %d, filesize = %d\n", startblk, filesize));
|
|
|
|
curblk = 0; /* force a re-read, 0 is impossible file start */
|
|
seek(0);
|
|
|
|
return 0;
|
|
}
|