DoC + GRUB booting problem

Edward A. Hildum
2003-04-08T18:17:56Z

I've got a 256M DoC2000 I'm trying to set up with GRUB as a boot disk,
and I'm not making much headway. I downloaded both GRUB and MTD from
CVS, patched GRUB and built it successfully. I loaded the resulting
grub_firmware to the DOC using the M-Systems DFORMAT utility (latest
version, 5.1.4). When I boot the system, I can see the BIOS accessing
the DOC (looking for BIOS extensions, I presume), but there is no
sign-on message from GRUB. Eventually, the BIOS gives up and says "no
operating system found".

I re-formatted the DOC with the TrueFFS boot image supplied by M-Systems
as part of their DOS utilities package, and the TrueFFS drivers do sign on.

I wrote a small piece of C code to check out the grub_firmware image,
and its checksums are correct according to the README_DiskOnChip file in
the patched GRUB build tree. The TrueFFS images do not have the correct
checksums, but they appear to work anyway.

I grabbed an image of the BIOS extension by booting into DOS and using
DOS DEBUG. I have disassembled the BIOS boot code (included below) and
found some conflicts with the README_DiskOnChip info:

1. The IPL in the BIOS extension loads 0x3000 bytes, not 0x2000 bytes.
2. The IPL doesn't necessarily load the SPL to address 0x2000:0. It
looks for a 64Kbyte block containing nothing but 0's between 0x2000:0
and 0xA000:0 and uses that block if it is found, otherwise it uses 0x5800:0.
3. The IPL looks for a 4-byte sequence (0x84 0xA8 0xAC 0xA0) in the SPL
image before it loads it. I don't have any DoC documentation of the
access registers, so I can't tell where it starts looking, and I don't
know where the IPL starts loading the image if it doesn't find the
signature its looking for.

I have attached the disassembled code, together with some snippets from
the BIOS boot specification and the doc_stage1 code from the GRUB stage1
directory. The code was disassembled using the Borland Turbo debugger,
so the operand ordering is
<operator> <destination>,
unlike the gcc ordering. Also, data segment references are offset from
code segment references by (minus) 0x100 bytes, causing a bit of
confusion with data embedded in the code segment.

If anybody has a canned solution, I'd love to see it. If someone has
DOC documentation that sheds light on what the IPL is doing, I can
probably get this working.

Thanks,
Ted Hildum

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Disassembled code + comments:

Option ROM header

0: 55 Signature byte 1
1: AA Signature byte 2
2: 10 Option ROM length in 512-byte blocks
3: EB 42 00 00 (jmp 147) Initialization entry point
7: 'DiskOnChip(C)', 0, '23', 0 Reserved
18: 0000 Offset to PCI data structure
1A: 0027 Offset to PnP expansion header

1C: 00 00 00 00 00 00 00 00 00 30 55 ???

PnP Expansion header structure

27: '$PnP' PnP signature bytes
2B: 01 Structure revision
2C: 02 Length in 16-byte increments
2D: 0000 Offset of next header, 0 if none
2F: 00 Reserved
30: A7 checksum
31: 6D 1A 01 10 Device identifier
35: 0007 Pointer to manufacturer string
37: 0007 Pointer to name string
39: 01 80 00 Device type code
3C: 94 Device indicators
3D: 0069 Boot Connection Vector, 0 if none
3F: 0000 Disconnection vector, 0 if none
41: 0000 Bootstrap entry vector, 0 if none
43: 0000 Reserved
45: 0000 Static resource information
vector, 0 if none

Starting at offset 47:
initialization:
cs:0147 50 push ax
cs:0148 2EA11A00 mov ax,cs:[001A]
cs:014C 0BC0 or ax,ax ;Check to see if there
is a PnP expansion header
cs:014E 58 pop ax
cs:014F 7418 je 0169 ;If no PnP expansion
header, jump to BCV

cs:0151 1E push ds ;Otherwise, see
whether we're being called
cs:0152 57 push di ; by a PnP-aware BIOS
cs:0153 56 push si
cs:0154 51 push cx
cs:0155 0E push cs
cs:0156 1F pop ds
cs:0157 BE2700 mov si,0027 ;offset of PnP signature
cs:015A B90400 mov cx,0004 ;4 bytes
cs:015D F3A6 rep cmpsb ;Compare these bytes
with those at ES:DI
cs:015F 59 pop cx ; (points to PnP
check structure)
cs:0160 5E pop si
cs:0161 5F pop di
cs:0162 1F pop ds
cs:0163 7504 jne 0169 ;if miscompare, jump to BCV
cs:0165 B82001 mov ax,0120 ;otherwise, return
0x120 in AX
cs:0168 CB retf ; ( say there is an
IPL device attached )

; ROM initialization return status bits
; AX Bit Description
; 8 1 = IPL Device supports INT 13h Block Device format
; 7 1 = Output Device supports INT 10h Character Output
; 6 1 = Input Device supports INT 9h Character Input
; 5:4 00 = No IPL device attached
; 01 = Unknown whether or not an IPL device is attached
; 10 = IPL device attached
; (RPL devices have a connection).
; 11 = Reserved
; 3:2 00 = No Display device attached
; 01 = Unknown whether or not a Display device is attached
; 10 = Display device attached
; 11 = Reserved
; 1:0 00 = No Input device attached
; 01 = Unknown whether or not an Input device is attached
; 10 = Input device attached
; 11 = Reserved

Starting at offset 69:

Bootstrap connection vector entry point:
cs:0169 9C pushf ;Save the world
cs:016A 50 push ax
cs:016B 53 push bx
cs:016C 51 push cx
cs:016D 52 push dx
cs:016E 56 push si
cs:016F 57 push di
cs:0170 55 push bp
cs:0171 1E push ds
cs:0172 06 push es
;--------------------------------------------------------------------------------------------
;Starting at 2000:0 and going up to A000:0, look for a block of memory
0x10000 in size
;with all zeroes in it. ES contains the segment of this block on exit.
cs:0173 BAC01F mov dx,1FC0 ; start looking at blocks
at 2000
cs:0176 33C0 xor ax,ax ;AX = 0
cs:0178 8EC0 mov es,ax ;ES = 0

cs:017A 33FF xor di,di ; set DI = 0
cs:017C 83C240 add dx,0040 ;
cs:017F 8EDA mov ds,dx ; next 1K block
cs:0181 81FA00A0 cmp dx,A000
cs:0185 741A je 01A1 ; quit when we reach A000
cs:0187 33F6 xor si,si ;SI = 0, beginning of 1K block
cs:0189 B90002 mov cx,0200 ;CX = 200 (512 decimal)
cs:018C FC cld ;string instructions
increment index registers
cs:018D AD lodsw ;DS:[SI++] --> AX
cs:018E 0BC0 or ax,ax
cs:0190 75E8 jne 017A ;if a non-zero word was
found, go to next block
cs:0192 E2F9 loop 018D ;get next word
cs:0194 83FF00 cmp di,0000
cs:0197 7502 jne 019B ;if this is first block with
all zeroes, set ES
cs:0199 1E push ds
cs:019A 07 pop es
cs:019B 47 inc di ;increment count of empty
1K blocks
cs:019C 83FF40 cmp di,0040
cs:019F 72DB jb 017C ;fall through if we have 64K
of zeroes
;--------------------------------------------------------------------------------------------
cs:01A1 83FF40 cmp di,0040
cs:01A4 7305 jnb 01AB ;if we found 64K of zeroes,
use that address
cs:01A6 BA0058 mov dx,5800
cs:01A9 8EC2 mov es,dx ;otherwise, use 5800:0
cs:01AB 0E push cs
cs:01AC 1F pop ds ;DS points to this BIOS
extension
cs:01AD 8BEC mov bp,sp
cs:01AF 83EC04 sub sp,0004 ;allocate some space on
the stack
cs:01B2 8CC0 mov ax,es
cs:01B4 8C46FE mov [bp-02],es ;store ES in allocated
variable
cs:01B7 C746FCD400 mov word ptr [bp-04],00D4 ; store 0xD4 in
allocated variable
cs:01BC 0E push cs ;push segment for far
return after near call
cs:01BD E81400 call 01D4
cs:01C0 8B46FE mov ax,[bp-02]
cs:01C3 8EC0 mov es,ax ;ES has returned value from
call
cs:01C5 33FF xor di,di ;DI = 0
cs:01C7 B90080 mov cx,8000 ;32K count into CX
cs:01CA 33C0 xor ax,ax ;AX = 0
cs:01CC FC cld
cs:01CD F3AB rep stosw ;Fill 64K segment with
zeroes (clean up what we've used)
cs:01CF 8BE5 mov sp,bp ;de-allocate stack space
cs:01D1 E99200 jmp 0266
;--------------------------------------------------------------------------------------------

; On entry, stack looks like this:
; stored ES seg value
; 0xD4
; stored CS
; SP --> IP for return
;
cs:01D4 55 push bp
cs:01D5 8BEC mov bp,sp
cs:01D7 2EA12000 mov ax,cs:[0020] ;location contains 0
cs:01DB 0BC0 or ax,ax
cs:01DD 7503 jne 01E2 ;if non-zero use it to
load DS
cs:01DF 8B4604 mov ax,[bp+04] ;otherwise use CS
value off stack
cs:01E2 8ED8 mov ds,ax
cs:01E4 BB0000 mov bx,0000 ;index register BX = 0
cs:01E7 C687031000 mov byte ptr [bx+1003],00 ;Floor select
register
cs:01EC C687021085 mov byte ptr [bx+1002],85 ;DoC control
register
cs:01F1 C687021085 mov byte ptr [bx+1002],85
cs:01F6 BE0008 mov si,0800
cs:01F9 B1FF mov cl,FF ;DoC command in cl
(NAND_CMD_RESET maybe?)
cs:01FB E87A00 call 0278 ;Some DOC operation.
Issue command?

;This routine probably gets the block number of the binary partition in DX
cs:01FE FC cld
cs:01FF 06 push es
cs:0200 0E push cs
cs:0201 07 pop es
cs:0202 2E8B162200 mov dx,cs:[0022] ; 0 --> DX
cs:0207 46 inc si
cs:0208 4E dec si
cs:0209 81FA0008 cmp dx,0800
cs:020D 7D20 jge 022F ;escape from comparison
routine
cs:020F BF7101 mov di,0171 ;This is offset of 4-byte
signature string 84 A8 AC A0 at cs:271
cs:0212 B150 mov cl,50
cs:0214 E86100 call 0278 ;issue DOC command?

cs:0217 83C210 add dx,0010
cs:021A B108 mov cl,08
cs:021C E85C00 call 027B ;issue DOC command?

cs:021F B90400 mov cx,0004
cs:0222 E85000 call 0275 ;issue DOC command?

cs:0225 84871D10 test [bx+101D],al

cs:0229 A6 cmpsb ;compare with
signature bytes
cs:022A 75DC jne 0208 ;next block
cs:022C 4E dec si
cs:022D E2FA loop 0229
cs:022F 07 pop es

;This routine reads the contents of the binary partition into memory,
accumulating
;a checksum as it goes.
cs:0230 4A dec dx
cs:0231 32E4 xor ah,ah
cs:0233 B100 mov cl,00 ;DoC command 0
(NAND_CMD_READ0 ?)
cs:0235 E84000 call 0278
cs:0238 2E8B0E2400 mov cx,cs:[0024] ;0x3000 --> CX
cs:023D 33FF xor di,di ;DI = 0
cs:023F F7C1FF01 test cx,01FF
cs:0243 750B jne 0250 ;should fall through
every 256 loops
cs:0245 42 inc dx
cs:0246 E83200 call 027B ;shift window to next block
cs:0249 E82900 call 0275
cs:024C 84871D10 test [bx+101D],al
cs:0250 AC lodsb ;DS:[SI] --> AL
cs:0251 02E0 add ah,al ;accumulate checksum
cs:0253 AA stosb ;AL --> ES:[DI]
cs:0254 4E dec si ;keep SI pointing to
same location
cs:0255 E2E8 loop 023F ;do this CX times
cs:0257 2E3A262600 cmp ah,cs:[0026] ;checksum == 0x55 ?
cs:025C 7506 jne 0264 ;fail exit if not
cs:025E 5D pop bp
cs:025F 06 push es ;segmment of
transferred code --> stack
cs:0260 33C0 xor ax,ax
cs:0262 50 push ax ;offset of transferred
code --> stack
cs:0263 CB retf ;Use far return to jump
to ES:0
cs:0264 5D pop bp
cs:0265 CB retf ;Fail exit if bad checksum
;--------------------------------------------------------------------------------------------
cs:0266 07 pop es ;Restore the world
cs:0267 1F pop ds
cs:0268 5D pop bp
cs:0269 5F pop di
cs:026A 5E pop si
cs:026B 5A pop dx
cs:026C 59 pop cx
cs:026D 5B pop bx
cs:026E 58 pop ax
cs:026F 9D popf
cs:0270 CB retf ;Return to caller

cs:0271 84 A8 AC A0 ;Bytes to be tested for in
boot image
cs:0275 E99916 jmp 1911
cs:0278 E98516 jmp 1900
cs:027B E9B916 jmp 1937

cs:1900 C68704100B mov byte ptr [bx+1004],0B ;WP+CLE+CE -->
DoC_CDSNControl
cs:1905 880C mov [si],cl
cs:1907 C6871E1000 mov byte ptr [bx+101E],00 ;DoC_WritePipeTerm
cs:190C C687041009 mov byte ptr [bx+1004],09 ;DoC_CDSNControl

cs:1911 F687201080 test byte ptr [bx+1020],80 ;DoC_NOP
cs:1916 F687201080 test byte ptr [bx+1020],80 ;DoC_NOP
cs:191B F687201080 test byte ptr [bx+1020],80 ;DoC_NOP
cs:1920 F687201080 test byte ptr [bx+1020],80 ;DoC_NOP
cs:1925 F687041080 test byte ptr [bx+1004],80 ;DoC_CDSNControl
cs:192A 74F9 je 1925
cs:192C F687201080 test byte ptr [bx+1020],80 ;DoC_NOP
cs:1931 F687201080 test byte ptr [bx+1020],80 ;DoC_NOP
cs:1936 C3 ret

cs:1937 C68704100D mov byte ptr [bx+1004],0D ;WP+ALE+CE -->
DoC_CDSNControl
cs:193C 880C mov [si],cl
cs:193E 8814 mov [si],dl
cs:1940 8834 mov [si],dh
cs:1942 F6871C1020 test byte ptr [bx+101C],20 ;DoC_ConfigInput
cs:1947 7403 je 194C
cs:1949 C60400 mov byte ptr [si],00
cs:194C C6871E1000 mov byte ptr [bx+101E],00 ;DoC_WritePipeTerm
cs:1951 C687041009 mov byte ptr [bx+1004],09 ;WP + CE -->
DoC_CDSNControl
cs:1956 C3 ret

cs:1957 7A00 jp 1959
cs:1959 0000 add [bx+si],al
cs:195B 0000 add [bx+si],al
cs:195D 0000 add [bx+si],al
cs:195F 0000 add [bx+si],al
cs:1961 0000 add [bx+si],al
cs:1963 0000 add [bx+si],al

-----------------------------------------------------------------------------------
Snippets from doc_stage1:

/* Some macros to make it obvious what we're accessing */
#define BXREG DoC_CDSNControl
#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 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)

#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

/* doc_cmd: Send a command to the flash chip */
doc_cmd:
/* Enable CLE line to flash ( movb 0x1B, (bx) ) */
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
--------------------------------------------------------------------------------------------------------------------------