[patch] GRUB on DiskOnChip 2000 & Millennium

Mark Meade mark at lakeshoremicro.com
Wed Oct 9 08:13:02 EDT 2002


Attached is a patch (for CVS Grub) that adds support for the DiskOnChip 2000 
and Millennium.

This is based on Ilguiz Latypov's <ilatypov at infradead.com> 07-29 patch for 
DiskOnChip support, with the following changes:

       Added a patch from Karim Yaghmour (karim at opersys.com) that 
       provides a way to bypass the DiskOnChip boot entirely, in case 
       Grub is misconfigured or doesn't behave as planned.  Previously, 
       a "hot plug" may have been required to fix a misconfigured
       DiskOnChip.

       Added support for DiskOnChip devices formatted with newer versions
       (5.x) of the M-Sys DFORMAT utility.  The Grub firmware may now
       be burned using the DFORMAT utility, possibly eliminating many
       problems that can occur when using doc_loadbios under Linux.
 
       Backed out requirement for automake 1.6 from previous DiskOnChip
       patch.  This patch needs automake 1.5 and autoconf 2.53, like the
       main GRUB tree.

       * README_DiskOnChip: New DiskOnChip option information
       * configure.in: Added option --enable-ctrlbypass
       * stage1/doc_stage1.S: Added Karim's patch, made minor changes
         to the code to make it fit in 512 bytes (limitation of the DoC
         IPL area).
       * stage2/bdev_diskonchip.c: Allow UnitSizeFactor = 0; assume this
         means the same thing as 0xFF.

An updated HOWTO will be coming shortly.  Here's a summary of the process:

1.  Get GRUB CVS code, apply patch
2.  aclocal && automake --add-missing && autoconf
3.  ./configure with approriate options:
         --enable-diskonchip-mil OR  --enable-diskonchip-2000
         --enable-diskonchip-biosnetboot        (optional)
         --enable-diskonchip-ctrlbypass          (optional)
         --enable-ext2fs
4.  make  (creates stage1/grub_firmware)
5.  In DOS, use DFORMAT to install GRUB and nftl format the DoC:
        DFORMAT /win:xxxx /BDKF0:grub_firmware 
6.  fdisk /dev/nftla  (delete FAT partition, create ext2)
7.  mke2fs /dev/nftla1
8.  mount -t ext2 /dev/nftla1 /mnt/doc
9.  create menu.lst in /mnt/doc/boot/grub, etc.





-------------- next part --------------
diff -urN ../orig/AUTHORS ./AUTHORS
--- ../orig/AUTHORS	Tue Oct  8 16:53:02 2002
+++ ./AUTHORS	Tue Oct  8 16:55:27 2002
@@ -42,3 +42,7 @@
 
 Jason Thomas added Linux DAC960 support and support for hiding/unhiding
 logical partitions, and did a significant bugfix for the terminal stuff.
+
+David Woodhouse <dwmw2 at infradead.org> added DiskOnChip support to GRUB.
+Ilguiz Latypov <ilatypov at superbt.com> tested and improved that, 
+Mark Meade <mark at lakeshoremicro.com> added DiskOnChip Millennium support.
diff -urN ../orig/ChangeLog ./ChangeLog
--- ../orig/ChangeLog	Tue Oct  8 16:53:02 2002
+++ ./ChangeLog	Tue Oct  8 16:55:27 2002
@@ -1,3 +1,31 @@
+2002-10-08  Mark Meade <mark at lakeshoremicro.com>
+
+	Ilguiz Latypov's <ilatypov at infradead.com> 07-29 patch for
+	DiskOnChip support, with the following changes:
+
+	Added a patch from Karim Yaghmour (karim at opersys.com) that 
+	provides a way to bypass the DiskOnChip boot entirely, in case 
+	Grub is misconfigured or doesn't behave as planned.  Previously, 
+	a "hot plug" may have been required to fix a misconfigured
+	DiskOnChip.
+
+	Added support for DiskOnChip devices formatted with newer versions
+	(5.x) of the M-Sys DFORMAT utility.  The Grub firmware may now
+	be burned using the DFORMAT utility, possibly eliminating many
+	problems that occurred when using doc_loadbios under Linux.
+ 
+ 	Backed out requirement for automake 1.6 from previous DiskOnChip
+	patch.  This patch needs automake 1.5 and autoconf 2.53, like the
+	main GRUB tree.
+
+ 	* README_DiskOnChip: New DiskOnChip option information
+ 	* configure.in: Added option --enable-ctrlbypass
+ 	* stage1/doc_stage1.S: Added Karim's patch, made minor changes
+	  to the code to make it fit in 512 bytes (limitation of the DoC
+	  IPL area).
+ 	* stage2/bdev_diskonchip.c: Allow UnitSizeFactor = 0; assume this
+	  means the same thing as 0xFF.
+
 2002-10-06  Yoshinori K. Okuji  <okuji at enbug.org>
 
 	* stage2/asm.S (gateA20): Output a dummy command (0xff), as a
@@ -60,6 +88,71 @@
 	* stage2/stage2.c (run_menu): Reverse if (!) to if () for
 	uniformitty.
 
+2002-07-29  Ilguiz Latypov <ilatypov at superbt.com>
+ 
+ 	Applied the GRUB patch written by David Woodhouse
+ 	<dwmw2 at infradead.org>.  The patch is a part of Linux MTD
+ 	project, http://linux-mtd.infradead.org/.  Applied numerous
+ 	patches written by Mark Meade <mark at lakeshoremicro.com>.
+ 	The latter enable DoC Millennium support and unify the code
+ 	base for both DiskOnChip 2000 and Millennium.
+ 
+ 	* README_DiskOnChip: New DiskOnChip background information.
+ 	* configure.in: Added options --enable-diskonchip-2000,
+ 	--enable-diskonchip-mil and --enable-diskonchip-biosnetboot.
+ 	* stage1/Makefile.am: Added explicit DiskOnChip dependency
+ 	rules.
+ 	* stage1/doc_stage1.S: Added DoC firmware startup code from
+ 	the above patch with the following modifications:
+ 	a) don't read SlowIO when fetching the flash data
+ 	b) disable CDSN_CTRL_CLE when finishing DoC command
+ 	c) apply Millennium-specific commands and addresses when DoC
+ 	   Millennium is detected
+ 	d) display DoC type (2000 or Millennium) and BIOS window
+ 	   segment
+ 	e) remove extra CDSNControl read from doc_wait to save space
+ 	* stage1/doc_stage1.h: Likewise.
+ 	* stage1/doc_stage1b.S: Removed.  Mark has eliminated the "sick 
+ 	  trick" that relied on DoC 2000 specifics.
+ 	* stage1/makecsum.c: New DoC compilation helper utility.
+ 	* stage2/Makefile.am: Added DiskOnChip NFTL block read module.
+ 	* stage2/asm.S: Set DiskOnChip as a default boot drive.
+ 	* stage2/bdev_diskonchip.c: New DoC NFTL block read module:
+ 	a) new flash memory manufacturer/model table after nand_ids.h
+ 	b) re-use DoC_Address for proper chip identification
+ 	* stage2/bios.c (get_diskinfo): Simulate BIOS disk information
+ 	for DoC and network drive to indulge GRUB drive sanity check and
+ 	command line geometry request.
+ 	* stage2/boot.c (load_image): Display the start of kernel
+ 	loading to simplify debugging.
+ 	* stage2/builtins.c (geometry_func): Return the size of DoC to 
+ 	command line geometry query.  Return 0's for network drive.
+ 	(print_root_device): DoC drive name completion.
+ 	* stage2/char_io.c (memcheck): Flag memory size error.
+ 	* disk_io.c (rawread): Redirect DoC rawread requests to NFTL
+ 	block read module.  Log all rawread() requests when run-time
+ 	debug switch is on.
+ 	(rawwrite): Deny DoC rawwrite requests as this is not
+ 	implemented for GRUB yet.
+ 	(sane_partition): Pass through the (fictitious) DoC drive
+ 	number.
+ 	(next_partition): More MBR debug for DoC as this is the first
+ 	sector of NFTL layer.  Show an error message when MBR is invalid
+ 	in DoC or other drives.
+ 	(set_device): Better drive name parsing, including "dc" for DoC.
+ 	(open_device): More DoC debugging.
+ 	(setup_part): Likewise.
+ 	* stage2/shared.h: Added fictitious DoC drive number.  Added the
+ 	compile-time DoC debug switch.
+ 	* stage2/stage2.c (try_config_partition): New function to open
+ 	configuration file in a given partition.
+ 	(find_config): New function to search for configuration file
+ 	through pre-set partition, whole disk and partitions 0 to 15.
+ 	(cmain): Attempt to open configuration file first on DoC, then
+ 	on the hard disk and lastly on the floppy disk.
+ 	(cmain): Do not clear screen on DiskOnChip failure and when 
+ 	debugging DoC.
+
 2002-07-12  Yoshinori K. Okuji  <okuji at enbug.org>
 
 	* stage2/boot.c (load_image): Rewrite the Linux booting support
diff -urN ../orig/README_DiskOnChip ./README_DiskOnChip
--- ../orig/README_DiskOnChip	Wed Dec 31 19:00:00 1969
+++ ./README_DiskOnChip	Tue Oct  8 16:55:27 2002
@@ -0,0 +1,134 @@
+	$Id: grub-2002-07-29-doc.patch,v 1.2 2002/08/02 20:42:53 ilatypov Exp $
+
+HOW TO GET GRUB BOOTING FROM DiskOnChip
+---------------------------------------
+
+(0. You'll need the Linux MTD driver modules loaded and the utils built.
+    These are available at http://linux-mtd.infradead.org/)
+
+ 1. Build a recent GRUB (from the FSF, not the old grub-0.5) with
+
+      aclocal && automake --add-missing && autoconf
+      ./configure --enable-diskonchip-2000 \
+                  --enable-ext2fs \
+                  --disable-ffs --disable-xfs \
+                  --disable-jfs --disable-vstafs --disable-reiserfs \
+                  --disable-minix --disable-fat   # for example
+      make
+      ls -al stage1/grub_firmware
+
+    Change the first configuration option to
+
+       --enable-diskonchip-mil
+
+    for DoC Millennium.
+
+    To activate GRUB through the network boot BIOS option instead of the
+    standard BIOS bootstrap loader, add
+
+      --enable-diskonchip-biosnetboot
+
+    To enable the ability to bypass the DoC GRUB boot entirely, add
+
+      --enable-diskonchip-ctrlbypass
+
+    This option is highly recommended until you are sure that the DoC is
+    booting correctly.  Holding the CTRL key down during boot will cause
+    GRUB to use the BIOS's original bootstrap handler, bypassing GRUB on
+    the DoC.
+    
+    Using this feature avoids having to hotplug the DoC in case Grub is 
+    misconfigured or doesn't behave as planned. 
+	    
+ 2. To store the firmware into DiskOnChip, run the Linux MTD utility
+
+      ./doc_loadbios /dev/mtd0 grub_firmware
+
+This assumes that the DiskOnChip is the only (or first) memory device
+for which the driver was loaded - and hence that it's /dev/mtd0
+
+It's also possible to use the M-Sys DFORMAT utility to both install
+GRUB and format the DiskOnChip.  This eliminates the need to use
+doc_loadbios and nftl_format under Linux:
+
+      DFORMAT /win:xxxx /BDKF0:grub_firmware
+
+HOW IT WORKS
+------------
+
+The DiskOnChip has a built-in ROM which is recognised as a BIOS extension
+by the system BIOS. This is called the 'IPL ROM', and it attempts to load 
+0x2000 bytes of the next stage, called the 'SPL, from the DiskOnChip.
+
+To make life complex, it doesn't just load the first 0x2000 bytes from the
+DiskOnChip - it loads half of the first 0x4000 bytes instead. Of each 512-byte
+block, it loads the first 256 bytes. 
+This is because of the way that NAND flash blocks are divided into two pages,
+and presumably it's done to save code space in the IPL ROM.
+
+As it loads the SPL, it performs a checksum. Iff the checksum of the SPL is
+equal to 0x55, then it will execute the SPL, which it has loaded to the 
+address 0x2000:0000.
+
+The SPL in turn is responsible for loading the rest of the firmware from the 
+DiskOnChip. Normally, this steals some memory for itself, and installs an
+INT 13h (Disk BIOS) handler to emulate disk access.
+
+We change this. Instead of installing an INT 13h handler, we install an
+INT 19h (bootstrap) handler. This is very small, and is held entirely
+within the first 256 bytes of the SPL with the code which installs it. 
+
+The INT 19h handler, when executed, loads GRUB Stage2 from the DiskOnChip
+to the place where GRUB Stage1 would normally load it, and then passes 
+control to it.
+
+Clear as mud? 
+
+
+NOTE: Grub 0.5, which was the latest version for some time before the FSF
+took over, expects stage2 to be loaded to 0x08000, and executed with CS:IP
+0000:8000.
+
+The versions currently in the FSF CVS repository are slightly different. The
+stage2 now has a 512-byte loader prepended to it, so now we need to load it
+as before but execute it starting as 0000:8200.
+
+I've changed the default behaviour of the DiskOnChip Stage1 over to the new 
+address, because I expect any development of DiskOnChip capability in Grub 
+itself to be added to the CVS version, not the old 0.5 version.
+
+If you are using this with the older grub, change the first few lines of the
+Makefile appropriately.
+
+DW 28/1/00
+
+
+The Millennium's Download Engine won't expose page size differences to
+IPL.  This is unlike DoC 2000 where ROM IPL will load different blocks
+of flash memory depending on flash chip page size.  The page 13 of
+Millennium data sheet
+
+http://www.m-sys.com/files/DataSheets%5Cdoc%5CDiskOnChip_Mil._DS_Rev2.1.pdf
+
+says the following:
+
+  Upon power-up (DIP version) or the negation of the RSTIN# input (TSOP
+  version), the DiskOnChip Millennium downloads the Initial Program Loader
+  (IPL) data into the boot-block from the first 512 bytes of flash memory.
+  As a failsafe mechanism, DiskOnChip Millennium uses its internal Error
+  Detection and Correction (EDC/ECC) logic to verify the integrity of this
+  data. If DiskOnChip Millennium detects an error, it will automatically
+  download the redundant copy of the IPL, which is stored in the following
+  page of the flash memory. The entire download process takes less than 1
+  millisecond. Access to the DiskOnChip Millennium is not permitted before
+  the download process is completed.
+
+Thanks to Mark Meade the DiskOnChip-specific stage 1 is now streamlined.
+Just as with stage2 or Linux MTD DiskOnChip driver, the stage 1 code will 
+find the page size by looking at the chip manufacturer and model codes.
+Both DiskOnChip 2000 and Millennium will have almost the same firmware.
+The only differences are ROM BIOS extension signature necessary for 
+Millennium and the checksum.
+
+--ilatypov 2002-07-29
+
diff -urN ../orig/configure.in ./configure.in
--- ../orig/configure.in	Tue Oct  8 16:53:02 2002
+++ ./configure.in	Tue Oct  8 16:55:27 2002
@@ -241,6 +241,69 @@
   FSYS_CFLAGS="$FSYS_CFLAGS -DFSYS_VSTAFS=1"
 fi
 
+for opt in ${!enable_diskonchip*} ; do
+  case "${opt}" in
+    ( enable_diskonchip_2000 |\
+      enable_diskonchip_mil |\
+      enable_diskonchip_ctrlbypass |\
+      enable_diskonchip_biosnetboot )
+      true
+      ;;
+    ( * )
+      AC_MSG_ERROR([
+        Only the following DiskOnChip options are allowed:
+        --enable-diskonchip-2000
+        --enable-diskonchip-mil
+        --enable-diskonchip-ctrlbypass
+        --enable-diskonchip-biosnetboot])
+      ;;
+  esac
+done
+
+AC_ARG_ENABLE(diskonchip-2000,
+  [  --enable-diskonchip-2000           enable DiskOnChip 2000 support])
+
+AC_ARG_ENABLE(diskonchip-mil,
+  [  --enable-diskonchip-mil            enable DoC Millennium support])
+
+AC_ARG_ENABLE(diskonchip-biosnetboot,
+  [  --enable-diskonchip-biosnetboot    hook DiskOnChip to BIOS network boot],
+  [STAGE1_CFLAGS="$STAGE1_CFLAGS -DDOC_BIOS_NETBOOT=1"])
+
+AC_ARG_ENABLE(diskonchip-ctrlbypass,
+  [  --enable-diskonchip-ctrlbypass     ctrl-key bypasses DiskOnChip boot],
+  [STAGE1_CFLAGS="$STAGE1_CFLAGS -DDOC_CTRL_BYPASS=1"])
+
+AM_CONDITIONAL(DOC_MIL, 
+  test x$enable_diskonchip_mil = xyes)
+
+if test x$enable_diskonchip_2000 = xyes \
+   -o x$enable_diskonchip_mil = xyes
+then
+  FSYS_CFLAGS="$FSYS_CFLAGS -DBDEV_DISKONCHIP=1"
+  if test x$enable_diskonchip_mil = xyes
+  then
+    STAGE1_CFLAGS="$STAGE1_CFLAGS -DDOC_MIL=1"
+  fi
+  n_doc_drivers=0
+  test x$enable_diskonchip_2000 = xyes && \
+	n_doc_drivers=$((n_doc_drivers + 1))
+  test x$enable_diskonchip_mil = xyes && \
+	n_doc_drivers=$((n_doc_drivers + 1))
+  test $n_doc_drivers -ne 1 && {
+    AC_MSG_ERROR([Only one DiskOnChip driver can be enabled])
+  }
+else
+  if test x$enable_diskonchip_biosnetboot = xyes \
+     -o x$enable_diskonchip_ctrlbypass = xyes ; then
+    AC_MSG_ERROR([The DiskOnChip driver should be enabled])
+  fi
+fi
+
+AM_CONDITIONAL(BDEV_DISKONCHIP, 
+  test x$enable_diskonchip_2000 = xyes \
+    -o x$enable_diskonchip_mil = xyes)
+
 AC_ARG_ENABLE(jfs,
   [  --disable-jfs           disable IBM JFS support in Stage 2])
 
diff -urN ../orig/stage1/Makefile.am ./stage1/Makefile.am
--- ../orig/stage1/Makefile.am	Tue Oct  8 16:53:02 2002
+++ ./stage1/Makefile.am	Tue Oct  8 16:55:27 2002
@@ -1,7 +1,48 @@
 pkgdatadir = $(datadir)/$(PACKAGE)/$(host_cpu)-$(host_vendor)
-nodist_pkgdata_DATA = stage1
 
-CLEANFILES = $(nodist_pkgdata_DATA)
+if BDEV_DISKONCHIP
+##################### Build DiskOnChip bootloader ######################
+# DiskOnChip stage 1 bootloader from http://linux-mtd.infradead.org/
+# as of Feb 19, 2002
+#
+STAGE2FILE = ../stage2/pre_stage2
+
+if DOC_MIL
+DOC_MIL_CHECKSUM = -m
+else
+DOC_MIL_CHECKSUM =
+endif
+
+grub_firmware: doc_stage1 makecsum $(STAGE2FILE) Makefile
+	./makecsum $(DOC_MIL_CHECKSUM) doc_stage1 $(STAGE2FILE) grub_firmware
+
+doc_stage1.o: doc_stage1.S doc_stage1.h stage2_size.h Makefile
+	$(CC) $(STAGE1_CFLAGS) -fno-builtin -nostdinc -c \
+		-o doc_stage1.o doc_stage1.S
+
+doc_stage1.exec: doc_stage1.o
+	$(LD) -N -Ttext 0 -o doc_stage1.exec doc_stage1.o
+
+stage2_size.h: $(STAGE2FILE)
+	-rm -f stage2_size.h
+	set dummy `ls -l $(STAGE2FILE)`; \
+	echo "#define STAGE2_SIZE $$6" > stage2_size.h
+
+# Ignore CFLAGS and LDFLAGS as these are set up for standalone mode
+makecsum: makecsum.c
+	$(CC) -o $@ $<
+
+GRUB_FIRMWARE = grub_firmware
+
+GRUB_FIRMWARE_CLEAN = doc_stage1 *.o *.bin *.exec *~ \
+		grub_firmware makecsum stage2_size.h
+
+########################################################################
+endif
+
+nodist_pkgdata_DATA = stage1 $(GRUB_FIRMWARE)
+
+CLEANFILES = $(nodist_pkgdata_DATA) $(GRUB_FIRMWARE_CLEAN)
 
 # We can't use builtins or standard includes.
 AM_ASFLAGS = $(STAGE1_CFLAGS) -fno-builtin -nostdinc
@@ -12,3 +53,5 @@
 
 %: %.exec
 	$(OBJCOPY) -O binary $< $@
+
+
diff -urN ../orig/stage1/doc_stage1.S ./stage1/doc_stage1.S
--- ../orig/stage1/doc_stage1.S	Wed Dec 31 19:00:00 1969
+++ ./stage1/doc_stage1.S	Tue Oct  8 16:55:27 2002
@@ -0,0 +1,494 @@
+/* -*-Asm-*- */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2000 Machine Vision Holdings, Inc.
+ *
+ *  Author: David Woodhouse <dwmw2 at infradead.org>
+ *
+ *  $Id: grub-2002-07-29-doc.patch,v 1.2 2002/08/02 20:42:53 ilatypov Exp $
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *  2002/08/10: Karim Yaghmour (karim at opersys.com)
+ *	Added code to keep a copy of the BIOS's handler and restore it
+ *      in case the user is holding the "ctrl" button down. Avoids
+ *      having to hotplug the DoC in case Grub is misconfigured or
+ *      doesn't behave as planned. 
+ *
+ *  2002/10/04: Mark Meade <mark at lakeshoremicro.com>
+ *	 Combined Karim's patch with the latest DoC 2000/Millenium patches.
+ *      These earlier patches include contributions from Ilguiz Latypov 
+ *	 <ilatypov at infradead.org> and myself.
+ */
+
+#include "doc_stage1.h"
+	
+	.file	"doc_stage1.S"
+
+	.text
+
+	/* Tell GAS to generate 16-bit instructions so that this code works
+	   in real mode. */
+	.code16
+
+.globl _start; _start:
+
+#ifdef DOC_MIL
+	/*
+	 * _start is in the beginning of DoC Millennium flash memory
+	 */
+	.byte 0x55, 0xAA	/* BIOS extension signature */
+	.byte 0x10		/* BIOS extension size in 512 byte blocks */
+       /*                                       
+	 * The checksum byte at offset 511 of every copy of IPL is the
+	 * complement to 0 of first 511 bytes.  This is because of 2 facts:
+	 * a) the sum of bytes in the BIOS extension window is supposed to 
+	 *    be 0
+	 * b) the whole 8K DoC window is split into 2 aliases of 2 copies
+	 *    of 512 byte IPL in the beginning, 4K of zero-filled area and 
+	 *    again 2 aliases of 2 copies of 512 byte IPL in the end.
+	 */
+	/* BIOS will call far _start + 3 */
+	.org	3
+bios_extension_start:
+	pushw	%cs
+#else
+	pushw	%ds
+#endif
+	cld 
+	MSG(doc_bios_scan_start_string)
+
+	/*
+	 * With DoC 2000, _start is loaded at 0x20000 by the IPL,
+	 * and is jumped to with CS:IP 0x2000:0, DS is <DoC segment>.
+	 *
+	 * With DoC Millennium, the flash memory is mapped to offset 0
+	 * of DoC window.  We suppose that BIOS jumps to that offset 
+	 * during the ROM extension scan with CS:IP <DoC segment>:3.
+	 */
+
+	/* What we need to do is:
+	   1. Check the current end-of-memory in the BIOS
+	   2. Reduce it by the amount of memory we need for our INT 19h 
+	   3. Return, and wait for our INT 18h/19h to be called.
+	*/
+
+	/* Find top of memory */
+	xorw	%ax, %ax
+	movw	%ax, %ds
+	movw	0x0413, %ax
+
+	/* Steal 1K from the top */
+	decw	%ax
+	movw	%ax,0x0413
+
+	/* Generate segment address */
+	shlw	$6,%ax
+	movw	%ax,%es
+
+#ifdef DOC_CTRL_BYPASS
+	/* Keep the BIOS's original handler handy */
+	movl    (DOC_BIOS_HOOK * 4), %eax
+	movl    %eax, %cs:bios_orig_handle_adr
+#endif
+
+	/* Set up our shiny new INT 18h or 19h handler */
+	movw	%es, (DOC_BIOS_HOOK * 4 + 2)
+	movw	$doc_bios_boot_hook, (DOC_BIOS_HOOK * 4)
+
+	/* Copy ourself into the new segment we've just reserved */
+	movw	$0x200, %cx
+	pushw	%cs
+	popw	%ds
+	xorw	%si,%si
+	xorw	%di,%di
+	rep
+	movsw
+       
+	/* Store the DiskOnChip segment */
+	popw	%es:doc_seg
+	lret
+
+
+doc_bios_boot_hook:
+	cld
+       
+#ifdef DOC_CTRL_BYPASS
+	/* Verify whether the user is holding down the "ctrl" key */
+	movb	$0x02, %ah
+	int	$0x16
+	testb	$0x04, %al
+	jz	doc_boot
+
+	/* Restore the BIOS's original handler and let it boot the machine */
+	xor     %ax, %ax
+	movw    %ax, %ds
+	movl	%cs:bios_orig_handle_adr, %eax
+	movl    %eax, (DOC_BIOS_HOOK * 4)
+	int	$DOC_BIOS_HOOK
+
+doc_boot:
+#endif DOC_CTRL_BYPASS
+
+	/* Say hello */
+	MSG(doc_bios_hook_execed_string)
+#ifndef SHOW_INFO
+	MSG(eol_string)
+#endif
+
+	/* Check where the DiskOnChip is */
+	movw	%cs:doc_seg, %ds
+
+	/* Reset DiskOnChip */
+	movw	$BXREG, %bx
+	movb	$DOC_MODE_CLR_ERR + DOC_MODE_MDWREN + DOC_MODE_NORMAL, BX_DOCControl
+	movb	$DOC_MODE_CLR_ERR + DOC_MODE_MDWREN + DOC_MODE_NORMAL, BX_DOCControl
+
+	/* Set the I/O register */
+	movw	$DoC_2k_CDSN_IO, %si
+	movb	BX_ChipID, %al
+	cmpb	$DOC_ChipID_Doc2k, %al
+	je	ioreg_end
+	movw	$DoC_Mil_CDSN_IO, %si
+ioreg_end:
+	
+	/* Flash command:	Reset */
+	movb	$NAND_CMD_RESET, %ah
+	call	doc_cmd
+
+#ifdef SHOW_INFO
+
+	/********* Display chip ID and segment ***************/
+	pushw	%bx
+	pushw	%si
+
+	movb	BX_ChipID, %al
+	cmpb	$DOC_ChipID_Doc2k, %al
+	movw	$(doc_2k_string), %si	
+	je	print_id
+
+	cmpb	$DOC_ChipID_DocMil, %al
+	movw	$(doc_mil_string), %si	
+	je	print_id
+
+	call	phbyte
+	jmp	print_seg
+
+print_id:
+	call	message
+
+print_seg:
+	movw	%ds, %ax
+	call	phword
+	MSG(eol_string)
+	popw	%si
+	popw	%bx
+#endif		
+	/* defined(SHOW_INFO) */
+	/*****************************************************/
+
+       /* figure out if we have a 256-byte or 512-byte device:
+       ** 
+       ** Basically, we know that the DiskOnChip 2000 IPL ROM will 
+       ** load only the first 256 bytes of each 512-byte page
+       ** from a 512-byte-page device, but it'll load 
+       ** the data from a 256-byte-page device.
+       **
+       ** Therefore, we'll put a copy of the code that normally resides
+       ** at offset 0x100..0x1FF at 0x200.  This way, the correct
+       ** code will be there, regardless of which device we have.
+       **
+       ** The DoC Millennium is slightly different -- here, we are
+       ** actually replacing the IPL, so this code duplication isn't
+       ** necessary.
+       */
+
+       movb    $NAND_CMD_READID, %ah
+       call    doc_cmd
+
+       /* Use existing block read routine to get Manufacturer &
+       ** Chip ID.  Overkill, but saves code space.  */  
+
+       movw    $1, %cx		/* read just one block */
+       movw    $GRUBSTART >> 4, %ax
+       movw    %ax, %es
+       movw    $0, %di
+       call    read_block
+
+       movw    %es:0, %ax	/* id/mfr in first 2 bytes */
+
+#ifdef SHOW_INFO
+
+       /* Display Chip ID and Manufacturer Code */
+
+       pushw   %ax
+       pushw   %bx
+       pushw   %si
+       call    phword
+       MSG(eol_string)
+       popw    %si
+       popw    %bx
+       popw    %ax
+
+#endif
+
+       /* Look at the device ID code to determine if we have 256 byte pages.
+       ** Based on the "nand_flash_dev" table in ../stage2/bdev_diskonchip.c,
+       ** there are currently only two ID codes that are 256: 0x64 and 0xea. 
+       ** All the other types of chips have 512 byte pages. */
+       
+       cmpb    $0x64,%ah
+       je      have_256
+
+       cmpb    $0xEA,%ah
+       jne     get_grub
+
+have_256:
+
+       movb    $1,%cs:page256
+
+get_grub:
+
+       /* Load %CX with the number of 256-byte blocks to load */       
+       movw    $LOADLEN, %cx
+
+       /* Set up our target address for writing stage2 */
+       movw    $GRUBLOADSEG, %ax
+       movw    %ax, %es
+       movw    $GRUBLOADOFS, %di
+
+       /* Stage2 proper starts at offset 0x300 of the DoC 2000 
+	  flash and offset 0x400 of the DoC Millennium flash. We
+	  have defined the load address GRUBLOADSEG so that
+	  we're loading this to 0x8000:0000 or 0x8000:0200, 
+	  depending on whether this is old or GNU Grub, 
+	  respectively.
+       */
+
+       call    copy_grub
+
+good:  
+       MSG(msgjmp)
+	       /* Run it:       jmpf 0:8200 */
+       .byte   0xea
+       .word   GRUBSTART,0
+
+       
+       /* Routines used by both 256- and 512- byte/page loaders. */
+       
+	/* doc_cmd:      Send a command to the flash chip */
+doc_cmd:
+	/* Enable CLE line to flash */
+	movb    $CDSN_CTRL_FLASH_IO + CDSN_CTRL_WP + CDSN_CTRL_CLE + CDSN_CTRL_CE, BX_CDSNControl
+	/* Dummy */
+	incw    BX_ChipID
+	/* Write the actual command */
+	movb    %ah,BX_SlowIO
+	movb    %ah,(%si)
+	/* Disable CLE */
+	incw    BX_ChipID
+	movb    $CDSN_CTRL_FLASH_IO + CDSN_CTRL_WP + CDSN_CTRL_CE, BX_CDSNControl
+
+	/* doc_wait:     Wait for the DiskOnChip to be ready */
+doc_wait:
+	incw    BX_ChipID
+l38:    
+	testb   $0x80,BX_CDSNControl
+	jz      l38
+	ret
+
+       /* copy_grub: copies GRUB code from flash to RAM */
+
+ copy_grub:
+
+       /* 256 bytes/page: Send new READ0 command
+       ** 512 bytes/page: READ0 or READ1, depending on address */
+
+       xorb   %ah, %ah
+       call    is_256_byte
+       je      send_cmd
+
+       movw    %di, %ax
+       andb    $1,%ah
+
+ send_cmd:
+
+       call    doc_cmd
+
+ read_block:
+
+       /* Start of new block. Set address */
+       movb    $CDSN_CTRL_FLASH_IO + CDSN_CTRL_WP + CDSN_CTRL_ALE + CDSN_CTRL_CE, BX_CDSNControl
+       incw    BX_ChipID
+
+       /* For 256 bytes/page, we send bits 0-7, then 8-15, then 16-23 */
+       /* For 512, We send bits 0-7, then 9-16, then 17-23 */
+       /* Yes bit 8 is missing. Read the NAND flash specs */           
+
+       movw    %di,%dx
+       
+       /* Bits 0-7 are always zero */
+       movb    $0,BX_SlowIO
+       movb    $0,(%si)
+
+       /* Bits 8-15 (256), or Bits 9-16 (512) */
+
+       call    is_256_byte
+       je      adrbytemid
+
+       shrw    $1,%dx
+       .byte   0x80, 0xce /* orb adrbit16, %dh */
+ adrbit16:     
+       .byte   0
+
+ adrbytemid:
+
+       movb    %dh,BX_SlowIO
+       movb    %dh,(%si)
+
+       /* Bits 16-23 (256), or Bits 17-24 (512) */
+
+       .byte   0xb2 /* movb adrbytehi, %dl */
+ adrbytehi:
+       .byte   0
+
+       movb    %dl,BX_SlowIO
+       movb    %dl,(%si)
+
+       /* Clear the ALE line to the flash chip */
+       movb    $CDSN_CTRL_FLASH_IO + CDSN_CTRL_WP + CDSN_CTRL_CE, BX_CDSNControl
+       call    doc_wait
+
+       pushw   %cx             /* Store the 'blocks remaining' count */
+       movw    $0x100, %cx     /* Set up to copy 0x100 bytes */
+       cmpw    $DoC_2k_CDSN_IO, %si
+       je      mil_spec_end1
+       testb   BX_ReadPipeInit, %al /* Millennium should use the */
+       decw    %cx             /* LastDataRead register - Pipeline Reads */
+ mil_spec_end1:
+
+ readbyte:     
+       movsb                   /* movb (%si), %al ; stosb would be more */
+       decw    %si             /* obvious, but would take an extra byte. */
+       loop    readbyte
+
+       cmpw    $DoC_2k_CDSN_IO, %si
+       je      mil_spec_end2
+       movb    BX_LastDataRead, %al
+       stosb
+ mil_spec_end2:
+
+       testw   $0xffff, %di    /* Check if we've done a whole 0x10000 bytes */
+       jnz     endloop         /* No - continue regardless */
+
+       /* Yes - increase %es */
+       movw    %es, %cx
+       addb    $0x10, %ch
+       movw    %cx, %es
+
+       call    is_256_byte
+       je      inc_hibyte
+
+       addb    $0x80, %cs:adrbit16     /* Increase bit 16 */
+       jnc     endloop         /* Did it overflow? */
+
+ inc_hibyte:
+       incb    %cs:adrbytehi           /* If so, increase the high byte too */
+
+ endloop:      
+       popw    %cx             /* Restore the 'blocks remaining' count */
+       loop    copy_grub       /* Loop till completely done */
+       ret
+
+ is_256_byte:
+       cmpb    $1,%cs:page256
+       ret
+
+
+/*
+ * message: write the string pointed to by %si
+ *
+ *   WARNING: trashes %si, %ax, and %bx
+ */
+
+	/*
+	 * Use BIOS "int 10H Function 0Eh" to write character in teletype mode
+	 *	%ah = 0xe	%al = character
+	 *	%bh = page	%bl = foreground color (graphics modes)
+	 */
+1:
+	call	printchar
+message:
+	lodsb	%cs:(%si)
+	cmpb	$0, %al
+	jne	1b	/* if not end of string, jmp to display */
+	ret
+
+#ifdef SHOW_INFO
+       /* Hex output routines, used at one point in debugging */       
+phword:
+	pushw   %ax
+	xchgb   %al,%ah
+	call    phbyte
+	movb    %ah,%al
+	call    phbyte
+	popw    %ax
+	ret
+
+phbyte:
+	pushw   %ax
+	movb    %al, %ah
+	shrb    $4,%al
+	call    phnibble
+	movb    %ah, %al
+	call    phnibble
+	popw    %ax
+	ret
+
+phnibble:
+	pushw   %ax
+	andb    $0xf,%al
+	addb    $48,%al
+	cmpb    $57,%al
+	jna     ph1
+	add     $7,%al
+ph1:    call   printchar
+	popw    %ax
+	ret
+
+printchar:
+	movb	$0xe, %ah
+	movw	$0x0001, %bx
+	int	$0x10
+	ret
+
+#endif         /* defined(SHOW_INFO) */
+
+doc_seg:	.word 0
+page256:       .byte   0
+
+#ifdef DOC_CTRL_BYPASS
+bios_orig_handle_adr:	.long 0
+#endif
+
+doc_bios_scan_start_string:	.string "DoC found\r\n"
+doc_bios_hook_execed_string:	.string "DoC "
+doc_2k_string:			.string "2000 "
+doc_mil_string:			.string "Mil "
+eol_string:			.string "\r\n"
+msgjmp:				.string "Grub:\r\n"
+       .org 0x1ff
+       .byte 0 /* checksum */
+
diff -urN ../orig/stage1/doc_stage1.h ./stage1/doc_stage1.h
--- ../orig/stage1/doc_stage1.h	Wed Dec 31 19:00:00 1969
+++ ./stage1/doc_stage1.h	Tue Oct  8 16:55:27 2002
@@ -0,0 +1,142 @@
+/* -*-Asm-*- */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2000 Machine Vision Holdings, Inc.
+ *
+ *  Author: David Woodhouse <dwmw2 at infradead.org>
+ *
+ *  $Id: grub-2002-07-29-doc.patch,v 1.2 2002/08/02 20:42:53 ilatypov Exp $
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/* SHOW_INFO displays the DiskOnChip ChipID (2000 or Millennium) and
+ * its window segment at the expense of extra code */
+#define SHOW_INFO 1
+
+#define DoC_ChipID 0x1000
+#define DoC_DOCStatus 0x1001
+#define DoC_DOCControl 0x1002
+#define DoC_FloorSelect 0x1003
+#define DoC_CDSNControl 0x1004
+#define DoC_CDSNDeviceSelect 0x1005
+#define DoC_ECCConf 0x1006
+#define DoC_2k_ECCStatus 0x1007
+
+#define DoC_CDSNSlowIO 0x100d
+#define DoC_ECCSyndrome0 0x1010
+#define DoC_ECCSyndrome1 0x1011
+#define DoC_ECCSyndrome2 0x1012
+#define DoC_ECCSyndrome3 0x1013
+#define DoC_ECCSyndrome4 0x1014
+#define DoC_ECCSyndrome5 0x1015
+#define DoC_AliasResolution 0x101b
+#define DoC_ConfigInput 0x101c
+#define DoC_ReadPipeInit 0x101d
+#define DoC_WritePipeTerm 0x101e
+#define DoC_LastDataRead 0x101f
+#define DoC_NOP 0x1020
+
+#define DoC_Mil_CDSN_IO 0x0800
+#define DoC_2k_CDSN_IO 0x1800
+
+#define DOC_MODE_RESET 0
+#define DOC_MODE_NORMAL 1
+#define DOC_MODE_RESERVED1 2
+#define DOC_MODE_RESERVED2 3
+
+#define DOC_MODE_MDWREN 4
+#define DOC_MODE_CLR_ERR 0x80
+
+#define DOC_ChipID_Doc2k 0x20
+#define DOC_ChipID_DocMil 0x30
+
+#define CDSN_CTRL_FR_B 0x80
+#define CDSN_CTRL_ECC_IO 0x20
+#define CDSN_CTRL_FLASH_IO 0x10
+#define CDSN_CTRL_WP 8
+#define CDSN_CTRL_ALE 4
+#define CDSN_CTRL_CLE 2
+#define CDSN_CTRL_CE 1
+
+	
+#define NAND_CMD_READ0 0
+#define NAND_CMD_READ1 1
+#define NAND_CMD_READID 0x90
+#define NAND_CMD_RESET 0xff
+
+#include "stage2_size.h"
+
+/*
+ *  defines for the code go here
+ */
+
+/*
+ * Whether we hook to the Bootstrap Loader INT 0x19
+ * or to the Diskless Boot INT 0x18
+ * See 
+ *    http://etherboot.sourceforge.net/doc/html/devman-3.html
+ *    http://www.ctyme.com/intr/rb-2270.htm
+ *    http://www.ctyme.com/intr/rb-2241.htm
+ */
+#ifdef DOC_BIOS_NETBOOT
+#define DOC_BIOS_HOOK 0x18
+#else
+#define DOC_BIOS_HOOK 0x19
+#endif
+
+
+#define LOADLEN (((STAGE2_SIZE + 0xff) & ~0xff) >> 8)
+
+#ifdef DOC_MIL
+/*
+ * We place GRUB stage 2 at offset 0x400 of DoC Millennium flash memory
+ * to leave room for 2 copies of stage 1 (IPL) at offset 0.  In this way
+ * the Download Engine can load the backup copy of IPL from offset 0x200.
+ */
+#  define GRUBLOADOFS	0x400
+#else
+/*
+ * DoC 2000 IPL is stored in ROM.  It will download stage 1 from two 256
+ * byte blocks of flash memory.  The first block is at the start.  The
+ * second block is at offset 0x100 if DoC 2000 page size is 256 bytes.  
+ * If the page size is 512 bytes, the second block gest loaded off 0x200.
+ * Stage 2 can be stored at offset 0x300 in both cases.
+ */
+#  define GRUBLOADOFS	0x300
+#endif
+
+#ifdef OLDGRUB /* Loading Erich's old grub */
+# define GRUBSTART	0x8000
+#else /* Loading FSF grub */
+# define GRUBSTART	0x8200
+#endif
+
+#define GRUBLOADSEG	((GRUBSTART - GRUBLOADOFS) >> 4)
+
+	/* Some macros to make it obvious what we're accessing */
+#define BXREG			DoC_CDSNControl
+
+#define BX_ChipID		(DoC_ChipID - BXREG)(%bx)
+#define BX_DOCControl		(DoC_DOCControl - BXREG)(%bx)
+				/* "movb (%bx), %al" takes only 2 bytes */
+#define BX_CDSNControl		(%bx)
+#define BX_SlowIO		(DoC_CDSNSlowIO - BXREG)(%bx)
+#define BX_ReadPipeInit		(DoC_ReadPipeInit - BXREG)(%bx)
+#define BX_LastDataRead		(DoC_LastDataRead - BXREG)(%bx)
+
+			/* Print message string */
+#define MSG(x)	movw $(x), %si; call message
+
diff -urN ../orig/stage1/makecsum.c ./stage1/makecsum.c
--- ../orig/stage1/makecsum.c	Wed Dec 31 19:00:00 1969
+++ ./stage1/makecsum.c	Tue Oct  8 16:55:27 2002
@@ -0,0 +1,226 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#define BUFSIZE 16384
+unsigned char buf[BUFSIZE];
+
+main(int argc, char **argv)
+{
+	int len, i, stubfd, st2fd, outfd, retval = 0;
+	unsigned char checksum = 0;
+	int isMillennium = 0;
+	unsigned char total;
+
+	memset(buf, 0xff, BUFSIZE);
+
+	if (argc < 4) {
+		fprintf(stderr, "Usage: makecsum [-m] <stubfile> <stage2> "
+				"<outfile> [checksum]\n");
+		exit(1);
+	}
+
+	i = 1;
+
+	if (!strcmp(argv[i], "-m")) {
+		isMillennium = 1;
+		i++;
+	}
+	
+	stubfd = open(argv[i++], O_RDONLY);
+	if (stubfd < 0) {
+		perror("open stub file");
+		exit(1);
+	}
+
+	st2fd = open(argv[i++], O_RDONLY);
+	if (st2fd < 0) {
+		perror("open stage2 file");
+		close(stubfd);
+		exit(1);
+	}
+
+	outfd = open(argv[i++], O_WRONLY | O_CREAT | O_TRUNC, 0664);
+	if (outfd < 0) {
+		perror("open output file");
+		exit(1);
+	}
+
+	if ((argc >= i + 1) && argv[i][0]) {
+		total = strtoul(argv[i++], NULL, 0);
+	} else {
+		total = isMillennium ? 0x00 : 0x55;
+	}
+
+	if (isMillennium) {
+		len =  read(stubfd, buf, 513);
+		if (len < 0) {
+			perror("read from stub file");
+			close(outfd);
+			close(st2fd);
+			close(stubfd);
+			exit(1);
+		}
+
+		if (len > 512) {
+			fprintf(stderr, "stub file is too large (>512 bytes)\n");
+			close(outfd);
+			close(st2fd);
+			close(stubfd);
+			exit(1);
+		}
+
+		close(stubfd);
+
+		/* Calculate the DoC Millennium IPL checksum for BIOS */
+		buf[511] = 0;
+
+		i = 0;
+		while (i < 511) {
+			checksum += buf[i++];
+		}
+
+		/* Set the slack byte to fix the csum */
+		buf[511] = total - checksum;
+
+		/*
+		 * Write the original buffer twice.
+		 * The second copy will be used by the DoC Millennium
+		 * Download Engine in case the ECC check fails on the 
+		 * first copy.
+		 */
+		for (i = 0; i < 2; i++) {
+			len = write(outfd, buf, 512);
+
+			if (len < 0) {
+				perror("write output file");
+				close(outfd);
+				close(st2fd);
+				exit(1);
+			}
+
+			if (len < 512) {
+				fprintf(stderr, "short write of "
+					"output file (%d bytes < 512)\n", len);
+				close(outfd);
+				close(st2fd);
+				exit(1);
+			}
+		}
+	} else {	/* DoC 2000 */
+		/* Read the stub loader (stage1) */
+
+		memset(buf, sizeof(buf), 0);
+		len =  read(stubfd,buf,513);
+		if (len < 0) {
+			perror("read from stub file");
+			close(outfd);
+			close(st2fd);
+			close(stubfd);
+			exit(1);
+		}
+
+		if (len > 512) {
+			fprintf(stderr, "stub file is "
+					"too large (>512 bytes)\n");
+			close(outfd);
+			close(st2fd);
+			close(stubfd);
+			exit(1);
+		}
+
+		close(stubfd);
+
+		/* Duplicate bytes 256 through 511 to 512 */
+		memcpy(&buf[512], &buf[256], 256);
+
+		/* Read enough of the remainder to calculate the csum */
+		
+		len = read(st2fd, buf+768, BUFSIZE-768);
+		if (len < 0) {
+			perror("read from stage2 file\n");
+			close(outfd);
+			close(st2fd);
+			exit(1);
+		}
+
+
+		/* Calculate the csum for 512-byte-page devices */
+		buf[767] = 0;
+
+		i=0;
+		while (i<BUFSIZE) {
+			checksum += buf[i];
+			i++;
+			if ((i >> 8)&1)
+				i += 256;
+		}
+
+		/* Set the slack byte to fix the csum */
+		buf[767] = total - checksum;
+
+		/* Calculate the csum for 256-byte-page devices */
+		checksum = 0;
+
+		buf[511] = 0;
+
+		i=0;
+		while (i<8192) {
+			checksum += buf[i++];
+		}
+
+		/* Set the slack byte to fix the csum */
+		buf[511] = total - checksum;
+
+
+		/* Write the original buffer */
+		len = write(outfd, buf, BUFSIZE);
+
+		if (len < 0) {
+			perror("write output file");
+			close(outfd);
+			close(st2fd);
+			exit(1);
+		}
+
+		if (len < BUFSIZE) {
+			fprintf(stderr, "short write of "
+				"output file (%d bytes < %d)\n", 
+				len, (int)BUFSIZE);
+			close(outfd);
+			close(st2fd);
+			exit(1);
+		}
+	}
+
+	/* Now chuck out the rest of the stage2 */
+
+	while (1) {
+		len = read(st2fd, buf, BUFSIZE);
+		if (len < 0) {
+			perror("read stage2 file");
+			retval = 1;
+			break;
+		}
+		if (len == 0)
+			break;
+		
+		i = write(outfd, buf, len);
+		if (i < 0) {
+			perror("write output file");
+			retval = 1;
+			break;
+		}
+		if (i<len) {
+			fprintf(stderr, "short write of "
+				"output file (%d bytes < %d)\n", i, len);
+			retval = 1;
+			break;
+		}
+	}
+		
+	close(outfd);
+	close(st2fd);
+	exit(retval);
+}
diff -urN ../orig/stage2/Makefile.am ./stage2/Makefile.am
--- ../orig/stage2/Makefile.am	Tue Oct  8 16:53:02 2002
+++ ./stage2/Makefile.am	Tue Oct  8 16:55:27 2002
@@ -83,7 +83,7 @@
 
 # For stage2 target.
 pre_stage2_exec_SOURCES = asm.S bios.c boot.c builtins.c char_io.c \
-	cmdline.c common.c console.c disk_io.c fsys_ext2fs.c \
+	cmdline.c common.c console.c bdev_diskonchip.c disk_io.c fsys_ext2fs.c \
 	fsys_fat.c fsys_ffs.c fsys_jfs.c fsys_minix.c fsys_reiserfs.c \
 	fsys_vstafs.c fsys_xfs.c gunzip.c hercules.c md5.c serial.c \
 	smp-imps.c stage2.c
diff -urN ../orig/stage2/asm.S ./stage2/asm.S
--- ../orig/stage2/asm.S	Tue Oct  8 16:53:02 2002
+++ ./stage2/asm.S	Tue Oct  8 16:55:27 2002
@@ -133,7 +133,7 @@
 
 	sti		/* we're safe again */
 
-#ifndef SUPPORT_DISKLESS
+#if !defined(SUPPORT_DISKLESS) && !defined(BDEV_DISKONCHIP)
 	/* save boot drive reference */
 	ADDR32	movb	%dl, EXT_C(boot_drive)
 
@@ -2256,8 +2256,10 @@
 	.long	PROTSTACKINIT
 
 VARIABLE(boot_drive)
-#ifdef SUPPORT_DISKLESS
+#if defined(SUPPORT_DISKLESS)
 	.long	NETWORK_DRIVE
+#elif defined(BDEV_DISKONCHIP)
+	.long	DISK_ON_CHIP
 #else
 	.long	0
 #endif
diff -urN ../orig/stage2/bdev_diskonchip.c ./stage2/bdev_diskonchip.c
--- ../orig/stage2/bdev_diskonchip.c	Wed Dec 31 19:00:00 1969
+++ ./stage2/bdev_diskonchip.c	Tue Oct  8 16:55:27 2002
@@ -0,0 +1,1068 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1996  Erich Boleyn  <erich at uruk.org>
+ *  Copyright (C) 1999  Free Software Foundation, Inc.
+ *  Copyright (C) 2000  David Woodhouse  <dwmw2 at infradead.org>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifdef BDEV_DISKONCHIP
+
+#include "shared.h"
+
+#define DoC_ChipID 0x1000
+#define DoC_DOCStatus 0x1001
+#define DoC_DOCControl 0x1002
+#define DoC_FloorSelect 0x1003
+#define DoC_CDSNControl 0x1004
+#define DoC_CDSNDeviceSelect 0x1005
+#define DoC_ECCConf 0x1006
+#define DoC_2k_ECCStatus 0x1007
+
+#define DoC_CDSNSlowIO 0x100d
+#define DoC_ECCSyndrome0 0x1010
+#define DoC_ECCSyndrome1 0x1011
+#define DoC_ECCSyndrome2 0x1012
+#define DoC_ECCSyndrome3 0x1013
+#define DoC_ECCSyndrome4 0x1014
+#define DoC_ECCSyndrome5 0x1015
+#define DoC_AliasResolution 0x101b
+#define DoC_ConfigInput 0x101c
+#define DoC_ReadPipeInit 0x101d
+#define DoC_WritePipeTerm 0x101e
+#define DoC_LastDataRead 0x101f
+#define DoC_NOP 0x1020
+
+#define DoC_Mil_CDSN_IO 0x0800
+#define DoC_2k_CDSN_IO 0x1800
+
+#define DOC_MODE_RESET 0
+#define DOC_MODE_NORMAL 1
+#define DOC_MODE_RESERVED1 2
+#define DOC_MODE_RESERVED2 3
+
+#define DOC_MODE_MDWREN 4
+#define DOC_MODE_CLR_ERR 0x80
+
+#define DOC_ChipID_Doc2k 0x20
+#define DOC_ChipID_DocMil 0x30
+#define DoC_is_Millennium(id) (id == DOC_ChipID_DocMil)
+
+#define CDSN_CTRL_FR_B 0x80
+#define CDSN_CTRL_ECC_IO 0x20
+#define CDSN_CTRL_FLASH_IO 0x10
+#define CDSN_CTRL_WP 8
+#define CDSN_CTRL_ALE 4
+#define CDSN_CTRL_CLE 2
+#define CDSN_CTRL_CE 1
+
+#define DOC_ECC_RESET 0
+#define DOC_ECC_ERROR 0x80
+#define DOC_ECC_RW 0x20
+#define DOC_ECC__EN 0x08
+#define DOC_TOGGLE_BIT 0x04
+#define DOC_ECC_RESV 0x02
+/* We have to also set the reserved bit 1 for enable */
+#define DOC_ECC_EN (DOC_ECC__EN | DOC_ECC_RESV)
+#define DOC_ECC_DIS (DOC_ECC_RESV)
+
+#define MAX_FLOORS 4
+#define MAX_CHIPS 4
+
+#define NAND_CMD_READ0 0
+#define NAND_CMD_READ1 1
+#define NAND_CMD_PAGEPROG 0x10
+#define NAND_CMD_READOOB 0x50
+#define NAND_CMD_ERASE1 0x60
+#define NAND_CMD_STATUS 0x70
+#define NAND_CMD_SEQIN 0x80
+#define NAND_CMD_READID 0x90
+#define NAND_CMD_ERASE2 0xd0
+#define NAND_CMD_RESET 0xff
+
+#define NAND_MFR_TOSHIBA 0x98
+#define NAND_MFR_SAMSUNG 0xec
+
+/*
+ * NAND Flash Device ID Structure
+ *
+ * Structure overview:
+ *
+ *  name - Complete name of device
+ *
+ *  manufacture_id - manufacturer ID code of device.
+ *
+ *  model_id - model ID code of device.
+ *
+ *  chipshift - total number of address bits for the device which
+ *              is used to calculate address offsets and the total
+ *              number of bytes the device is capable of.
+ *
+ *  page256 - denotes if flash device has 256 byte pages or not.
+ *
+ *  pageadrlen - number of bytes minus one needed to hold the
+ *               complete address into the flash array. Keep in
+ *               mind that when a read or write is done to a
+ *               specific address, the address is input serially
+ *               8 bits at a time. This structure member is used
+ *               by the read/write routines as a loop index for
+ *               shifting the address out 8 bits at a time.
+ *
+ *  erasesize - size of an erase block in the flash device.
+ */
+struct nand_flash_dev {
+	char * name;
+	int manufacture_id;
+	int model_id;
+	int chipshift;
+	char page256;
+	char pageaddrlen;
+	unsigned long erasesize;
+};
+
+static struct nand_flash_dev nand_flash_ids[] = {
+	{"Toshiba TC5816BDC",     NAND_MFR_TOSHIBA, 0x64, 21, 1, 2, 0x1000},
+	{"Toshiba TC5832DC",      NAND_MFR_TOSHIBA, 0x6b, 22, 0, 2, 0x2000},
+	{"Toshiba TH58V128DC",    NAND_MFR_TOSHIBA, 0x73, 24, 0, 2, 0x4000},
+	{"Toshiba TC58256FT/DC",  NAND_MFR_TOSHIBA, 0x75, 25, 0, 2, 0x4000},
+	{"Toshiba TH58512FT",     NAND_MFR_TOSHIBA, 0x76, 26, 0, 3, 0x4000},
+	{"Toshiba TC58V32DC",     NAND_MFR_TOSHIBA, 0xe5, 22, 0, 2, 0x2000},
+	{"Toshiba TC58V64AFT/DC", NAND_MFR_TOSHIBA, 0xe6, 23, 0, 2, 0x2000},
+	{"Toshiba TC58V16BDC",    NAND_MFR_TOSHIBA, 0xea, 21, 1, 2, 0x1000},
+	{"Samsung KM29N16000",    NAND_MFR_SAMSUNG, 0x64, 21, 1, 2, 0x1000},
+	{"Samsung unknown 4Mb",   NAND_MFR_SAMSUNG, 0x6b, 22, 0, 2, 0x2000},
+	{"Samsung KM29U128T",     NAND_MFR_SAMSUNG, 0x73, 24, 0, 2, 0x4000},
+	{"Samsung KM29U256T",     NAND_MFR_SAMSUNG, 0x75, 25, 0, 2, 0x4000},
+	{"Samsung unknown 64Mb",  NAND_MFR_SAMSUNG, 0x76, 26, 0, 3, 0x4000},
+	{"Samsung KM29W32000",    NAND_MFR_SAMSUNG, 0xe3, 22, 0, 2, 0x2000},
+	{"Samsung unknown 4Mb",   NAND_MFR_SAMSUNG, 0xe5, 22, 0, 2, 0x2000},
+	{"Samsung KM29U64000",    NAND_MFR_SAMSUNG, 0xe6, 23, 0, 2, 0x2000},
+	{"Samsung KM29W16000",    NAND_MFR_SAMSUNG, 0xea, 21, 1, 2, 0x1000},
+	{NULL,}
+};
+
+
+#define MAX_NFTLS 16
+
+#define ERASE_MARK 0x3c69
+#define BLOCK_FREE 0xffff
+#define BLOCK_USED 0x5555
+#define BLOCK_IGNORE 0x1111
+#define BLOCK_DELETED 0x0000
+
+struct NFTLMediaHeader
+{
+  char DataOrgID[6];
+  unsigned short NumEraseUnits;
+  unsigned short FirstPhysicalEUN;
+  unsigned long FormattedSize;
+  unsigned char UnitSizeFactor;
+} __attribute__((packed));
+
+struct NFTLrecord
+{
+  unsigned short MediaUnit, SpareMediaUnit;
+  struct NFTLMediaHeader MediaHdr;
+  unsigned short numvunits;
+  unsigned short lastEUN; /* last + 1 */
+};
+
+/* Block Control Information */
+
+struct nftl_bci
+{
+  unsigned char ECCSig[6];
+  unsigned short Status;
+} __attribute__((packed));
+
+/* Unit Control Information */
+
+struct nftl_uci0
+{
+  unsigned short VirtUnitNum;
+  unsigned short ReplUnitNum;
+  unsigned short SpareVirtUnitNum;
+  unsigned short SpareReplUnitNum;
+} __attribute__((packed));
+
+struct nftl_uci1
+{
+  unsigned long WearInfo;
+  unsigned short EraseMark;
+  unsigned short EraseMark1;
+} __attribute__((packed));
+
+struct nftl_uci2
+{
+  unsigned long WriteInh;
+  unsigned long unused;
+} __attribute__((packed));
+
+union nftl_uci
+{
+  struct nftl_uci0 a;
+  struct nftl_uci1 b;
+  struct nftl_uci2 c;
+};
+
+struct nftl_oob
+{
+  struct nftl_bci b;
+  union nftl_uci u;
+};
+
+static int doc_inited = 0;
+static volatile unsigned char *docloc = NULL;
+static unsigned char docid = 0;
+static char *docname = NULL;
+static int eccstatusreg = 0;
+static int ioreg = 0;
+static int numchips[MAX_FLOORS];
+static int totalchips = 0;
+static int chipshift = 0;
+static char page256 = 0;
+static char pageaddrlen = 0;
+static unsigned long erasesize = 0;
+static unsigned long erasesect = 0;
+static int current_floor = -1;
+static int current_chip = -1;
+static unsigned char nftlbuf[512];
+
+static struct NFTLrecord NFTLs[MAX_NFTLS];
+static int nNFTLs = 0;
+
+/* badtable cache */
+static unsigned char badtableblock[512];
+static int badtablestart = -1;
+static int badtablenftl = -1;
+
+/* Perform the required delay cycles by reading from the appropriate register */
+static void DoC_Delay(unsigned short cycles)
+{
+	volatile char dummy;
+	int i;
+	
+	for (i = 0; i < cycles; i++) {
+		if (DoC_is_Millennium(docid))
+			dummy = docloc[DoC_NOP];
+		else
+			dummy = docloc[DoC_DOCStatus];
+	}
+	
+}
+
+static int _DoC_WaitReady(void)
+{
+	short c=-1;
+
+	while (!(docloc[DoC_CDSNControl] & CDSN_CTRL_FR_B) && --c)
+		;
+
+	if (c == 0)
+		printf("_DoC_WaitReady timed out\n");
+
+	return (c==0);
+}
+
+static inline int DoC_WaitReady(void)
+{
+	int ret = 0;
+
+	DoC_Delay(4);
+	if (!(docloc[DoC_CDSNControl] & CDSN_CTRL_FR_B))
+		ret = _DoC_WaitReady();
+	DoC_Delay(2);
+
+	return ret;
+}
+
+static inline void slow_write(unsigned char data)
+{
+	if (DoC_is_Millennium(docid)) {
+		docloc[DoC_CDSNSlowIO] = data;
+		DoC_Delay(4);
+	}
+
+	docloc[ioreg] = data;
+}
+
+static inline void DoC_Command(unsigned char command, unsigned char xtraflags)
+{
+	DoC_WaitReady();
+	docloc[DoC_CDSNControl] = 
+		CDSN_CTRL_FLASH_IO | xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE;
+	DoC_Delay(4);
+
+	slow_write(command);
+
+	docloc[DoC_CDSNControl] = 
+		CDSN_CTRL_FLASH_IO | xtraflags | CDSN_CTRL_CE;
+	DoC_WaitReady();
+}
+
+static void DoC_Address(unsigned long address, unsigned char xtraflags1,
+	unsigned char xtraflags2, int read_cmd)
+{
+	if (read_cmd)
+		DoC_Command(!page256 && (address & 0x100) ? 
+			NAND_CMD_READ1 : NAND_CMD_READ0, CDSN_CTRL_WP);
+
+	xtraflags1 |= CDSN_CTRL_FLASH_IO;
+	docloc[DoC_CDSNControl] = xtraflags1 | CDSN_CTRL_ALE | CDSN_CTRL_CE;
+	DoC_Delay(4);
+
+	slow_write(address & 0xff);
+	address >>= 8;
+	if (!page256)
+		address >>= 1;
+	slow_write(address & 0xff);
+	address >>= 8;
+	slow_write(address & 0xff);
+	address >>= 8;
+	if (pageaddrlen > 2) {
+		slow_write(address & 0xff);
+	}
+	DoC_Delay(2);
+
+	docloc[DoC_CDSNControl] = xtraflags1 | xtraflags2 | CDSN_CTRL_CE;
+	DoC_Delay(4);
+
+	DoC_WaitReady();
+}
+
+static int DoC_IdentChip(unsigned char floor, unsigned char chip)
+{
+	int dummy, mfr, id;
+	struct nand_flash_dev *mfr_id;
+
+	docloc[DoC_FloorSelect] = floor;
+	docloc[DoC_CDSNDeviceSelect] = chip;
+
+	DoC_Command(NAND_CMD_RESET, CDSN_CTRL_WP);
+
+	if (DoC_WaitReady())
+		return 0;
+
+	/* Read the NAND chip ID: 1. Send ReadID command */
+	DoC_Command(NAND_CMD_READID, CDSN_CTRL_WP);
+
+	/* Read the NAND chip ID: 2. Send address byte zero */
+        DoC_Address(0, CDSN_CTRL_WP, 0, 0);
+
+	/* Read the manufacturer and device id codes from the device */
+	/* CDSN Slow IO register see Software Requirement 11.4 item 5. */
+	
+	dummy = docloc[DoC_CDSNSlowIO];
+	DoC_Delay(2);
+	mfr = docloc[ioreg];
+
+	dummy = docloc[DoC_CDSNSlowIO];
+	DoC_Delay(2);
+	id = docloc[ioreg];
+
+	if (mfr == 0xff || mfr == 0)
+		return 0;
+
+	/*
+        if (mfr != NAND_MFR_TOSHIBA && mfr != NAND_MFR_SAMSUNG)
+		return 0;
+	*/
+
+        switch (mfr)
+        {
+          case NAND_MFR_TOSHIBA:
+            printf("floor %d, chip %d; ", floor, chip);
+            printf("manufacturer: Toshiba\n");
+            break;
+          case NAND_MFR_SAMSUNG:
+            printf("floor %d, chip %d; ", floor, chip);
+            printf("manufacturer: Samsung\n");
+            break;
+          default:
+            printf("unknown manufacturer code: 0x%x\n", mfr);
+            return 0;
+        }
+
+        for (mfr_id = nand_flash_ids; mfr_id->name != NULL; mfr_id++)
+        {
+          if (mfr_id->manufacture_id == mfr && mfr_id->model_id == id)
+            break;
+        }
+        if (mfr_id->name == NULL)
+        {
+           printf("unknown model id 0x%x\n", (int)id);
+           return 0;
+        }
+        chipshift = mfr_id->chipshift;
+        page256 = mfr_id->page256;
+        pageaddrlen = mfr_id->pageaddrlen;
+        erasesize = mfr_id->erasesize;
+        erasesect = erasesize >> 9;
+	
+        printf("Chip: %s, total size: %d MiB\n", 
+		mfr_id->name, (int) (1 << (chipshift - 20)));
+        printf("       erase unit: %d bytes, page: %d bytes\n", 
+		(int) erasesize, (int) (page256 ? 256 : 512));
+	
+	DoC_WaitReady();
+
+	/* disable the ECC engine */
+	docloc[DoC_ECCConf] = DOC_ECC_RESV;
+
+	DoC_WaitReady();
+	return 1;
+}
+
+static void DoC_ScanChips(void)
+{
+	unsigned char floor, chip;
+	int ret;
+
+	for (floor = 0; floor < MAX_FLOORS; floor++) {
+		ret = 1;
+		numchips[floor]=0;
+		for (chip = 0; chip < MAX_CHIPS && ret != 0; chip++) {
+			ret = DoC_IdentChip(floor, chip);
+			if (ret) {
+				numchips[floor]++;
+				totalchips++;
+			}
+		}
+	}
+}
+
+
+static int DoC_Probe(volatile unsigned char *loc)
+{
+	unsigned char tmp;
+	unsigned char ChipID;
+#ifndef DOC_PASSIVE_PROBE
+	unsigned char tmp2;
+#endif
+	unsigned int eccstatus;
+	char *name;
+	unsigned int io;
+
+	if ((loc[0] != 0x55) || (loc[1] != 0xaa))
+	    return 0;
+	
+#ifndef DOC_PASSIVE_PROBE
+	tmp2 = loc[DoC_DOCControl];
+
+	loc[DoC_DOCControl] = 
+		DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET;
+	loc[DoC_DOCControl] = 
+		DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET;
+
+	loc[DoC_DOCControl] = 
+		DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL;
+	loc[DoC_DOCControl] = 
+		DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL;
+#endif
+
+	ChipID = loc[DoC_ChipID];
+
+	if ((ChipID != DOC_ChipID_Doc2k) && 
+	    (ChipID != DOC_ChipID_DocMil)) {
+		printf("Unknown ChipID %x at %x\n", ChipID, (unsigned int)loc);
+#ifndef DOC_PASSIVE_PROBE
+		loc[DoC_DOCControl] = tmp2;
+#endif
+		return 0;
+	}
+
+	if (DoC_is_Millennium(ChipID)) {
+		name = "Millennium";
+		eccstatus = DoC_ECCConf;
+		io = DoC_Mil_CDSN_IO;
+	} else {
+		name = "2000";
+		eccstatus = DoC_2k_ECCStatus;
+		io = DoC_2k_CDSN_IO;
+	}
+
+	/* See if the TOGGLE bit is toggling */
+
+	tmp = loc[eccstatus] & DOC_TOGGLE_BIT;
+	if (tmp == (loc[eccstatus] & DOC_TOGGLE_BIT)) {
+#ifndef DOC_PASSIVE_PROBE
+		loc[DoC_DOCControl] = tmp2;
+#endif
+		return 0;
+	}
+
+	/* OK. We're fairly sure it's a DiskOnChip now. */
+	printf("DiskOnChip %s found at %x\n", name, (unsigned int)loc);
+	docloc = loc;
+	docid = ChipID;
+	docname = name;
+	eccstatusreg = eccstatus;
+	ioreg = io;
+
+	DoC_ScanChips();
+
+	if (!totalchips)
+		return 0;
+	printf("Total of %d chips found - total capacity %d MiB\n",
+		    totalchips, totalchips * ( 1 << (chipshift -20)));
+	return 1;
+}
+
+static void doc_select_chip(int chip)
+{
+	docloc[DoC_CDSNControl] = CDSN_CTRL_WP;
+	DoC_Delay(4);
+	docloc[DoC_CDSNDeviceSelect] = chip;
+	current_chip = chip;
+	DoC_Delay(4);
+	docloc[DoC_CDSNControl] = CDSN_CTRL_CE | CDSN_CTRL_FLASH_IO | \
+				  CDSN_CTRL_WP;
+	DoC_Delay(4);
+	DoC_WaitReady();
+}
+
+static int doc_select_floor_chip(unsigned long sector)
+{
+	unsigned char chip, floor;
+
+	chip = sector >> (chipshift - 9);
+	floor = 0;
+
+	/* dprintf("sector %d\n", sector); */
+
+	while (chip >= numchips[floor]) {
+		dprintf("Chip %d Not on floor %d (%d chips)\n", 
+			chip, floor, numchips[floor]);
+		chip -= numchips[floor];
+		floor++;
+		if (floor == MAX_FLOORS)
+			return -1;
+	}
+		
+	/*
+	dprintf("sector 0x%x is on floor %d, chip %d\n", sector, floor, chip);
+	*/
+	
+	if (floor != current_floor) {
+		docloc[DoC_FloorSelect] = floor;
+		current_floor = floor;
+		DoC_WaitReady();
+		doc_select_chip(chip);
+	} else if (chip != current_chip) {
+		doc_select_chip(chip);
+	}
+
+	return 1;
+}
+
+static void DoC_ReadBuf(unsigned char *buf, int len)
+{
+	int i;
+	volatile int dummy;
+	unsigned int modulus = 0xffff;
+
+	if (DoC_is_Millennium(docid)) {
+		/* Read the data via the internal pipeline through 
+		   CDSN IO register, see Pipelined Read Operations 11.3 */
+		dummy = docloc[DoC_ReadPipeInit];
+
+		/* Millennium should use the LastDataRead register - 
+		   Pipeline Reads */
+		len--;
+
+		/* This is needed for correct ECC calculation */
+		modulus = 0xff;
+	}
+
+	for (i=0; i < len; i++) {
+		buf[i] = docloc[ioreg + (i & modulus)];
+	}
+
+	if (DoC_is_Millennium(docid)) {
+		buf[i] = docloc[DoC_LastDataRead];
+	}
+}
+
+static int doc_read_oob(unsigned long sector, void *buf)
+{
+	unsigned long ofs = (sector << 9);
+
+	// printf("doc_read_oob %d\n", (unsigned int) sector);
+	doc_select_floor_chip(sector);
+
+	/* update address for 2M x 8bit devices. OOB starts on the second */
+	/* page to maintain compatibility with doc_read_ecc. */
+	if (page256) {
+		/* read from the middle of 512 byte sector */
+		ofs += 0x100;
+	}
+
+	DoC_Command(NAND_CMD_READOOB, CDSN_CTRL_WP);
+	DoC_Address(ofs, CDSN_CTRL_WP, 0, 0);
+
+	/* treat crossing 8-byte OOB data for 2M x 8bit devices */
+	/* Note: datasheet says it should automaticaly wrap to the */
+	/*       next OOB block, but it didn't work here. mf.      */
+	if (page256) {
+		DoC_ReadBuf(buf, 8);
+		DoC_Command(NAND_CMD_READOOB, CDSN_CTRL_WP);
+
+		/* read from the beginning of 512 byte sector */
+		DoC_Address(ofs & (~0x1ff), CDSN_CTRL_WP, 0, 0);
+		DoC_ReadBuf((unsigned char *)buf + 8, 8);
+	} else {
+		DoC_ReadBuf(buf, 16);
+	}
+	DoC_WaitReady();
+        return 0;
+}
+
+static int doc_read_sector(unsigned long sector, unsigned char *buf)
+{
+        unsigned long address = (sector << 9);
+	// printf("doc_read_sector %d\n", (unsigned int) sector);
+	doc_select_floor_chip(sector);
+
+	DoC_Address(address, CDSN_CTRL_WP, 0, 1);
+
+	/* treat crossing 256-byte sector for 2M x 8bits devices */
+	if (page256) {
+		DoC_ReadBuf(buf, 256);
+		DoC_Address(address + 256, CDSN_CTRL_WP, 0, 1);
+		DoC_ReadBuf(buf + 256, 256);
+	} else {
+		DoC_ReadBuf(buf, 512);
+	}
+
+	DoC_WaitReady();
+        return 0;
+}
+
+static
+void NFTL_setup(unsigned long sect, struct NFTLMediaHeader *hdr)
+{
+  int i;
+  unsigned int eun = sect / erasesect;
+  struct NFTLrecord *nftl;
+
+  for (i=0; i<nNFTLs; i++)
+  {
+    if (NFTLs[i].MediaHdr.FirstPhysicalEUN == hdr->FirstPhysicalEUN)
+    {
+      /* This is a Spare Media Header for an NFTL we've already found */
+      printf("Spare Media Header for NFTL %d found in flash sector %d\n",
+        i, sect);
+      NFTLs[i].SpareMediaUnit = eun;
+      return;
+    }
+  }
+  printf("NFTL Media Header found in flash sector %d\n", sect);
+
+  if (hdr->UnitSizeFactor != 0xff && hdr->UnitSizeFactor != 0 ) 
+  {
+    printf("Sorry, we don't support UnitSizeFactor of != 1 yet\n");
+    return;
+  }
+
+  if (nNFTLs >= MAX_NFTLS)
+  {
+    printf("Maximum of NFTLs is exceeded\n");
+    return;
+  }
+
+  nftl = &NFTLs[nNFTLs++];
+  nftl->MediaHdr = *hdr;
+  nftl->MediaUnit = eun;
+  nftl->SpareMediaUnit = 0xffff;
+  nftl->numvunits = hdr->FormattedSize / erasesize;
+
+  nftl->lastEUN =
+    nftl->MediaHdr.NumEraseUnits + nftl->MediaHdr.FirstPhysicalEUN;
+}
+
+/* read badtable
+*/
+static
+int isbad(unsigned int nftl_num, int eun)
+{
+  int flag = 0;
+  int offset = eun / 512;
+  if (badtablenftl != nftl_num)
+    flag = 1;
+  else if (badtablestart != offset)
+    flag = 1;
+  if (flag)
+  {
+    doc_read_sector(
+      NFTLs[nftl_num].MediaUnit * erasesect + offset + 1, badtableblock
+    );
+    badtablenftl = nftl_num;
+    badtablestart = offset;
+  }
+  return badtableblock[eun % 512] == 0xff ? 0 : 1;
+}
+
+/* virtual block -> physical block hash
+*/
+#define max_blk_hash 4096
+#define blk_hash_func(a, b) ((b) & 0x0fff)
+static
+struct blk_hash_entry
+{
+  int vblock;
+  int pblock;
+  char nftl;
+} __attribute__((packed)) blk_hash[max_blk_hash];
+
+/* VUN -> firstEUN hash
+*/
+#define max_eun_hash 1024
+#define eun_hash_func(a, b) ((b) & 0x03ff)
+static
+struct eun_hash_entry
+{
+  unsigned short vun;
+  unsigned short eun;
+  char nftl;
+} __attribute__((packed)) eun_hash[max_eun_hash];
+
+static
+int nftl_findsect(unsigned int nftl_num, int block)
+{
+  struct NFTLrecord *nftl;
+  unsigned short vun, eun, save_stat, save_eun;
+  unsigned long sect;
+  struct nftl_oob oob, oob1;
+  unsigned int k, n;
+
+  if (nftl_num >= nNFTLs)
+    return -1;
+
+  nftl = &NFTLs[nftl_num];
+
+  k = blk_hash_func(nftl_num, block);
+  if (blk_hash[k].nftl == nftl_num &&
+      blk_hash[k].vblock == block)
+    return blk_hash[k].pblock;
+
+  blk_hash[k].nftl = nftl_num;
+  blk_hash[k].vblock = block;
+  blk_hash[k].pblock = -1;
+
+  /* dprintf("find virtual sector %d\n", block); */
+  vun = block / erasesect;
+  block = block % erasesect;
+  /* dprintf("virtual unit number %d, offset %d\n", vun, block); */
+
+  /* find first eun in a chain */
+  n = eun_hash_func(nftl_num, vun);
+  if (eun_hash[n].nftl == nftl_num && eun_hash[n].vun == vun)
+  {
+    eun = eun_hash[n].eun;
+    if (eun == 0xffff)
+      return -1;
+    sect = eun * erasesect;
+    /* Read Unit Control Information #0 */
+    if (doc_read_oob(sect, &oob) < 0)
+      return -1;
+  }
+  else
+  {
+    eun_hash[n].nftl = nftl_num;
+    eun_hash[n].vun = vun;
+    eun_hash[n].eun = 0xffff;
+    eun = nftl->MediaHdr.FirstPhysicalEUN;
+    sect = eun * erasesect;
+    for (; eun<nftl->lastEUN; eun++, sect += erasesect)
+    {
+      if (isbad(nftl_num, eun))
+      { dprintf("eun %d is bad\n", eun);
+        continue;
+      }
+      /* Read Unit Control Information #0 */
+      if (doc_read_oob(sect, &oob) < 0)
+        return -1;
+      if (oob.u.a.VirtUnitNum != oob.u.a.SpareVirtUnitNum)
+        continue;
+      if (oob.u.a.ReplUnitNum != oob.u.a.SpareReplUnitNum)
+        continue;
+      /* find first only! */
+      if (oob.u.a.VirtUnitNum == vun)
+        break;
+    }
+    if (eun == nftl->lastEUN)
+      return -1;
+    eun_hash[n].eun = eun;
+    /* dprintf("found first eun %d\n", eun); */
+  }
+
+  /* walk throw the chain */
+  save_stat = BLOCK_FREE;
+  save_eun = eun;
+  for (;;)
+  {
+    /* read block status */
+    if (doc_read_oob(sect + block, &oob1) < 0)
+      return -1;
+    /* end of block chain? */
+    if (oob1.b.Status == BLOCK_FREE)
+    { /* dprintf("block %d in eun %d is BLOCK_FREE\n", block, eun); */
+      break;
+    }
+    /* remember last 'active' status and EUN */
+    if (oob1.b.Status == BLOCK_USED ||
+        oob1.b.Status == BLOCK_DELETED)
+    {
+      save_stat = oob1.b.Status;
+      save_eun = eun;
+#ifdef DOC_THOROUGH_DEBUG
+      dprintf("block %d in eun %d is %s\n",
+        block, eun, save_stat == BLOCK_USED ? "BLOCK_USED" : "BLOCK_DELETED");
+#endif
+    }
+    /* is it last in eun chain? */
+    if (oob.u.a.ReplUnitNum == 0xffff)
+    { /* dprintf("eun %d is last in the chain\n", eun); */
+      break;
+    }
+    /* read next eun in the chain */
+    eun = oob.u.a.ReplUnitNum;
+    sect = eun * erasesect;
+    /* dprintf("read block in next eun %d\n", eun); */
+    if (doc_read_oob(sect, &oob) < 0)
+      return -1;
+    if (oob.u.a.VirtUnitNum != oob.u.a.SpareVirtUnitNum)
+      return -1;
+    if (oob.u.a.ReplUnitNum != oob.u.a.SpareReplUnitNum)
+      return -1;
+  }
+
+  if (save_stat == BLOCK_USED)
+  { /* dprintf("found block %d in eun %d\n",
+      save_eun * erasesect + block, save_eun); */
+    return
+      blk_hash[k].pblock = save_eun * erasesect + block;
+  }
+
+  return -1;
+}
+
+static int doc_init(void);
+
+static
+void NFTL_Scan(void)
+{
+  unsigned long sector;
+  struct NFTLMediaHeader *hdr = (struct NFTLMediaHeader *)nftlbuf;
+
+  /* total size in sectors */
+  unsigned long ssize = totalchips * (1 << (chipshift - 9));
+
+  printf("Scanning for NFTL Media Header\n");
+
+  /* Scan for NFTL partitions */
+  for (sector=0; sector<ssize; sector+=erasesect)
+  {
+    if (doc_read_sector(sector, nftlbuf) < 0)
+      continue;      
+    if (!strcmp(hdr->DataOrgID, "ANAND"))
+    {
+      dprintf("NFTL Media Header found in flash sector %d\n", sector);
+      dprintf("NumEraseUnits: %d\n", hdr->NumEraseUnits);
+      dprintf("FirstPhysicalEUN: %d\n", hdr->FirstPhysicalEUN);
+      dprintf("FormattedSize: %d\n", hdr->FormattedSize);
+      dprintf("UnitSizeFactor: %d\n", hdr->UnitSizeFactor);
+      NFTL_setup(sector, hdr);
+    }
+  }
+}
+
+static void doc_find(void)
+{
+	unsigned long probeloc = 0xc8000;
+	
+
+	for (probeloc = 0xc8000; probeloc < 0xf0000; probeloc += 0x2000) {
+		if (DoC_Probe((unsigned char *)probeloc)) {
+			NFTL_Scan();
+			return;
+		}
+	}
+	
+	printf("No DiskOnChip found\n");
+	return;
+}
+		
+static int doc_init(void)
+{
+  /* If this is the first time we've been called, 
+     we need to go searching for a DiskOnChip
+  */
+  if (!doc_inited)
+  {
+    int i;
+
+    for (i=0; i<max_blk_hash; i++)
+    {
+      blk_hash[i].nftl = -1;
+      blk_hash[i].vblock = -1;
+      blk_hash[i].pblock = -1;
+    }
+    
+    for (i=0; i<max_eun_hash; i++)
+    {
+      eun_hash[i].nftl = -1;
+      eun_hash[i].vun = 0xffff;
+      eun_hash[i].eun = 0xffff;
+    }
+
+    current_floor = -1;
+    current_chip = -1;
+
+    doc_find();
+
+    doc_inited = 1;
+  }
+
+  if (docloc == NULL)
+  {
+    errnum = ERR_NO_DISK;
+    return 0;
+  }
+  return 1;
+}
+
+int nftl_rawread(int drive, int sector, int byte_offset, int byte_len, char *buf)
+{
+  int size, block;
+
+  /* dprintf("nftl_rawread(%d, %d, %d, %d)\n", 
+	drive, sector, byte_offset, byte_len); */
+  if (byte_len < 0 || !buf || drive < 0 || drive >= MAX_NFTLS)
+  { errnum = ERR_BAD_ARGUMENT; return 0;
+  }
+
+  if (!doc_init())
+    return 0;
+
+  if (drive < 0 || drive >= nNFTLs)
+  { errnum = ERR_NO_DISK; return 0;
+  }
+
+  sector += (byte_offset >> 9);
+  byte_offset &= 511;
+
+  while (byte_len)
+  {
+    size = 512 - byte_offset;
+    if (byte_len < size) size = byte_len;
+
+    /* virtual unit number */
+    block = nftl_findsect(drive, sector);
+    if (block < 0)
+    { errnum = ERR_FSYS_CORRUPT; return 0;
+    }
+
+    if (doc_read_sector(block, nftlbuf) < 0)
+    { errnum = ERR_FSYS_CORRUPT; return 0;
+    }
+
+#ifdef DOC_THOROUGH_DEBUG
+  {
+    /* int sec = 16352;
+    printf("nftl_rawread: reading sector %d\n", sector);
+    if (doc_read_sector(sec, nftlbuf) < 0) {
+      printf("error\n");
+    } else { */
+      int i = 0;
+      while (i < SECTOR_SIZE) {
+        printf("%x    "
+          "%b %b %b %b %b %b %b %b "
+          "%b %b %b %b %b %b %b %b\n",
+           i, 
+           nftlbuf[i + 0], nftlbuf[i + 1], nftlbuf[i + 2], nftlbuf[i + 3], 
+           nftlbuf[i + 4], nftlbuf[i + 5], nftlbuf[i + 6], nftlbuf[i + 7],
+           nftlbuf[i + 8], nftlbuf[i + 9], nftlbuf[i + 10], nftlbuf[i + 11],
+           nftlbuf[i + 12], nftlbuf[i + 13], nftlbuf[i + 14], nftlbuf[i + 15]);
+        i += 16;
+      }
+      printf("Press any key..."); getkey(); printf("\n");
+    // }
+  }
+#endif
+
+#ifdef DOC_THOROUGH_DEBUG
+    printf("memcpy(0x%x, 0x%x, 0x%x)\n", 
+      (int)buf, (int)(nftlbuf + byte_offset), (int)size);
+#endif
+    if (memcpy(buf, nftlbuf + byte_offset, size) == NULL) {
+	printf("nftl_rawread(): error %d in memcpy() or earlier,"
+		" press any key...", errnum);
+	getkey();
+        printf("\n");
+        errnum = ERR_FSYS_CORRUPT;
+        return 0;
+     }
+#ifdef DOC_THOROUGH_DEBUG
+  {
+    int i = 0;
+    while (i < SECTOR_SIZE) {
+      printf("%x    "
+        "%b %b %b %b %b %b %b %b "
+        "%b %b %b %b %b %b %b %b\n",
+         i, 
+         buf[i + 0], buf[i + 1], buf[i + 2], buf[i + 3], 
+         buf[i + 4], buf[i + 5], buf[i + 6], buf[i + 7],
+         buf[i + 8], buf[i + 9], buf[i + 10], buf[i + 11],
+         buf[i + 12], buf[i + 13], buf[i + 14], buf[i + 15]);
+      i += 16;
+    }
+    printf("Press any key..."); getkey(); printf("\n");
+  }
+#endif
+
+#ifdef DOC_THOROUGH_DEBUG
+    dprintf("read sector %d (%d), bytes %d..%d\n",
+      sector, block, byte_offset, byte_offset + size - 1);
+#endif
+    sector++;
+    byte_offset = 0;
+    byte_len -= size;
+    buf += size;
+  }
+
+  errnum = ERR_NONE;
+  return 1;
+}
+
+int get_diskinfo_diskonchip (int drive, unsigned long *cylinders, 
+	unsigned long *heads, unsigned long *sectors)
+{
+  if (!doc_init())
+    return 1;		// bios.c functions return non-zero on error
+
+  if (drive < 0 || drive >= nNFTLs)
+  { errnum = ERR_NO_DISK; return 1;
+  }
+
+  *sectors = 1;
+  *heads = erasesect;
+  *cylinders = (NFTLs[drive].MediaHdr.FormattedSize >> 9) / (*heads);
+  return 0;
+}
+
+#endif /* BDEV_DISKONCHIP */
+
diff -urN ../orig/stage2/bios.c ./stage2/bios.c
--- ../orig/stage2/bios.c	Tue Oct  8 16:53:02 2002
+++ ./stage2/bios.c	Tue Oct  8 16:55:27 2002
@@ -129,6 +129,22 @@
   /* Clear the flags.  */
   geometry->flags = 0;
   
+#ifdef BDEV_DISKONCHIP
+  if ((drive & 0xFFFFFFF0) == DISK_ON_CHIP)
+    {
+      err = get_diskinfo_diskonchip (drive & 0x0000000f,
+		   &geometry->cylinders,
+		   &geometry->heads,
+		   &geometry->sectors);
+      if (err)
+	return err;
+
+      geometry->total_sectors = (geometry->cylinders
+				 * geometry->heads
+				 * geometry->sectors);
+    }
+  else
+#endif
   if (drive & 0x80)
     {
       /* hard disk */
@@ -210,6 +226,13 @@
 	}
       geometry->total_sectors = total_sectors;
     }
+  else if (drive == NETWORK_DRIVE)
+    { /* indulge the command line geometry query */
+      geometry->cylinders = 0;
+      geometry->heads = 0;
+      geometry->sectors = 0;
+      geometry->total_sectors = 0;
+    }
   else
     {
       /* floppy disk */
diff -urN ../orig/stage2/boot.c ./stage2/boot.c
--- ../orig/stage2/boot.c	Tue Oct  8 16:53:02 2002
+++ ./stage2/boot.c	Tue Oct  8 16:55:27 2002
@@ -61,6 +61,8 @@
      buffer by default */
   pu.aout = (struct exec *) buffer;
 
+  printf("Loading kernel image...\n");
+
   if (!grub_open (kernel))
     return KERNEL_TYPE_NONE;
 
diff -urN ../orig/stage2/builtins.c ./stage2/builtins.c
--- ../orig/stage2/builtins.c	Tue Oct  8 16:53:02 2002
+++ ./stage2/builtins.c	Tue Oct  8 16:55:27 2002
@@ -1319,56 +1319,68 @@
       return 1;
     }
 
-  /* Attempt to read the first sector, because some BIOSes turns out not
-     to support LBA even though they set the bit 0 in the support
-     bitmap, only after reading something actually.  */
-  if (biosdisk (BIOSDISK_READ, current_drive, &geom, 0, 1, SCRATCHSEG))
+  if ((current_drive & 0xfffffff0) == DISK_ON_CHIP)
     {
-      errnum = ERR_READ;
-      return 1;
+      msg = "Disk On Chip";
     }
-
-#ifdef GRUB_UTIL
-  ptr = skip_to (0, device);
-  if (*ptr)
+  else if (current_drive == NETWORK_DRIVE)
     {
-      char *cylinder, *head, *sector, *total_sector;
-      int num_cylinder, num_head, num_sector, num_total_sector;
-
-      cylinder = ptr;
-      head = skip_to (0, cylinder);
-      sector = skip_to (0, head);
-      total_sector = skip_to (0, sector);
-      if (! safe_parse_maxint (&cylinder, &num_cylinder)
-	  || ! safe_parse_maxint (&head, &num_head)
-	  || ! safe_parse_maxint (&sector, &num_sector))
-	return 1;
-
-      disks[current_drive].cylinders = num_cylinder;
-      disks[current_drive].heads = num_head;
-      disks[current_drive].sectors = num_sector;
-
-      if (safe_parse_maxint (&total_sector, &num_total_sector))
-	disks[current_drive].total_sectors = num_total_sector;
-      else
-	disks[current_drive].total_sectors
-	  = num_cylinder * num_head * num_sector;
-      errnum = 0;
-
-      geom = disks[current_drive];
-      buf_drive = -1;
+      msg = "Network Drive";
     }
+  else
+    {
+      /* Attempt to read the first sector, because some BIOSes turns out not
+         to support LBA even though they set the bit 0 in the support
+         bitmap, only after reading something actually.  */
+      if (biosdisk (BIOSDISK_READ, current_drive, &geom, 0, 1, SCRATCHSEG))
+        {
+          errnum = ERR_READ;
+          return 1;
+        }
+
+#ifdef GRUB_UTIL
+      ptr = skip_to (0, device);
+      if (*ptr)
+        {
+          char *cylinder, *head, *sector, *total_sector;
+          int num_cylinder, num_head, num_sector, num_total_sector;
+
+          cylinder = ptr;
+          head = skip_to (0, cylinder);
+          sector = skip_to (0, head);
+          total_sector = skip_to (0, sector);
+          if (! safe_parse_maxint (&cylinder, &num_cylinder)
+              || ! safe_parse_maxint (&head, &num_head)
+              || ! safe_parse_maxint (&sector, &num_sector))
+            return 1;
+
+          disks[current_drive].cylinders = num_cylinder;
+          disks[current_drive].heads = num_head;
+          disks[current_drive].sectors = num_sector;
+
+          if (safe_parse_maxint (&total_sector, &num_total_sector))
+            disks[current_drive].total_sectors = num_total_sector;
+          else
+            disks[current_drive].total_sectors
+              = num_cylinder * num_head * num_sector;
+          errnum = 0;
+
+          geom = disks[current_drive];
+          buf_drive = -1;
+        }
 #endif /* GRUB_UTIL */
 
 #ifdef GRUB_UTIL
-  msg = device_map[current_drive];
+      msg = device_map[current_drive];
 #else
-  if (geom.flags & BIOSDISK_FLAG_LBA_EXTENSION)
-    msg = "LBA";
-  else
-    msg = "CHS";
+      if (geom.flags & BIOSDISK_FLAG_LBA_EXTENSION)
+        msg = "LBA";
+      else
+        msg = "CHS";
 #endif
 
+    }  /* current_drive is not DiskOnChip and not a network drive */
+
   grub_printf ("drive 0x%x: C/H/S = %d/%d/%d, "
 	       "The number of sectors = %d, %s\n",
 	       current_drive,
@@ -3041,6 +3053,19 @@
       /* Network drive.  */
       grub_printf (" (nd):");
     }
+  else if ((saved_drive & 0xfffffff0) == DISK_ON_CHIP)
+    {
+      /* DiskOnChip 2000 */
+      grub_printf (" (dc%d", saved_drive & 0x0F);
+
+      if ((saved_partition & 0xFF0000) != 0xFF0000)
+	grub_printf (",%d", saved_partition >> 16);
+
+      if ((saved_partition & 0x00FF00) != 0x00FF00)
+	grub_printf (",%c", ((saved_partition >> 8) & 0xFF) + 'a');
+
+      grub_printf ("):");
+    }
   else if (saved_drive & 0x80)
     {
       /* Hard disk drive.  */
diff -urN ../orig/stage2/char_io.c ./stage2/char_io.c
--- ../orig/stage2/char_io.c	Tue Oct  8 16:53:02 2002
+++ ./stage2/char_io.c	Tue Oct  8 16:55:27 2002
@@ -1157,6 +1157,7 @@
 int
 memcheck (int addr, int len)
 {
+  int memerr = 0;
 #ifdef GRUB_UTIL
   static int start_addr (void)
     {
@@ -1181,22 +1182,38 @@
     }
 
   if (start_addr () <= addr && end_addr () > addr + len)
-    return ! errnum;
+    {
+      memerr = errnum = ERR_WONT_FIT;
+      return ! memerr;
+    }
 #endif /* GRUB_UTIL */
 
+#if MEMORY_DEBUG
+  printf("memcheck(): errnum %d, addr 0x%x, len 0x%x,"
+	" mem_lower 0x%x, mem_upper 0x%x\n",
+	errnum, addr, len, mbi.mem_lower, mbi.mem_upper);
+#endif
+
   if ((addr < RAW_ADDR (0x1000))
       || (addr < RAW_ADDR (0x100000)
 	  && RAW_ADDR (mbi.mem_lower * 1024) < (addr + len))
       || (addr >= RAW_ADDR (0x100000)
 	  && RAW_ADDR (mbi.mem_upper * 1024) < ((addr - 0x100000) + len)))
-    errnum = ERR_WONT_FIT;
+    {
+      printf("memcheck(): error\n");
+      memerr = errnum = ERR_WONT_FIT;
+    }
 
-  return ! errnum;
+  return ! memerr;
 }
 
 void *
 grub_memmove (void *to, const void *from, int len)
 {
+#if MEMORY_DEBUG
+   printf("grub_memmove(): errnum %d, to 0x%x, from 0x%x, len 0x%x\n",
+	errnum, (int) to, (int) from, len);
+#endif
    if (memcheck ((int) to, len))
      {
        /* This assembly code is stolen from
@@ -1227,6 +1244,9 @@
 	 }
      }
 
+#if MEMORY_DEBUG
+   printf("grub_memmove(): errnum %d\n", errnum);
+#endif
    return errnum ? NULL : to;
 }
 
diff -urN ../orig/stage2/disk_io.c ./stage2/disk_io.c
--- ../orig/stage2/disk_io.c	Tue Oct  8 16:53:02 2002
+++ ./stage2/disk_io.c	Tue Oct  8 16:55:27 2002
@@ -126,9 +126,24 @@
 {
   int slen = (byte_offset + byte_len + SECTOR_SIZE - 1) >> SECTOR_BITS;
 
+#if !defined(STAGE1_5)
+  if (debug)
+    {
+      printf("rawread: drive %d, sector %d, byte_offset %d,"
+	" byte_len %d, buf 0x%x\n", drive, sector, byte_offset, byte_len, buf);
+    }
+#endif
+
   if (byte_len <= 0)
     return 1;
 
+#ifdef BDEV_DISKONCHIP
+  if ((drive & 0xfffffff0) == DISK_ON_CHIP)
+    return nftl_rawread(
+      drive & 0x0000000f, sector, byte_offset, byte_len, buf
+    );
+#endif
+
   while (byte_len > 0 && !errnum)
     {
       int soff, num_sect, bufaddr, track, size = byte_len;
@@ -297,6 +312,16 @@
 int
 rawwrite (int drive, int sector, char *buf)
 {
+
+#ifdef BDEV_DISKONCHIP
+  if ((drive & 0xfffffff0) == DISK_ON_CHIP)
+    {
+      errnum = ERR_WRITE;
+      return 0;
+      /* return nftl_rawrite(drive & 0x0000000f, sector, buf); */
+    }
+#endif
+
   if (sector == 0)
     {
       if (biosdisk (BIOSDISK_READ, drive, &buf_geom, 0, 1, SCRATCHSEG))
@@ -364,15 +389,20 @@
     return 1;
   
   if (!(current_partition & 0xFF000000uL)
-      && (current_drive & 0xFFFFFF7F) < 8
+      && ((current_drive & 0xFFFFFF7F) < 8 
+	  || (current_drive & 0xFFFFFFF0) == DISK_ON_CHIP)
       && (current_partition & 0xFF) == 0xFF
       && ((current_partition & 0xFF00) == 0xFF00
 	  || (current_partition & 0xFF00) < 0x800)
       && ((current_partition >> 16) == 0xFF
 	  || (current_drive & 0x80)))
-    return 1;
+    {
+      dprintf("sane_partition: yes\n");
+      return 1;
+    }
 
   errnum = ERR_DEV_VALUES;
+  dprintf("sane_partition: no\n");
   return 0;
 }
 #endif /* ! STAGE1_5 */
@@ -617,14 +647,39 @@
 	  pc_slice_no = -1;
 	}
 
+      dprintf("next_pc_slice(): errnum %d\n", errnum);
+      dprintf("next_pc_slice(): reading MBR at sector 0x%x of drive 0x%x\n",
+		*offset, drive);
       /* Read the MBR or the boot sector of the extended partition.  */
       if (! rawread (drive, *offset, 0, SECTOR_SIZE, buf))
-	return 0;
+	{
+	  printf("next_pc_slice: error reading MBR at sector %x"
+		" of drive %x\n", *offset, drive);
+	  return 0;
+	}
+
+#ifdef DOC_THOROUGH_DEBUG
+        {
+	  int i = 0;
+	  while (i < SECTOR_SIZE) {
+	    printf("%x    "
+		"%b %b %b %b %b %b %b %b "
+		"%b %b %b %b %b %b %b %b\n",
+		i, 
+		buf[i + 0], buf[i + 1], buf[i + 2], buf[i + 3], 
+		buf[i + 4], buf[i + 5], buf[i + 6], buf[i + 7],
+		buf[i + 8], buf[i + 9], buf[i + 10], buf[i + 11],
+		buf[i + 12], buf[i + 13], buf[i + 14], buf[i + 15]);
+	    i += 16;
+	  }
+        }
+#endif
 
       /* Check if it is valid.  */
       if (! PC_MBR_CHECK_SIG (buf))
 	{
 	  errnum = ERR_BAD_PART_TABLE;
+	  printf("next_pc_slice: invalid MBR\n");
 	  return 0;
 	}
 
@@ -674,6 +729,9 @@
 
   /* Start the body of this function.  */
   
+  dprintf("next_partition(): drive %x, current_drive 0x%x\n",
+	drive, current_drive);
+
 #ifndef STAGE1_5
   if (current_drive == NETWORK_DRIVE)
     return 0;
@@ -931,6 +989,8 @@
   current_drive = saved_drive;
   current_partition = 0xFFFFFF;
 
+  dprintf("set_device(): device %s, drive %x\n", device, current_drive);
+
   if (*device == '(' && !*(device + 1))
     /* user has given '(' only, let disk_choice handle what disks we have */
     return device + 1;
@@ -941,7 +1001,7 @@
 	{
 	  char ch = *device;
 
-	  if (*device == 'f' || *device == 'h')
+	  if (ch == 'f' || ch == 'h')
 	    {
 	      /* user has given '([fh]', check for resp. add 'd' and
 		 let disk_choice handle what disks we have */
@@ -956,24 +1016,37 @@
 		return device + 2;
 	    }
 
-	  if ((*device == 'f' || *device == 'h' || *device == 'n')
-	      && (device += 2, (*(device - 1) != 'd')))
-	    errnum = ERR_NUMBER_PARSING;
+	  device += 2;
 
-	  if (ch == 'n')
-	    current_drive = NETWORK_DRIVE;
-	  else
+#define MK16(a,b) ( ((a)<<8) + (b) )
+
+	  switch (MK16(ch, *(device-1)))
 	    {
+	    case MK16('n','d'):
+	      current_drive = NETWORK_DRIVE;
+	      break;
+	      
+	    case MK16('d','c'):
 	      safe_parse_maxint (&device, (int *) &current_drive);
+	      disk_choice = 0;
+	      current_drive += DISK_ON_CHIP;
+	      break;
 	      
+	    case MK16('h','d'):
+	    case MK16('f','d'):
+	      safe_parse_maxint (&device, (int *) &current_drive);
 	      disk_choice = 0;
 	      if (ch == 'h')
 		current_drive += 0x80;
+	      break;
+	      
+	    default:
+	      errnum = ERR_NUMBER_PARSING;
 	    }
 	}
 
       if (errnum)
-	return 0;
+	 return 0;
 
       if (*device == ')')
 	{
@@ -1055,8 +1128,15 @@
 int
 open_device (void)
 {
+  dprintf("open_device(): errnum %d, current_drive %x, current_partition %x\n",
+	errnum, current_drive, current_partition);
   if (open_partition ())
-    attempt_mount ();
+    {
+      dprintf("open_device(): open_partition() ok, attempt_mount()\n");
+      attempt_mount ();
+    }
+
+  dprintf("open_device(): errnum %d\n", errnum);
 
   if (errnum != ERR_NONE)
     return 0;
@@ -1139,6 +1219,7 @@
   
 #else /* ! STAGE1_5 */
   
+  dprintf("setup_part(): filename %s\n", filename);
   if (*filename == '(')
     {
       if ((filename = set_device (filename)) == 0)
@@ -1172,6 +1253,7 @@
   
 #endif /* ! STAGE1_5 */
   
+  dprintf("setup_part(): errnum %d\n", errnum);
   if (errnum && (*filename == '/' || errnum != ERR_FSYS_MOUNT))
     return 0;
   else
@@ -1556,8 +1638,10 @@
   print_possibilities = 0;
 # endif
 
+  dprintf("grub_open(): errnum %d == 0 ? calling dir_func\n", errnum);
   if (!errnum && (*(fsys_table[fsys_type].dir_func)) (filename))
     {
+      dprintf("grub_open(): dir_func returned success\n");
 #ifndef NO_DECOMPRESSION
       return gunzip_test_header ();
 #else /* NO_DECOMPRESSION */
diff -urN ../orig/stage2/shared.h ./stage2/shared.h
--- ../orig/stage2/shared.h	Tue Oct  8 16:53:02 2002
+++ ./stage2/shared.h	Tue Oct  8 16:55:27 2002
@@ -27,6 +27,14 @@
 
 #include <config.h>
 
+/* #define DOC_DEBUG */
+
+#ifdef DOC_DEBUG
+	#define dprintf(fmt...)  printf(fmt)
+#else
+	#define dprintf(fmt...)
+#endif
+
 /* Add an underscore to a C symbol in assembler code if needed. */
 #ifdef HAVE_ASM_USCORE
 # define EXT_C(sym) _ ## sym
@@ -180,7 +188,7 @@
 
 /* Not bad, perhaps.  */
 #define NETWORK_DRIVE	0x20
-
+#define DISK_ON_CHIP	0xE0
 /*
  *  GRUB specific information
  *    (in LSB order)
@@ -973,6 +981,14 @@
 
 void init_bios_info (void);
 
+#ifdef BDEV_DISKONCHIP
+extern int nftl_rawread(
+  int drive, int sector, int byte_offset, int byte_len, char *buf
+);
+extern int get_diskinfo_diskonchip (int drive, unsigned long *cylinders, 
+	unsigned long *heads, unsigned long *sectors);
+#endif
+
 #endif /* ASM_FILE */
 
 #endif /* ! GRUB_SHARED_HEADER */
diff -urN ../orig/stage2/stage2.c ./stage2/stage2.c
--- ../orig/stage2/stage2.c	Tue Oct  8 16:53:02 2002
+++ ./stage2/stage2.c	Tue Oct  8 16:55:27 2002
@@ -777,6 +777,46 @@
   return pos;
 }
 
+#ifdef BDEV_DISKONCHIP
+static int
+try_config_partition(int part_no)
+{
+  int is_opened = 0;
+  saved_partition   = 0x0000FFFF | (part_no << 16);
+  current_partition = 0xFFFFFFFF;
+  errnum = ERR_NONE;
+  printf("Attempting to open %s on drive 0x%x, partition 0x%x\n", 
+    config_file, saved_drive, part_no);
+  is_opened = grub_open (config_file);
+  dprintf("grub_open %d, errnum %d\n", is_opened, errnum);
+  return is_opened;
+}
+
+static int
+find_config(int drive_no)
+{
+  int part_no;
+  unsigned long saved_drive2;
+  unsigned long saved_partition2;
+  saved_drive2 = saved_drive;
+  saved_partition2 = saved_partition;
+  saved_drive = drive_no;
+  current_drive = 0xFF;
+  if (try_config_partition(saved_partition >> 16))
+    return 1;
+  if (try_config_partition(0xFF))
+    return 1;
+  for (part_no = 0; part_no < 0x10; part_no++)
+    {
+      if (try_config_partition(part_no))
+        return 1;
+    }
+  saved_partition   = saved_partition2;
+  saved_drive = saved_drive2;
+  current_partition = 0xFFFFFFFF;
+  return 0;
+}
+#endif
 
 /* This is the starting function in C.  */
 void
@@ -820,30 +860,47 @@
 	  do
 	    {
 	      /* STATE 0:  Before any title command.
-		 STATE 1:  In a title command.
-		 STATE >1: In a entry after a title command.  */
+	         STATE 1:  In a title command.
+	         STATE >1: In a entry after a title command.  */
 	      int state = 0, prev_config_len = 0, prev_menu_len = 0;
 	      char *cmdline;
 
 	      /* Try the preset menu first. This will succeed at most once,
-		 because close_preset_menu disables the preset menu.  */
+	         because close_preset_menu disables the preset menu.  */
 	      is_opened = is_preset = open_preset_menu ();
 	      if (! is_opened)
 		{
+#ifdef BDEV_DISKONCHIP
+		  /* Look for configuration file in DiskOnChip 
+		   * by trying to find a file system from its very start.
+		   * If that failed, find_config() will assume there is
+		   * a partition table and will attempt to open partitions
+		   * 0 through 15.
+		   * Next, if the above didn't succeed, perform the same
+		   * search on the first hard disk (BIOS drive 0x80).
+		   */
+		  is_opened = find_config(saved_drive);
+		  if (! is_opened )
+	            is_opened = find_config(0x80);
+		  if (! is_opened )
+	            is_opened = find_config(0);
+		  dprintf("is_opened %d\n", is_opened);
+#else
 		  is_opened = grub_open (config_file);
-		  errnum = ERR_NONE;
+#endif
+		  if (! is_opened)
+	            current_term->flags |= TERM_DUMB;
 		}
 
 	      if (! is_opened)
-		break;
+	        break;
 
 	      /* This is necessary, because the menu must be overrided.  */
 	      reset ();
 	      
 	      cmdline = (char *) CMDLINE_BUF;
-	      while (get_line_from_config (cmdline, NEW_HEAPSIZE,
-					   ! is_preset))
-		{
+	      while (get_line_from_config (cmdline, NEW_HEAPSIZE, ! is_preset))
+	        {
 		  struct builtin *builtin;
 		  
 		  /* Get the pointer to the builtin structure.  */


More information about the linux-mtd mailing list