diff -uNr grub/configure.in grub-doc/configure.in
--- grub/configure.in	Thu Oct  5 05:43:25 2000
+++ grub-doc/configure.in	Thu Oct 12 13:58:53 2000
@@ -216,6 +216,14 @@
   FSYS_CFLAGS="$FSYS_CFLAGS -DFSYS_REISERFS=1"
 fi
 
+AC_ARG_ENABLE(diskonchip,
+  [  --enable-diskonchip     enable DiskOnChip 2000 support in Stage 2])
+
+if test x"$enable_diskonchip" = xyes; then
+  FSYS_CFLAGS="$FSYS_CFLAGS -DBDEV_DISKONCHIP=1"
+fi
+
+
 dnl AC_ARG_ENABLE(tftp,
 dnl [  --enable-tftp           enable TFTP support in Stage 2])
 dnl 
diff -uNr grub/stage2/Makefile.am grub-doc/stage2/Makefile.am
--- grub/stage2/Makefile.am	Sun Aug 20 13:14:14 2000
+++ grub-doc/stage2/Makefile.am	Thu Oct 12 13:59:36 2000
@@ -72,7 +72,7 @@
 pre_stage2_exec_SOURCES = asm.S bios.c boot.c builtins.c common.c \
 	char_io.c cmdline.c disk_io.c gunzip.c fsys_ext2fs.c \
 	fsys_fat.c fsys_ffs.c fsys_minix.c fsys_reiserfs.c serial.c \
-	smp-imps.c stage2.c
+	smp-imps.c stage2.c bdev_diskonchip.c
 pre_stage2_exec_CFLAGS = $(STAGE2_COMPILE) $(FSYS_CFLAGS)
 pre_stage2_exec_LDFLAGS = $(PRE_STAGE2_LINK)
 
diff -uNr grub/stage2/bdev_diskonchip.c grub-doc/stage2/bdev_diskonchip.c
--- grub/stage2/bdev_diskonchip.c	Thu Jan  1 01:00:00 1970
+++ grub-doc/stage2/bdev_diskonchip.c	Thu Oct 12 13:58:53 2000
@@ -0,0 +1,819 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1996  Erich Boleyn  <erich@uruk.org>
+ *  Copyright (C) 1999  Free Software Foundation, Inc.
+ *
+ *  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
+
+/* #define DOC_DEBUG */
+
+#include "shared.h"
+
+#define DoC_M_CDSN_IO 0x800
+
+#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_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 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 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
+
+#define ERASE_SIZE 0x2000
+#define ERASE_SECT (ERASE_SIZE >> 9)
+#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 int numchips[MAX_FLOORS];
+static int totalchips=0;
+static int chipshift=0;
+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;
+
+static int _DoC_WaitReady(void)
+{
+	short c=-1;
+
+	while (!(docloc[DoC_CDSNControl] & CDSN_CTRL_FR_B) && --c)
+		;
+
+	if (c == 0)
+		grub_printf("_DoC_WaitReady timed out\n");
+
+	return (c==0);
+}
+
+static inline int DoC_WaitReady(void)
+{
+	volatile unsigned char dummy;
+	int ret = 0;
+
+	dummy = docloc[DoC_CDSNControl];
+	dummy = docloc[DoC_CDSNControl];
+	dummy = docloc[DoC_CDSNControl];
+	dummy = docloc[DoC_CDSNControl];
+
+	if (!(docloc[DoC_CDSNControl] & CDSN_CTRL_FR_B))
+		ret = _DoC_WaitReady();
+
+	dummy = docloc[DoC_CDSNControl];
+	dummy = docloc[DoC_CDSNControl];
+
+	return ret;
+}
+
+static inline void DoC_Command(unsigned char command, unsigned char xtraflags)
+{
+	docloc[DoC_CDSNControl] = 
+		CDSN_CTRL_FLASH_IO | xtraflags | CDSN_CTRL_CLE | CDSN_CTRL_CE;
+	
+	docloc[DoC_2k_CDSN_IO] = command;
+
+	docloc[DoC_CDSNControl] = 
+		CDSN_CTRL_FLASH_IO | xtraflags | CDSN_CTRL_CE;
+}
+
+static int DoC_IdentChip(unsigned char floor, unsigned char chip)
+{
+	int mfr, id, thischipshift=0;
+
+	docloc[DoC_FloorSelect] = floor;
+	docloc[DoC_CDSNDeviceSelect] = chip;
+
+	DoC_Command(NAND_CMD_RESET, CDSN_CTRL_WP);
+
+	if (DoC_WaitReady())
+		return 0;
+
+	DoC_Command(NAND_CMD_READID, CDSN_CTRL_WP);
+
+
+	/* Send a zero address */
+	docloc[DoC_CDSNControl] = 
+		CDSN_CTRL_FLASH_IO | CDSN_CTRL_WP | CDSN_CTRL_ALE | CDSN_CTRL_CE;
+	docloc[DoC_2k_CDSN_IO] = 0;
+
+	docloc[DoC_CDSNControl] = 
+		CDSN_CTRL_FLASH_IO | CDSN_CTRL_WP | CDSN_CTRL_CE;
+	
+	mfr = docloc[DoC_2k_CDSN_IO];
+	id = docloc[DoC_2k_CDSN_IO];
+
+        /*
+        if (mfr != NAND_MFR_TOSHIBA && mfr != NAND_MFR_SAMSUNG)
+		return 0;
+        */
+        switch (mfr)
+        {
+          case NAND_MFR_TOSHIBA:
+            grub_printf("floor %d, chip %d; ", floor, chip);
+            grub_printf("manufacturer: Toshiba\n");
+            break;
+          case NAND_MFR_SAMSUNG:
+            grub_printf("floor %d, chip %d; ", floor, chip);
+            grub_printf("manufacturer: Samsung\n");
+            break;
+          default:
+            /* grub_printf("unknown manufacturer code: 0x%x\n", mfr); */
+            return 0;
+        }
+
+	switch (id) {
+	case 0x64:
+	case 0xea:
+		thischipshift = 21;
+		break;
+		
+	case 0x6b:
+	case 0xe5:
+	case 0xe3:
+		thischipshift = 22;
+		break;
+		
+	case 0xe6: 
+		thischipshift = 23;
+		break;
+		
+	case 0x73: 
+		thischipshift = 24;
+		break;
+		
+	case 0x75: 
+		thischipshift = 25;
+		break;
+		
+	default:
+	}
+	
+	if (!chipshift) {
+		chipshift = thischipshift;
+                grub_printf("chipshift: %d\n", chipshift);
+		grub_printf("DiskOnChip NAND chip size: %x Mb\n", 1 << 
+			    (chipshift - 20));
+	}
+	
+	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
+
+	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_DocMil) {
+		grub_printf("DiskOnChip Millennium found at %x but not supported yet\n",
+			    (unsigned int)loc);
+		return 0;
+	}
+
+	if (ChipID != DOC_ChipID_Doc2k) {
+#ifndef DOC_PASSIVE_PROBE
+		loc[DoC_DOCControl] = tmp2;
+#endif
+		return 0;
+	}
+
+	/* See if the TOGGLE bit is toggling */
+
+	tmp = loc[DoC_2k_ECCStatus] & DOC_TOGGLE_BIT;
+	if (tmp == (loc[DoC_2k_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. */
+	grub_printf("DiskOnChip 2000 found at %x\n", (unsigned int)loc);
+	docloc = loc;
+
+	DoC_ScanChips();
+
+	if (!totalchips)
+		return 0;
+	grub_printf("Total of %d chips found - total capacity %d Mb\n",
+		    totalchips, totalchips * ( 1 << (chipshift -20)));
+	return 1;
+}
+
+static int doc_read_oob(unsigned long sector, void *buf)
+{
+	unsigned char chip, floor;
+	int di;
+
+	chip = sector >> (chipshift - 9);
+	floor = 0;
+
+	while (chip >= numchips[floor]) {
+		chip -= numchips[floor];
+		floor++;
+		if (floor == MAX_FLOORS)
+			return -1;
+	}
+		
+	/* grub_printf("sector 0x%x is on floor %d, chip %d\n",
+		    sector, floor, chip); */
+
+	docloc[DoC_FloorSelect] = floor;
+	docloc[DoC_CDSNDeviceSelect] = chip;
+
+	DoC_Command(NAND_CMD_READOOB, CDSN_CTRL_WP);
+
+	DoC_WaitReady();
+
+	docloc[DoC_CDSNControl] = 
+		CDSN_CTRL_FLASH_IO |CDSN_CTRL_WP | CDSN_CTRL_ALE | CDSN_CTRL_CE;
+
+	docloc[DoC_2k_CDSN_IO] = 0;
+	docloc[DoC_2k_CDSN_IO] = sector & 0xff;
+	docloc[DoC_2k_CDSN_IO] = (sector >> 8) & 0xff;
+
+	docloc[DoC_CDSNControl] = 
+		CDSN_CTRL_FLASH_IO |CDSN_CTRL_WP | CDSN_CTRL_CE;
+
+	DoC_WaitReady();
+
+	for (di=0; di < 16; di++) {
+		((unsigned char *)buf)[di] = docloc[DoC_2k_CDSN_IO];
+	}
+
+        return 0;
+}
+
+static int doc_read_sector(unsigned long sector, unsigned char *buf)
+{
+	unsigned char chip, floor;
+	int di;
+
+	chip = sector >> (chipshift - 9);
+	floor = 0;
+
+	//	grub_printf("sector %d\n", sector);
+
+	while (chip >= numchips[floor]) {
+		//		grub_printf("Chip %d Not on floor %d (%d chips)\n", chip, floor, numchips[floor]);
+		chip -= numchips[floor];
+		floor++;
+		if (floor == MAX_FLOORS)
+			return -1;
+	}
+		
+	//	grub_printf("sector 0x%x is on floor %d, chip %d\n", 
+	//    sector, floor, chip);
+	
+	docloc[DoC_FloorSelect] = floor;
+	docloc[DoC_CDSNDeviceSelect] = chip;
+
+	DoC_Command(NAND_CMD_READ0, CDSN_CTRL_WP);
+
+	DoC_WaitReady();
+
+	docloc[DoC_CDSNControl] = 
+		CDSN_CTRL_FLASH_IO |CDSN_CTRL_WP | CDSN_CTRL_ALE | CDSN_CTRL_CE;
+
+	docloc[DoC_2k_CDSN_IO] = 0;
+	docloc[DoC_2k_CDSN_IO] = sector & 0xff;
+	docloc[DoC_2k_CDSN_IO] = (sector >> 8) & 0xff;
+
+	docloc[DoC_CDSNControl] = 
+		CDSN_CTRL_FLASH_IO |CDSN_CTRL_WP | CDSN_CTRL_CE;
+
+	DoC_WaitReady();
+
+	for (di=0; di < 512; di++) {
+		buf[di] = docloc[DoC_2k_CDSN_IO];
+	}
+
+        return 0;
+}
+
+static
+void NFTL_setup(unsigned long sect, struct NFTLMediaHeader *hdr)
+{
+  int i;
+  unsigned int eun = sect / ERASE_SECT;
+  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)
+  {
+    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 / ERASE_SIZE;
+
+  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 * ERASE_SECT + 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;
+
+  /* printf("find virtual sector %d\n", block); */
+  vun = block / ERASE_SECT;
+  block = block % ERASE_SECT;
+  /* printf("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 * ERASE_SECT;
+    /* 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 * ERASE_SECT;
+    for (; eun<nftl->lastEUN; eun++, sect += ERASE_SECT)
+    {
+      if (isbad(nftl_num, eun))
+      { printf("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;
+    /* printf("was 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)
+    { /* printf("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;
+      /* printf("block %d in eun %d is %s\n",
+        block, eun, save_stat == BLOCK_USED ? "BLOCK_USED" : "BLOCK_DELETED");
+      */
+    }
+    /* is it last in eun chain? */
+    if (oob.u.a.ReplUnitNum == 0xffff)
+    { /* printf("eun %d is last in the chain\n", eun); */
+      break;
+    }
+    /* read next eun in the chain */
+    eun = oob.u.a.ReplUnitNum;
+    sect = eun * ERASE_SECT;
+    /* printf("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)
+  { /* printf("was found block %d in eun %d\n",
+      save_eun * ERASE_SECT + block, save_eun);
+    */
+    return
+      blk_hash[k].pblock = save_eun * ERASE_SECT + block;
+  }
+
+  return -1;
+}
+
+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+=ERASE_SECT)
+  {
+    if (doc_read_sector(sector, nftlbuf) < 0)
+      continue;      
+    if (!strcmp(hdr->DataOrgID, "ANAND"))
+    {
+      /* printf("NFTL Media Header found in flash sector %d\n", sector); */
+      /* printf("NumEraseUnits: %d\n", hdr->NumEraseUnits);
+      printf("FirstPhysicalEUN: %d\n", hdr->FirstPhysicalEUN);
+      printf("FormattedSize: %d\n", hdr->FormattedSize);
+      printf("UnitSizeFactor: %d\n", hdr->UnitSizeFactor); */
+      NFTL_setup(sector, hdr);
+    }
+  }
+}
+
+static void doc_find(void)
+{
+	unsigned long probeloc = 0xc8000;
+	doc_inited = 1;
+	
+
+	for (probeloc = 0xc8000; probeloc < 0xf0000; probeloc += 0x2000) {
+		if (DoC_Probe((unsigned char *)probeloc)) {
+			NFTL_Scan();
+			return;
+		}
+	}
+	
+	grub_printf("No DiskOnChip found\n");
+	return;
+}
+		
+int nftl_rawread(int drive, int sector, int byte_offset, int byte_len, char *buf)
+{
+  int size, block;
+
+#ifdef DOC_DEBUG
+  printf(
+    "nftl_rawread(%d, %d, %d, %d)\n", drive, sector, byte_offset, byte_len
+  );
+#endif
+  if (byte_len < 0 || !buf || drive < 0 || drive >= MAX_NFTLS)
+  { errnum = ERR_BAD_ARGUMENT; return 0;
+  }
+
+  /* If this is the first time we've been called, 
+     we need to go searching for a DiskOnChip
+  */
+  if (!doc_inited)
+  {
+    int i;
+    doc_find();
+
+    if (!docloc)
+    { errnum = ERR_NO_DISK; return 0;
+    }
+
+    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;
+    }
+  }
+
+  if (!docloc)
+  { errnum = ERR_NO_DISK; 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;
+    }
+
+    memcpy(buf, nftlbuf + byte_offset, size);
+#ifdef DOC_DEBUG
+    printf("readed 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;
+}
+
+#endif /* BDEV_DISKONCHIP */
+
diff -uNr grub/stage2/builtins.c grub-doc/stage2/builtins.c
--- grub/stage2/builtins.c	Mon Oct  9 17:32:59 2000
+++ grub-doc/stage2/builtins.c	Thu Oct 12 13:58:53 2000
@@ -2745,6 +2745,19 @@
       /* Network drive.  */
       grub_printf (" (nd):");
     }
+  else if (saved_drive == 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 -uNr grub/stage2/disk_io.c grub-doc/stage2/disk_io.c
--- grub/stage2/disk_io.c	Sat Oct  7 08:19:12 2000
+++ grub-doc/stage2/disk_io.c	Thu Oct 12 13:58:53 2000
@@ -115,6 +115,13 @@
   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;
@@ -268,7 +275,8 @@
     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)
@@ -849,19 +857,32 @@
 		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;
 	    }
 	}
 
diff -uNr grub/stage2/shared.h grub-doc/stage2/shared.h
--- grub/stage2/shared.h	Fri Sep 29 01:41:42 2000
+++ grub-doc/stage2/shared.h	Thu Oct 12 13:58:53 2000
@@ -182,7 +182,7 @@
 
 /* Not bad, perhaps.  */
 #define NETWORK_DRIVE	0x20
-
+#define DISK_ON_CHIP	0xE0
 /*
  *  GRUB specific information
  *    (in LSB order)
@@ -891,6 +891,12 @@
 #endif
 
 void init_bios_info (void);
+
+#ifdef BDEV_DISKONCHIP
+extern int nftl_rawread(
+  int drive, int sector, int byte_offset, int byte_len, char *buf
+);
+#endif
 
 #endif /* ASM_FILE */
 
