summaryrefslogtreecommitdiffstats
path: root/sys/i386/boot/cdboot/cdrom.c
diff options
context:
space:
mode:
Diffstat (limited to 'sys/i386/boot/cdboot/cdrom.c')
-rw-r--r--sys/i386/boot/cdboot/cdrom.c356
1 files changed, 356 insertions, 0 deletions
diff --git a/sys/i386/boot/cdboot/cdrom.c b/sys/i386/boot/cdboot/cdrom.c
new file mode 100644
index 0000000..03081d9
--- /dev/null
+++ b/sys/i386/boot/cdboot/cdrom.c
@@ -0,0 +1,356 @@
+/*
+ * 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;
+}
OpenPOWER on IntegriCloud