use sys.stdout.write instead of print to prevent additional newlines
[grml2usb.git] / mbr / mbr.S
1 /* $MirOS: src/sys/arch/i386/stand/mbr/mbr.S,v 1.13 2009/03/04 10:50:28 tg Exp $ */
2
3 /*-
4  * Copyright (c) 2009
5  *      Thorsten Glaser <tg@mirbsd.org>
6  *
7  * Provided that these terms and disclaimer and all copyright notices
8  * are retained or reproduced in an accompanying document, permission
9  * is granted to deal in this work without restriction, including un-
10  * limited rights to use, publicly perform, distribute, sell, modify,
11  * merge, give away, or sublicence.
12  *
13  * This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
14  * the utmost extent permitted by applicable law, neither express nor
15  * implied; without malicious intent or gross negligence. In no event
16  * may a licensor, author or contributor be held liable for indirect,
17  * direct, other damage, loss, or other issues arising in any way out
18  * of dealing in the work, even if advised of the possibility of such
19  * damage or existence of a defect, except proven that it results out
20  * of said person's immediate fault when using the work as intended.
21  *-
22  * Compile commands:
23  *      $ gcc -D_ASM_SOURCE -DBOOTMANAGER -D__BOOT_VER=\"0AA6\" -c mbr.S
24  *      $ ld -nostdlib -Ttext 0x0600 -N -Bstatic -e _start -o mbrmgr.elf mbr.o
25  *      $ objcopy -O binary mbrmgr.elf mbrmgr
26  *      $ gcc -D_ASM_SOURCE -DBOOT_QUIET -D__BOOT_VER=\"0AA6\" -c mbr.S
27  *      $ ld -nostdlib -Ttext 0x0600 -N -Bstatic -e _start -o mbrldr.elf mbr.o
28  *      $ objcopy -O binary mbrldr.elf mbrldr
29  */
30
31         .intel_syntax noprefix
32         .code16
33         .text
34
35         .globl  _start
36 _start: xor     eax,eax
37         mov     ss,ax
38         mov     sp,offset Lstack
39         push    eax
40         popfd
41         mov     ds,ax
42         mov     es,ax
43         mov     si,offset Lbadr
44         mov     di,offset _start
45         mov     cx,0x0200       /* size of one sector */
46         push    si              /* load / return address */
47         push    ax
48         push    offset Lmain
49         rep     movsb
50         lret
51
52         /* entry message */
53 Lemsg:  .ascii  "Mir-"
54         .ascii  __BOOT_VER
55 #ifdef BOOTMANAGER
56         /* Lem<nn> are patch points */
57         .ascii  ": 0="
58 Lem00:  .ascii  "00 1="
59 Lem01:  .ascii  "00 2="
60 Lem02:  .ascii  "00 3="
61 Lem03:  .ascii  "00 4=hd0 5=fd0  Enter=default (timeout)\r\n"
62 #endif
63         .asciz  ">"
64
65         /* failure message */
66 Lfmsg:  .asciz  "bad magic\r\n"
67
68 #if 1
69         /* okay boot message */
70 Lbmsg:  .asciz  " OK\r"
71 #endif
72
73         /* output NUL-terminated string from ds:si */
74 Lotxt0: mov     ah,0x0E
75         mov     bx,7
76         int     0x10
77 Lotxt:  lodsb
78         or      al,al
79         jnz     Lotxt0
80         ret
81
82 Lmain:  sti
83 #ifdef BOOTMANAGER
84         /* patch the partition type values into the message */
85         mov     di,offset Lem00
86         mov     al,ds:[Lptab + 0x04]
87         call    LpBY
88         mov     di,offset Lem01
89         mov     al,ds:[Lptab + 0x14]
90         call    LpBY
91         mov     di,offset Lem02
92         mov     al,ds:[Lptab + 0x24]
93         call    LpBY
94         mov     di,offset Lem03
95         mov     al,ds:[Lptab + 0x34]
96         call    LpBY
97 #endif
98 #if !defined(BOOT_QUIET) || defined(BOOTMANAGER)
99         mov     si,offset Lemsg
100         call    Lotxt
101 #endif
102
103         /* fake invalid partition entry for MBR/FDD boot */
104         mov     di,offset Lptab + 0x40
105         xor     eax,eax
106         stosw
107         inc     ax
108         stosw
109         dec     ax
110         stosd
111         stosd
112
113         /* force bad magic if sector load fails */
114         mov     ds:[Lbmag],al
115
116 #ifdef BOOTMANAGER
117 #if 0 /* see above, eax is already zero here */
118         xor     ax,ax           /* read CMOS clock ticks since midnight */
119 #endif
120         int     0x1A            /* 32-bit result in cx:dx */
121         mov     di,cx           /* save it in edi for later */
122         shl     edi,16
123         mov     di,dx
124         add     edi,183         /* 10 seconds, rounded up one tick */
125         Lptmo = . - 4           /* offset of the "183" above */
126
127         /* input loop with timeout */
128 Lwkey:  mov     ah,1
129         int     0x16            /* check if a key was pressed */
130         jnz     Lgkey           /* yeap */
131         /* delay loop */
132         xor     ax,ax
133         int     0x1A
134         shl     ecx,16
135         mov     cx,dx
136         or      al,al           /* past midnight? */
137         jz      Lsday           /* no */
138         add     ecx,1573040     /* should be 1572480, but according to RBIL… */
139 Lsday:  cmp     ecx,edi         /* time is over? */
140         mov     al,13
141         ja      Lfkey           /* yep, fake a return keypress */
142         jmp     Lwkey
143
144         /* input loop without timeout */
145 Lgkey:  mov     ah,1
146         int     0x16            /* check if a key was pressed */
147         jz      Lgkey
148         mov     ah,0
149         int     0x16
150 #endif /* BOOTMANAGER */
151 Lfkey:  mov     bx,offset Lptab
152         mov     dl,0x80         /* drive to load from */
153 #ifndef BOOTMANAGER
154         jmp     Lscan
155 #else
156         sub     al,13
157         je      Lscan           /* CR / Return / Enter */
158         jb      Lgkey           /* invalid input */
159         sub     al,('0' - 13)
160         jb      Lgkey           /* invalid input */
161         cmp     al,5            /* floppy */
162         ja      Lgkey           /* invalid input */
163         jb      LdoHD           /* hard disc */
164         mov     dl,0            /* drive to load from */
165         dec     ax              /* 5 -> 4 */
166 #endif
167 LdoHD:  shl     al,4            /* 0..4 where 4 is virtual partition */
168         add     bl,al           /* we boot this one */
169         jmp     Lboot
170
171         /* scan the partition table for an active partition */
172 Lscan:  mov     al,[dpart]      /* try hard-coded by fdisk(8) 'fdef' first */
173         cmp     al,3
174         jbe     LdoHD
175 Lspar:  cmp     byte ptr [bx],0x80
176         je      Lboot           /* found an active partition */
177         add     bl,0x10
178         cmp     bl,0xFE         /* BX = 0x07FE = Lptab + 0x40 */
179         jb      Lspar
180         /* boot the virtual partition #4 (MBR) */
181
182 Lboot:  /* try to boot, first LBA (we're on a HDD) then CHS */
183         mov     [bx],dl         /* drive (0x80 or 0x00) */
184         mov     si,offset Lpblk /* LBA parameter block */
185         mov     di,si
186         mov     ax,0x0010
187         stosw                   /* size of LBA parameter block */
188         mov     al,1
189         stosw                   /* number of sectors to load */
190         pop     ax
191         push    ax
192         push    bx
193         stosw                   /* load address offset */
194         xor     ax,ax
195         stosw                   /* load address segment */
196         mov     eax,[bx+8]
197         stosd                   /* LBA offset of start sector (low 32 bit) */
198         xor     ax,ax
199         stosw                   /* high 32 bit */
200         stosw                   /* high 32 bit */
201         mov     ah,0x42         /* LBA extended read */
202         call    Lload           /* try to boot that */
203         pop     si              /* edited partition table entry */
204         pop     bx              /* load offset (ES=CS=SS=DS=0000h) */
205         push    bx
206         push    si
207         mov     ax,0x0201       /* CHS read 0x01 sectors */
208         mov     cx,[si+2]       /* cylinder; sector number */
209         mov     dx,[si]         /* head; drive number */
210         call    Lload
211         mov     si,offset Lfmsg
212         call    Lotxt
213 #if 0
214 Lfail:  jmp     Lfail
215 #else
216         xor     ax,ax
217         int     0x16
218         ljmp    0xF000,0xFFF0
219 #endif
220
221 Lload:  mov     bp,4            /* number of tries */
222 Lldlp:  pusha
223         int     0x13
224         popa
225         jc      Lldre           /* error, try again */
226         cmp     word ptr ds:[Lbmag],0xAA55
227         jne     Lldre           /* bad magic, try again */
228 #if 0
229         mov     ax,0x0E0D       /* output a carriage return */
230         xor     bx,bx
231         int     0x10
232 #else
233         mov     si,offset Lbmsg
234         call    Lotxt
235 #endif
236         pop     si              /* Lload return address */
237         pop     si              /* partition table entry */
238         mov     dl,[si]
239         /* DS:SI point to partition table entry, DL is set */
240         cli                     /* be nice :) */
241         ret                     /* jump to 0000:7C00h */
242 Lldre:  pusha
243         xor     ax,ax           /* reset drive */
244         int     0x13
245         popa
246         dec     bp              /* another try left? */
247         jnz     Lldlp
248         ret
249
250 #ifdef BOOTMANAGER
251 LpBY:   mov     ah,al
252         shr     al,4
253         and     ah,0x0F
254         add     ax,0x3030
255         cmp     al,0x39
256         jbe     LpBY1
257         add     al,7
258 LpBY1:  cmp     ah,0x39
259         jbe     LpBY2
260         add     ah,7
261 LpBY2:  stosw
262         ret
263 #endif
264
265         . = _start + 0x01B7
266         .globl  dpart
267         .size   dpart,1
268 dpart:  .byte   0xFF            /* default partition [0..3] or none */
269
270         . = _start + 0x01B8
271 Lntid:  .long   0               /* Microsoft® NT® volume identifier */
272 Lpad1:  .byte   0, 0
273
274         . = _start + 0x01BE
275         /* partition table */
276 Lptab:  .long   0, 0, 0, 0      /* partition entry #0 */
277         .long   0, 0, 0, 0      /* partition entry #1 */
278         .long   0, 0, 0, 0      /* partition entry #2 */
279         /* partition entry #3 + pre-installation hint */
280         .word   0, 0, 0, 0, 0
281         . = _start + 0x01F8
282         .size   Lhint,2
283 #ifdef BOOTMANAGER
284 Lhint:  .word   (Lptmo - _start)
285 #else
286 Lhint:  .word   0xFFFF
287 #endif
288 Lpad2:  .word   0, 0
289
290         . = _start + 0x01FE
291 Lpmag:  .word   0xAA55          /* BIOS boot magic */
292
293         Lstack = 0x4000
294         Lpblk = 0x5000
295
296         Lbadr = 0x7C00
297         Lbmag = Lbadr + 0x01FE