1; -----------------------------------------------------------------------
2;
3;   Copyright 1994-2009 H. Peter Anvin - All Rights Reserved
4;   Copyright 2009-2011 Intel Corporation; author: H. Peter Anvin
5;
6;   This program is free software; you can redistribute it and/or modify
7;   it under the terms of the GNU General Public License as published by
8;   the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
9;   Boston MA 02110-1301, USA; either version 2 of the License, or
10;   (at your option) any later version; incorporated herein by reference.
11;
12; -----------------------------------------------------------------------
13
14;
15; diskstart.inc
16;
17; Common early-bootstrap code for harddisk-based Syslinux derivatives.
18;
19
20Sect1Ptr0_VAL	equ 0xdeadbeef
21Sect1Ptr1_VAL	equ 0xfeedface
22
23%include "diskboot.inc"
24
25; ===========================================================================
26;  Padding after the (minimum) 512-byte boot sector so that the rest of
27;  the file has aligned sectors, even if they are larger than 512 bytes.
28; ===========================================================================
29
30		section .init
31align_pad	zb 512
32
33; ===========================================================================
34;  Start of LDLINUX.SYS
35; ===========================================================================
36
37LDLINUX_SYS	equ ($-$$)+TEXT_START
38ldlinux_sys:
39
40early_banner	db CR, LF, MY_NAME, ' ', VERSION_STR, ' ', 0
41		db CR, LF, 1Ah	; EOF if we "type" this in DOS
42
43		alignz 8
44ldlinux_magic	dd LDLINUX_MAGIC
45		dd LDLINUX_MAGIC^HEXDATE
46
47;
48; This area is patched by the installer.  It is found by looking for
49; LDLINUX_MAGIC, plus 8 bytes.
50;
51SUBVOL_MAX	equ 256
52CURRENTDIR_MAX	equ FILENAME_MAX
53
54patch_area:
55DataSectors	dw 0		; Number of sectors (not including bootsec)
56ADVSectors	dw 0		; Additional sectors for ADVs
57LDLDwords	dd 0		; Total dwords starting at ldlinux_sys,
58CheckSum	dd 0		; Checksum starting at ldlinux_sys
59				; value = LDLINUX_MAGIC - [sum of dwords]
60MaxTransfer	dw 127		; Max sectors to transfer
61EPAPtr		dw EPA - LDLINUX_SYS	; Pointer to the extended patch area
62
63;
64; Extended patch area -- this is in .data16 so it doesn't occupy space in
65; the first sector.  Use this structure for anything that isn't used by
66; the first sector itself.
67;
68		section .data16
69		alignz 2
70EPA:
71ADVSecPtr	dw ADVSec0 - LDLINUX_SYS
72CurrentDirPtr	dw CurrentDirName-LDLINUX_SYS	; Current directory name string
73CurrentDirLen	dw CURRENTDIR_MAX
74SubvolPtr	dw SubvolName-LDLINUX_SYS
75SubvolLen	dw SUBVOL_MAX
76SecPtrOffset	dw SectorPtrs-LDLINUX_SYS
77SecPtrCnt	dw (SectorPtrsEnd - SectorPtrs)/10
78
79;
80; Boot sector patch pointers
81;
82Sect1Ptr0Ptr	dw Sect1Ptr0 - bootsec		; Pointers to Sector 1 location
83Sect1Ptr1Ptr	dw Sect1Ptr1 - bootsec
84RAIDPatchPtr	dw kaboom.again - bootsec	; Patch to INT 18h in RAID mode
85
86;
87; Pointer to the Syslinux banner
88;
89BannerPtr	dw syslinux_banner - LDLINUX_SYS
90
91;
92; Base directory name and subvolume, if applicable.
93;
94%define HAVE_CURRENTDIRNAME
95		global CurrentDirName:data hidden, SubvolName:data hidden
96CurrentDirName	times CURRENTDIR_MAX db 0
97SubvolName	times SUBVOL_MAX db 0
98
99		section .init
100ldlinux_ent:
101;
102; Note that some BIOSes are buggy and run the boot sector at 07C0:0000
103; instead of 0000:7C00 and the like.  We don't want to add anything
104; more to the boot sector, so it is written to not assume a fixed
105; value in CS, but we don't want to deal with that anymore from now
106; on.
107;
108		jmp 0:.next	; Normalize CS:IP
109.next:		sti		; In case of broken INT 13h BIOSes
110
111;
112; Tell the user we got this far
113;
114		mov si,early_banner
115		call writestr_early
116
117;
118; Checksum data thus far
119;
120		mov si,ldlinux_sys
121		mov cx,[bsBytesPerSec]
122		shr cx,2
123		mov edx,-LDLINUX_MAGIC
124.checksum:
125		lodsd
126		add edx,eax
127		loop .checksum
128		mov [CheckSum],edx		; Save intermediate result
129		movzx ebx,si			; Start of the next sector
130
131;
132; Tell the user if we're using EBIOS or CBIOS
133;
134print_bios:
135		mov si,cbios_name
136		cmp byte [getonesec.jmp+1],(getonesec_ebios-(getonesec.jmp+2))
137		jne .cbios
138		mov si,ebios_name
139		mov byte [getlinsec.jmp+1],(getlinsec_ebios-(getlinsec.jmp+2))
140.cbios:
141		mov [BIOSName],si
142		call writestr_early
143
144		section .earlybss
145		global BIOSName
146		alignb 2
147%define	HAVE_BIOSNAME 1
148BIOSName	resw 1
149
150		section .init
151;
152; Now we read the rest of LDLINUX.SYS.
153;
154load_rest:
155		push bx				; LSW of load address
156
157		lea esi,[SectorPtrs]
158		mov cx,[DataSectors]
159		dec cx				; Minus this sector
160
161.get_chunk:
162		jcxz .done
163		mov eax,[si]
164		mov edx,[si+4]
165		movzx ebp,word [si+8]
166		sub cx,bp
167		push ebx
168		shr ebx,4			; Convert to a segment
169		mov es,bx
170		xor bx,bx
171		call getlinsec
172		pop ebx
173		imul bp,[bsBytesPerSec]		; Will be < 64K
174		add ebx,ebp
175		add si,10
176		jmp .get_chunk
177
178.done:
179
180;
181; All loaded up, verify that we got what we needed.
182; Note: the checksum field is embedded in the checksum region, so
183; by the time we get to the end it should all cancel out.
184;
185verify_checksum:
186		pop si				; LSW of load address
187		movzx eax,word [bsBytesPerSec]
188		shr ax,2
189		mov ecx,[LDLDwords]		; Total dwords
190		sub ecx,eax			; ... minus one sector
191		mov eax,[CheckSum]
192.checksum:
193		add eax,[si]
194		add si,4
195		jnz .nowrap
196		; Handle segment wrap
197		mov dx,ds
198		add dx,1000h
199		mov ds,dx
200.nowrap:
201		dec ecx
202		jnz .checksum
203
204		mov ds,cx
205
206		and eax,eax			; Should be zero
207		jz all_read			; We're cool, go for it!
208
209;
210; Uh-oh, something went bad...
211;
212		mov si,checksumerr_msg
213		call writestr_early
214		jmp kaboom
215
216;
217; -----------------------------------------------------------------------------
218; Subroutines that have to be in the first sector
219; -----------------------------------------------------------------------------
220
221
222
223;
224; getlinsec: load a sequence of BP floppy sector given by the linear sector
225;	     number in EAX into the buffer at ES:BX.  We try to optimize
226;	     by loading up to a whole track at a time, but the user
227;	     is responsible for not crossing a 64K boundary.
228;	     (Yes, BP is weird for a count, but it was available...)
229;
230;	     On return, BX points to the first byte after the transferred
231;	     block.
232;
233;            This routine assumes CS == DS.
234;
235		global getlinsec:function hidden
236getlinsec:
237		pushad
238		add eax,[Hidden]		; Add partition offset
239		adc edx,[Hidden+4]
240.jmp:		jmp strict short getlinsec_cbios
241
242;
243; getlinsec_ebios:
244;
245; getlinsec implementation for EBIOS (EDD)
246;
247getlinsec_ebios:
248.loop:
249                push bp                         ; Sectors left
250.retry2:
251		call maxtrans			; Enforce maximum transfer size
252		movzx edi,bp			; Sectors we are about to read
253		mov cx,retry_count
254.retry:
255
256		; Form DAPA on stack
257		push edx
258		push eax
259		push es
260		push bx
261		push di
262		push word 16
263		mov si,sp
264		pushad
265                mov ah,42h                      ; Extended Read
266		push ds
267		push ss
268		pop ds
269		call xint13
270		pop ds
271		popad
272		lea sp,[si+16]			; Remove DAPA
273		jc .error
274		pop bp
275		add eax,edi			; Advance sector pointer
276		adc edx,0
277		sub bp,di			; Sectors left
278		imul di,[bsBytesPerSec]
279                add bx,di			; Advance buffer pointer
280                and bp,bp
281                jnz .loop
282
283		popad
284                ret
285
286.error:
287		; Some systems seem to get "stuck" in an error state when
288		; using EBIOS.  Doesn't happen when using CBIOS, which is
289		; good, since some other systems get timeout failures
290		; waiting for the floppy disk to spin up.
291
292		pushad				; Try resetting the device
293		xor ax,ax
294		call xint13
295		popad
296		loop .retry			; CX-- and jump if not zero
297
298		;shr word [MaxTransfer],1	; Reduce the transfer size
299		;jnz .retry2
300
301		; Total failure.  Try falling back to CBIOS.
302		mov byte [getlinsec.jmp+1],(getlinsec_cbios-(getlinsec.jmp+2))
303		;mov byte [MaxTransfer],63	; Max possibe CBIOS transfer
304
305		pop bp
306		; ... fall through ...
307
308;
309; getlinsec_cbios:
310;
311; getlinsec implementation for legacy CBIOS
312;
313getlinsec_cbios:
314.loop:
315		push edx
316		push eax
317		push bp
318		push bx
319
320		movzx esi,word [bsSecPerTrack]
321		movzx edi,word [bsHeads]
322		;
323		; Dividing by sectors to get (track,sector): we may have
324		; up to 2^18 tracks, so we need to use 32-bit arithmetric.
325		;
326		div esi
327		xor cx,cx
328		xchg cx,dx		; CX <- sector index (0-based)
329					; EDX <- 0
330		; eax = track #
331		div edi			; Convert track to head/cyl
332
333		cmp eax,1023		; Outside the CHS range?
334		ja kaboom
335
336		;
337		; Now we have AX = cyl, DX = head, CX = sector (0-based),
338		; BP = sectors to transfer, SI = bsSecPerTrack,
339		; ES:BX = data target
340		;
341
342		call maxtrans			; Enforce maximum transfer size
343
344		; Must not cross track boundaries, so BP <= SI-CX
345		sub si,cx
346		cmp bp,si
347		jna .bp_ok
348		mov bp,si
349.bp_ok:
350
351		shl ah,6		; Because IBM was STOOPID
352					; and thought 8 bits were enough
353					; then thought 10 bits were enough...
354		inc cx			; Sector numbers are 1-based, sigh
355		or cl,ah
356		mov ch,al
357		mov dh,dl
358		xchg ax,bp		; Sector to transfer count
359		mov ah,02h		; Read sectors
360		mov bp,retry_count
361.retry:
362		pushad
363		call xint13
364		popad
365		jc .error
366.resume:
367		movzx ecx,al		; ECX <- sectors transferred
368		imul ax,[bsBytesPerSec]	; Convert sectors in AL to bytes in AX
369		pop bx
370		add bx,ax
371		pop bp
372		pop eax
373		pop edx
374		add eax,ecx
375		sub bp,cx
376		jnz .loop
377		popad
378		ret
379
380.error:
381		dec bp
382		jnz .retry
383
384		xchg ax,bp		; Sectors transferred <- 0
385		shr word [MaxTransfer],1
386		jnz .resume
387		jmp kaboom
388
389maxtrans:
390		cmp bp,[MaxTransfer]
391		jna .ok
392		mov bp,[MaxTransfer]
393.ok:		ret
394
395;
396;
397; writestr_early: write a null-terminated string to the console
398;	    This assumes we're on page 0.  This is only used for early
399;           messages, so it should be OK.
400;
401writestr_early:
402		pushad
403.loop:		lodsb
404		and al,al
405                jz .return
406		mov ah,0Eh		; Write to screen as TTY
407		mov bx,0007h		; Attribute
408		int 10h
409		jmp short .loop
410.return:	popad
411		ret
412
413;
414; Checksum error message
415;
416checksumerr_msg	db ' Load error - ', 0	; Boot failed appended
417
418;
419; BIOS type string
420;
421cbios_name	db 'CHS', 0			; CHS/CBIOS
422ebios_name	db 'EDD', 0			; EDD/EBIOS
423
424;
425; Debug routine
426;
427%ifdef debug
428safedumpregs:
429		cmp word [Debug_Magic],0D00Dh
430		jnz nc_return
431		jmp dumpregs
432%endif
433
434rl_checkpt	equ $				; Must be <= 8000h
435
436rl_checkpt_off	equ $-ldlinux_sys
437%ifndef DEPEND
438 %if rl_checkpt_off > 512-10			; Need minimum one extent
439  %assign rl_checkpt_overflow rl_checkpt_off - (512-10)
440  %error Sector 1 overflow by rl_checkpt_overflow bytes
441 %endif
442%endif
443
444;
445; Extent pointers... each extent contains an 8-byte LBA and an 2-byte
446; sector count.  In most cases, we will only ever need a handful of
447; extents, but we have to assume a maximally fragmented system where each
448; extent contains only one sector.
449;
450		alignz 2
451MaxInitDataSize	equ 96 << 10
452MaxLMA		equ LDLINUX_SYS+MaxInitDataSize
453SectorPtrs	zb 10*(MaxInitDataSize >> MIN_SECTOR_SHIFT)
454SectorPtrsEnd	equ $
455
456; ----------------------------------------------------------------------------
457;  End of code and data that have to be in the first sector
458; ----------------------------------------------------------------------------
459
460		section .text16
461all_read:
462		; We enter here with ES scrambled...
463		xor ax,ax
464		mov es,ax
465;
466; Let the user (and programmer!) know we got this far.  This used to be
467; in Sector 1, but makes a lot more sense here.
468;
469		mov si,late_banner
470		call writestr_early
471
472		mov si,copyright_str
473		call writestr_early
474
475
476;
477; Insane hack to expand the DOS superblock to dwords
478;
479expand_super:
480		xor eax,eax
481		mov si,superblock
482		mov di,SuperInfo
483		mov cx,superinfo_size
484.loop:
485		lodsw
486		dec si
487		stosd				; Store expanded word
488		xor ah,ah
489		stosd				; Store expanded byte
490		loop .loop
491
492
493;
494; Common initialization code
495;
496%include "init.inc"
497
498		pushad
499		mov eax,ROOT_FS_OPS
500		movzx dx,byte [DriveNumber]
501		; DH = 0: we are boot from disk not CDROM
502		mov ecx,[Hidden]
503		mov ebx,[Hidden+4]
504		mov si,[bsHeads]
505		mov di,[bsSecPerTrack]
506		movzx ebp,word [MaxTransfer]
507		pm_call pm_fs_init
508		pm_call load_env32
509		popad
510
511		section .bss16
512SuperInfo	resq 16			; The first 16 bytes expanded 8 times
513
514;
515; Banner information not needed in sector 1
516;
517		section .data16
518		global syslinux_banner
519syslinux_banner	db CR, LF, MY_NAME, ' ', VERSION_STR
520late_banner	db ' ', DATE_STR, 0
521
522		section .text16
523