File: doc_bootstub.S

package info (click to toggle)
mtd 20050122-2
  • links: PTS
  • area: main
  • in suites: sarge
  • size: 6,244 kB
  • ctags: 9,869
  • sloc: ansic: 97,013; asm: 1,055; sh: 558; makefile: 356; cpp: 68
file content (583 lines) | stat: -rw-r--r-- 15,854 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
/* -*-Asm-*- */
/*
 *  DOCBoot -- A very simple Linux bootloader for DiskOnChip
 *
 *  Author: Dan Brown <dan_brown@ieee.org>
 *
 *  Diskonchip Millennium Plus support by Kalev Lember <kalev@smartlink.ee>
 *
 *  Portions taken from the DOC Grub bootloader by
 *          David Woodhouse <dwmw2@infradead.org>
 *
 *  $Id: doc_bootstub.S,v 1.4 2005/01/03 18:31:10 dbrown 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.
 */

#include "doc_bootstub.h"
	
	.file	"doc_bootstub.S"

	.text

	/* Tell GAS to generate 16-bit instructions so that this code works
	   in real mode. */
	.code16

.globl _start; _start:
	/*
	 * _start is loaded by the DiskOnChip IPL.  0x3000 bytes are loaded
	 * at the first empty (all-zeros) 64K region in the range 0x20000 to
	 * 0x90000 (with 1k alignment).  If the 64k region is at 0x20000, then
	 * CS:IP at this point will be 0x2000:0
	 * DS will be the segment of the DOC itself.
	 */

#ifdef BIOS_EXTENSION
 #ifndef DOC_ADDRESS
  #error BIOS_EXTENSION requires DOC_ADDRESS
 #endif

       .word	BIOS_SIG	/* BIOS extension signature */
       .byte	((stub_end-_start)>>9)	/* BIOS extension size in 512 byte blocks */

	/* BIOS will call far _start + 3 */
#endif

/* Jump to the installer, which will install an int 18h or 19h handler and then
   return, waiting for the handler to be called.  The installer is placed after
   the handler to minimize the amount of interrupt handler code copied to the
   top of low memory. */
	jmp	install

/* The actual int 18h/19h handler does three things:
   1) Copy the real-mode kernel from the DOC into the same 64k memory segment
      originally used by the MSYS IPL to store the 0x3000 bytes of SPL (on the
      theory that we already know it's safe to use).
   2) Copy the rest of the kernel (the protected-mode part) from the DOC into
      high memory starting at 0x100000.  BIOS int15h function 87h is used to
      perform the low-to-high copy.
   3) Patch some variables in the real-mode kernel, that a Linux bootloader is
      supposed to set.  Then jump to the real-mode kernel.
 */

handler:
	pushw	%cs
	popw	%ds
	MSG(kernel_string)

/*
	mov	$0x88, %ah
	int	$0x15
	call	phword
*/

	cld

	movw	setup_seg, %ax
	movw	%ax, %es

		/* gdt_src is currently set to 0x8000.  Add the setup base
		   address to it, yielding a 32k offset from the start of the
		   setup segment.  The math is simplified by the fact that
		   setup_seg must be 1Kbyte aligned. */
	shrw	$4, %ax
	addw	%ax, gdt_src_mid

	movw	doc_seg, %ds
	movw	$BXREG, %bx
	movw	$SIREG, %si

		/* Enable the DiskOnChip ASIC */
#ifdef MILPLUS
	movb	$(DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT), BX_Mplus_DOCControl
	movb	$~(DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT), BX_Mplus_CtrlConfirm

		/* Assert ChipEnable and WriteProtect */
	movb	$(DOC_FLASH_CE | DOC_FLASH_WP), BX_Mplus_FlashSelect
#else
	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

		/* Flash command:	Reset */
	movb	$NAND_CMD_RESET, %al
	call	doc_cmd
#endif

	xorw	%di, %di
	movw	$0x20, %dx	/* scan from second eraseblock */
read_setup_sects:
	call	doc_readpage
	decw	%cs:low_sects
	jnz	read_setup_sects

#if 0
		/* Print the kernel version string.  Partially for fun, mostly
		   to show that we've loaded the low part and are about to
		   load the high part. */
	pushw	%ds
	pushw	%es
	popw	%ds
	pushw	%si
	pushw	%bx
	movw	kernel_version, %si
	addw	$0x200, %si
	call	message
	popw	%bx
	popw	%si
	popw	%ds
#endif

	movw	$0x8000, %di	/* Go to 32k from setup base */
	call	read_high_sects	/* copy the kernel sectors */

	pushw	%cs
	popw	%ds
	movw	initrd_sects, %ax
	orw	%ax, high_sects		/* high_sects was previously 0 */
	jz	skip_initrd
	movw	initrd_start, %ax
	movw	%ax, gdt_dst_mid

	pushw	%si
	pushw	%bx
	MSG(initrd_string)
	popw	%bx
	popw	%si

	movw	doc_seg, %ds

	call	read_high_sects	/* copy the initrd sectors */

	pushw	%cs
	popw	%ds

skip_initrd:

/* We're done with the DOC.  Set up some things a kernel loader is supposed
   to set, then try to boot! */

	MSG(done_string)
	MSG(cmdline)
	MSG(boot_string)

	movl	initrd_bytes, %eax

	pushw	%es
	popw	%ds	/* Now write parameters into setup segment */

	orl	%eax, ramdisk_size  /* ramdisk_size should be 0 before this */
	jz	skip_initrd2
	movw	%cs:initrd_start, %ax
	movw	%ax, ramdisk_image + 1
skip_initrd2:

		/* Compute the physical address of the commandline and
		   store into cmd_line_ptr.  Simplified by the fact that
		   CS must be 1k-aligned and cmdline is at a 256-byte-aligned
		   offset from CS. */
	pushw	%cs
	popw	%ax
	shrw	$4, %ax
	addw	$((cmdline-_start)>>8), %ax
	movw	%ax, cmd_line_ptr + 1

	movb	$0x81, loadflags
	movw	$0xffff, %ax
	movb	%al, type_of_loader
	movw	%ax, heap_end_ptr	/* Actually should be 0xfdff, but it
					   should never get that far */

	movw	%ds, %bx
	movw	%bx, %fs	/* Not strictly needed */
	movw	%bx, %gs	/* Not strictly needed */
	movw	%bx, %ss
	movw	%ax, %sp	/* We know we have 64k of space */

	incw	%ax		/* Now ax=0 */
	addw	$0x20, %bx
	pushw	%bx
	pushw	%ax
	cli
	lret		/* GO! */

/***************************************************************************/
		/* read_high_sects: Read pages from DOC into high memory.
		   Each page is first loaded into low memory using doc_readpage,
		   then copied to high memory using int 15h function 87h.
		   We read cs:high_sects pages (leaving cs:high_sects = 0 when
		   we're done.) */
read_high_sects:
	call	doc_readpage
	movw	%cx, %di	/* re-use the same low memory buffer */
	pushw	%es
	pushw	%cs
	popw	%es
	pushw	%si
	movw	$gdt, %si
	movw	$0x200, %cx
	movb	$0x87, %ah
	int	$0x15
	popw	%si
	popw	%es
	addw	$2, %cs:gdt_dst_mid
	decw	%cs:high_sects
	jnz	read_high_sects
	ret
/***************************************************************************/

		/* doc_readpage: Read a page from DOC to es:di
		   Then read the OOB.  If the magic tag (0xdbb1) isn't found
		   at OOB locations 6 and 7, try the next page, etc.  This
		   allows us not only to scan until we find the kernel image,
		   but also to skip over holes in the image (which are
		   presumably the result of bad blocks). */
nextpage:
	popw	%di
doc_readpage:
#ifdef MILPLUS
		/* Flash command:	Reset */
	movb	$NAND_CMD_RESET, %al
	call	doc_cmd
#endif
		/* Flash command:	Read0 */
	movb	$NAND_CMD_READ0, %al
	call	doc_cmd
#ifdef MILPLUS
	movb	$0, BX_Mplus_FlashAddress
	movb	%dl, BX_Mplus_FlashAddress
	movb	%dh, BX_Mplus_FlashAddress
		/* Terminate the write pipeline */
	movb	$0, BX_Mplus_WritePipeTerm
	movb	$0, BX_Mplus_WritePipeTerm
		/* Deassert ALE */
	movb	$0, BX_Mplus_FlashControl
	call	doc_wait
	testb	$0, BX_Mplus_ReadPipeInit
	testb	$0, BX_Mplus_ReadPipeInit
#else
	movb	$CDSN_CTRL_WP + CDSN_CTRL_ALE + CDSN_CTRL_CE, BX_CDSNControl
	movb	$0, SI_CDSN_IO
	movb	%dl, SI_CDSN_IO
	movb	%dh, SI_CDSN_IO
		/* This test is used in the MSYS IPL.  It appears to check
		   an undocumented bit in the ConfigurationInput register,
		   presumably to determine if the NAND chip is large enough to
		   require a 3-byte page number. */
	testb	$0x20, BX_ConfigurationInput
	jz	notbigchip
	movb	$0, SI_CDSN_IO
notbigchip:
	call	doc_wait
	testb	$0, BX_ReadPipeInit
#endif
	movw	$0x208, %cx	/* Read page + 8 bytes OOB */
	pushw	%di
rploop:
	lodsb
	stosb
	decw	%si
	loop	rploop
	incw	%dx
	cmpw	$0xb1db, %es:-2(%di)
	jne	nextpage
	subw	$8, %di
	popw	%cx	/* di = old di + 512. Return original di in cx */
	ret

                /* doc_cmd:      Send a command to the flash chip */
doc_cmd:
#ifdef MILPLUS
		/* Send the command to the flash */
	movb	%al, BX_Mplus_FlashCmd
		/* Terminate the write pipeline */
	movb	$0, BX_Mplus_WritePipeTerm
	movb	$0, BX_Mplus_WritePipeTerm
	/* fall through... */

                /* doc_wait:     Wait for the DiskOnChip to be ready */
doc_wait:
	/* FIXME: probably only three NOP's are needed */
	testb	$0xc0, BX_Mplus_NOP
	testb	$0xc0, BX_Mplus_NOP
	testb	$0xc0, BX_Mplus_NOP
	testb	$0xc0, BX_Mplus_NOP
doc_waitloop:    
	movb	BX_Mplus_FlashControl, %al
	andw	$0xc0, %ax
	cmpw	$0xc0, %ax
	jne	doc_waitloop
	ret
#else
                /* Enable CLE line to flash */
        movb    $CDSN_CTRL_WP + CDSN_CTRL_CLE + CDSN_CTRL_CE, BX_CDSNControl
                /* Write the actual command */
        movb	%al, SI_CDSN_IO
	/* fall through... */

                /* doc_wait:     Wait for the DiskOnChip to be ready */
doc_wait:
        movb	$0, BX_WritePipeTerm
	movb	$CDSN_CTRL_WP + CDSN_CTRL_CE, BX_CDSNControl
	testb	$0x80, BX_NOP
	testb	$0x80, BX_NOP
	testb	$0x80, BX_NOP
	testb	$0x80, BX_NOP
doc_waitloop:    
	testb	$0x80, BX_CDSNControl
        jz      doc_waitloop
	testb	$0x80, BX_NOP
	testb	$0x80, BX_NOP
        ret
#endif
                

/*
 * 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:
	movw	$0x0001, %bx
	movb	$0xe, %ah
	int	$0x10		/* display a byte */

message:
	lodsb
	cmpb	$0, %al
	jne	1b	/* if not end of string, jmp to display */
	ret

/***************************************************************************/
#if 0

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:    mov     $0xe,%ah
	int     $0x10
	popw    %ax
	ret

#endif
/***************************************************************************/
handler_end:

#ifdef DOC_ADDRESS
doc_seg:	.word DOC_ADDRESS
#else
doc_seg:	.word 0
#endif

initrd_start:	.word	0

/* gdt structure for high loading using int15/87 */
gdt:
		.skip   0x10
gdt_src_limit:  .word   0xffff
gdt_src_lo:     .byte   0
gdt_src_mid:    .byte   0x80
gdt_src_hi:     .byte   0
gdt_src_perm:   .byte   0x93
        .word   0
gdt_dst_limit:  .word   0xffff
gdt_dst_lo:     .byte   0
gdt_dst_mid:    .byte   0
gdt_dst_hi:     .byte   0x10
gdt_dst_perm:   .byte   0x93
                .word   0
                .skip   0x10

kernel_string:	.string "Loading kernel... "
initrd_string:	.string "Loading initrd... "
done_string:	.string "done.\n\rCommandline: "
boot_string:	.string "\n\rBooting!\n\r"

checksum:
setup_seg:	.word 0
low_sects:	.word 0
high_sects:	.word 0
initrd_sects:	.word 0
initrd_bytes:	.long 0

/* Make sure our parameters are in sync with the C code. */
.if ((.-checksum) <> PARAM_BYTES)
 .err
.endif

	.balign	256, 0xff	/* cmdline starts on 256-byte boundary... */
cmdline:
	.skip	256, 0xff	/* .. and is 256-bytes long. */
reloc_end:


/***************************************************************************
 ***************************************************************************
 Everything ABOVE this point is needed by the int 18h/19h handler, and will
 be copied to the top of low memory by the installer (below).  Everything
 below this point is needed only by the installer.
 ***************************************************************************
 ***************************************************************************/


install:
#ifndef DOC_ADDRESS
		/* Store the DiskOnChip segment */
	movw	%ds, %cs:doc_seg
#endif
	cld
	xorw	%dx, %dx
	movw	%dx, %ds

		/* Abort installation if any shift, control, or alt key is
		   depressed. */
	movb	0x0417, %al
	andb	$0x0f, %al
	jnz	skip_install

		/* Test for multiple loads due to aliased ROM addresses.  If
		   the code pointed to by the interrupt vector is identical to
		   our code, just return. */
	movw	(DOC_BIOS_HOOK * 4 + 2), %es
	pushw	%cs
	popw	%ds
	xorw	%di, %di
	xorw	%si, %si
	movw	$handler_end, %cx
	repe
	cmpsb
	je	skip_install

	MSG(installer_string)

		/* Store the setup segment */
	movw	%cs, setup_seg

	movb	$0x88, %ah
	int	$0x15		/* Get top of high mem (-1M) in 1k-blocks */
	cmpw	$(0x4000-0x400), %ax /* safety check: if it's above 15M ... */
	jae	topmem_ok
	movw	$(0x4000-0x400), %ax /* ... replace with 15M. */
topmem_ok:
	addw	$0x400, %ax	/* Adjust for 1M offset */
	shlw	$1, %ax		/* Convert to sectors */
	subw	initrd_sects, %ax /* Compute start of initrd */
	shlw	$1, %ax		/* Convert to 256-byte blocks */
	andw	$0xfff0, %ax	/* Round down to 4k block (req'd by kernel) */
	movw	%ax, initrd_start	/* Store initrd start for later use */

	/* Now install the handler.  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 18h/19h 
	   3. Copy the handler code and data into the area we just reserved.
	   4. Return, and wait for our INT 18h/19h to be called.
	*/

		/* Find top of low memory */
	movw	%dx, %ds
	movw	0x0413, %ax

		/* Steal enough room from the top.  The line below is
		   suboptimal for relocated code size of 2k bytes or less, but
		   it's not a big deal (1-2 bytes). */
	subw	$(((reloc_end-_start)+1023)>>10), %ax
	movw	%ax,0x0413

		/* Generate segment address */
	shlw	$6,%ax
	movw	%ax,%es

		/* Set up our shiny new INT 18h/19h handler */
	movw	%ax,(DOC_BIOS_HOOK * 4 + 2)
	movw	$handler,(DOC_BIOS_HOOK * 4)

		/* Copy the handler into the new segment we've just reserved */
	movw	$(reloc_end-_start), %cx
	pushw	%cs
	popw	%ds
	xorw	%si,%si
	xorw	%di,%di
	rep
	movsb

skip_install:
	lret	

installer_string: .string "Installing DOCBoot.\n\r"

install_end:

	.balign	512, 0xff	/* Pad the entire stub to a 512-byte boundary */
stub_end:


/**************************************************************************
 **************************************************************************
   The symbols defined below will not be emitted by the assembler.  This
   is just here as a convenient way of encoding the offsets for use in the
   bootloader.
   This section adapted from arch/i386/boot/boot.S
 **************************************************************************
 **************************************************************************/

	.section absolute
	.org 512
kernel_start:	.word 0
        # This is the setup header, and it must start at %cs:2 (old 0x9020:2)
header_sig:	.ascii  "...."          # header signature
header_version: .word   0               # header version number (>= 0x0105)
realmode_swtch: .word   0, 0            # default_switch, SETUPSEG
start_sys_seg:  .word   0
kernel_version: .word   0		# pointing to kernel version string
type_of_loader: .byte   0               # = 0, old one (LILO, Loadlin,
loadflags:	.byte   0		# If set, the kernel is loaded high
setup_move_size: .word  0               # size to move, when setup is not
code32_start:	.long   0               # 0x100000 = default for big kernel
ramdisk_image:  .long   0               # address of loaded ramdisk image
ramdisk_size:   .long   0               # its size in bytes
bootsect_kludge: .word  0, 0
heap_end_ptr:   .word   0
pad1:           .word   0
cmd_line_ptr:   .long   0               # If nonzero, a 32-bit pointer
ramdisk_max:    .long   0               # The highest safe address