Fix numerous problems in the new MBR code.
[profile/ivi/syslinux.git] / mbr / mbr.S
1 /* -----------------------------------------------------------------------
2  *   
3  *   Copyright 2007 H. Peter Anvin - All Rights Reserved
4  *
5  *   Permission is hereby granted, free of charge, to any person
6  *   obtaining a copy of this software and associated documentation
7  *   files (the "Software"), to deal in the Software without
8  *   restriction, including without limitation the rights to use,
9  *   copy, modify, merge, publish, distribute, sublicense, and/or
10  *   sell copies of the Software, and to permit persons to whom
11  *   the Software is furnished to do so, subject to the following
12  *   conditions:
13  *   
14  *   The above copyright notice and this permission notice shall
15  *   be included in all copies or substantial portions of the Software.
16  *   
17  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24  *   OTHER DEALINGS IN THE SOFTWARE.
25  *
26  * ----------------------------------------------------------------------- */
27
28         .code16
29         .text
30
31         .globl  bootsec
32 stack   = 0x7c00
33 driveno = (stack-6)
34 heads   = (stack-8)
35 sectors = (stack-10)
36
37 BIOS_page = 0x462
38
39         /* gas/ld has issues with doing this as absolute addresses... */
40         .section ".bootsec", "a", @nobits
41         .globl  bootsec
42 bootsec:
43         .space  512
44
45         .text   
46         .globl  _start
47 _start:
48         cli
49         xorw    %ax, %ax
50         movw    %ax, %ds
51         movw    %ax, %ss
52         movw    $stack, %sp
53         movw    %sp, %si
54         pushw   %es             /* es:di -> $PnP header */
55         pushw   %di
56         pushw   %dx             /* dl -> drive number */
57         movw    %ax, %es
58         sti
59         cld
60
61         /* Copy down to 0:0x600 */
62         movw    $_start, %di
63         movw    $(512/2), %cx
64         rep; movsw
65
66         ljmpw   $0, $next
67
68 next:
69         /* Check to see if we have EBIOS */
70         pushw   %dx             /* drive number */
71         movw    $0x4100, %ax
72         movw    $0x55aa, %bx
73         xorw    %cx, %cx
74         xorb    %dh, %dh
75         stc
76         int     $0x13
77         jc      1f
78         cmpw    $0xaa55, %bx
79         jne     1f
80         testb   $0x01, %cl
81         jz      1f
82
83         /* We have EBIOS; patch in a jump to read_sector_ebios */
84         movw    $0xeb+((read_sector_ebios-read_sector_cbios-2)<< 8), (read_sector_cbios)
85
86 1:
87         popw    %dx
88
89         /* Get (C)HS geometry */
90         movb    $0x08, %ah
91         int     $0x13
92         xorw    %ax, %ax
93         movb    %dh, %al        /* dh = number of heads */
94         incw    %ax             /* From 0-based to count */
95         pushw   %ax             /* Save heads on the stack */
96         andw    $0x3f, %cx      /* Sector count */
97         pushw   %cx             /* Save sectors on the stack */
98
99         xorl    %eax, %eax
100         pushl   %eax            /* Base */
101         pushl   %eax            /* Root */
102         call    scan_partition_table
103         
104         /* If we get here, we have no OS */
105         jmp     missing_os
106
107 /*
108  * read_sector: read a single sector pointed to by %eax to 0x7c00.
109  * CF is set on error.  All registers clobbered.
110  */
111 read_sector:
112 read_sector_cbios:
113         movl    %eax, %edx
114         shrl    $16, %edx
115         divw    (sectors)
116         incw    %dx
117         movw    %dx, %cx
118         xorw    %dx, %dx
119         divw    (heads)
120         /* dx = head, ax = cylinder */
121         movb    %al, %ch
122         shrw    $2, %ax
123         shrw    %ax
124         andb    $0xc0, %al
125         orb     %al, %cl
126         movb    %dl, %dh
127         movw    $bootsec, %bx
128         movw    $0x0201, %ax
129         jmp     read_common
130 read_sector_ebios:
131         movw    $dapa, %si
132         movl    %eax, 8(%si)
133         movb    $0x42, %ah
134 read_common:
135         movb    (driveno), %dl
136         int     $0x13
137         ret
138
139 /*
140  * read_partition_table:
141  *      Read a partition table (pointed to by %eax), and copy
142  *      the partition table into the ptab buffer.
143  *      Preserve registers.
144  */
145 ptab    = _start+446
146
147 read_partition_table:
148         pushal
149         call    read_sector
150         jc      20f
151         movw    $bootsec+446, %si
152         movw    $ptab, %di
153         movw    $(16*4/2), %cx
154         rep ; movsw
155 20:
156         popal
157         ret
158                         
159 /*
160  * scan_partition_table:
161  *      Scan a partition table currently loaded in the partition table
162  *      area.  Preserve 16-bit registers.
163  *
164  *      On stack:
165  *        18(%bp) - root (offset from MBR, or 0 for MBR)
166  *        22(%bp) - base (location of this partition table)
167  */
168         
169 scan_partition_table:
170         pusha
171         movw    %sp, %bp
172
173         /* Search for active partitions */      
174         movw    $ptab, %di
175         movw    $4, %cx
176         xorw    %ax, %ax
177 5:
178         testb   $0x80, (%di)
179         jz 6f
180         incw    %ax
181         movw    %di, %si
182 6:
183         addw    $16, %di
184         loopw   5b
185
186         cmpw    $1, %ax         /* Number of active partitions found */
187         je      boot
188         ja      too_many_active
189
190         /* No active partitions found, look for extended partitions */
191         movw    $ptab, %di
192         movb    $4, %cl         /* cx == 0 here */
193 7:
194         movb    4(%di), %al
195         cmpb    $0x05, %al      /* MS-DOS extended */
196         je      8f
197         cmpb    $0x0f, %al      /* Win9x extended */
198         je      8f
199         cmpb    $0x85, %al      /* Linux extended */
200         jne     9f
201         
202         /* It is an extended partition.  Read the extended partition and
203            try to scan it.  If the scan returns, re-load the current
204            partition table and resume scan. */
205 8:
206         movl    8(%di), %eax            /* Partition table offset */
207         movl    18(%bp), %edx           /* "Root" */
208         addl    %edx, %eax              /* Compute location of new ptab */
209         andl    %edx, %edx              /* Is this the MBR? */
210         jnz     10f
211         movl    %eax, %edx              /* Offset -> root if this was MBR */
212 10:
213         pushl   %eax                    /* Push new base */
214         pushl   %edx                    /* Push new root */
215         call    read_partition_table
216         jc 11f
217         call    scan_partition_table
218 11:
219         addw    $8, %sp
220         /* This returned, so we need to reload the current partition table */
221         movl    22(%bp), %eax
222         call    read_partition_table
223
224         /* fall through */              
225 9:
226         /* Not an extended partition */
227         addw    $16, %di
228         loopw   7b
229
230         /* Nothing found, return */
231         popa
232         ret
233
234 /*
235  * boot: invoke the actual bootstrap. (%si) points to the partition
236  *       table entry, and 22(%bp) has the partition table base.
237  */
238 boot:
239         movl    8(%si), %eax
240         addl    22(%bp), %eax
241         movl    %eax, 8(%si)
242         pushw   %si
243         call    read_sector
244         popw    %si
245         jc      disk_error
246         cmpw    $0xaa55, (bootsec+510)
247         jne     missing_os              /* Not a valid boot sector */
248         movw    $driveno, %sp
249         popw    %dx             /* dl -> drive number */
250         popw    %di             /* es:di -> $PnP vector */
251         popw    %es
252         cli
253         jmp     bootsec
254
255 /*
256  * error messages
257  */
258 missing_os:
259         movw    $missing_os_msg, %si
260         jmp     error
261 disk_error:
262         movw    $disk_error_msg, %si
263         jmp     error
264 too_many_active:
265         movw    $too_many_active_msg, %si
266         /* jmp error */
267         
268 error:
269 2:
270         lodsb
271         andb    %al, %al
272         jz      3f
273         movb    $0x0e, %ah
274         movb    (BIOS_page), %bh
275         movb    $0x07, %bl
276         int     $0x10
277         jmp     2b
278 3:
279         int     $0x18           /* Boot failure */
280         jmp     .               /* Die */
281
282 missing_os_msg:
283         .ascii  "Missing operating system."
284         .byte   0
285 disk_error_msg:
286         .ascii  "Operating system load error."
287         .byte   0
288 too_many_active_msg:
289         .ascii  "Multiple active partitions."
290         .byte   0
291
292         .balign 4
293 dapa:
294         .short  16              /* Size of packet */
295         .short  1               /* Sector count */
296         .short  0x7c00          /* Buffer offset */
297         .short  0               /* Buffer segment */
298         .long   0               /* LSW of LBA */
299         .long   0               /* MSW of LBA */