New MBR which supports logical partitions.
[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 bootsec = 0x7c00
32 stack   = bootsec
33 driveno = (stack-4)
34 heads   = (stack-6)
35 sectors = (stack-8)
36
37 BIOS_page = 0x462
38         
39         .globl  _start
40 _start:
41         cli
42         xorw    %ax, %ax
43         movw    %ax, %ds
44         movw    %ax, %ss
45         movw    $stack, %sp
46         pushw   %es             /* es:di -> $PnP header */
47         pushw   %di
48         pushw   %dx             /* dl -> drive number */
49         movw    %ax, %es
50         sti
51         cld
52
53         /* Copy down to 0:0x600 */
54         movw    %sp, %si
55         movw    $_start, %di
56         movw    $(512/2), %cx
57         rep; movsw
58
59         ljmpw   $0, $next
60
61 next:
62         /* Check to see if we have EBIOS */
63         pushw   %dx             /* drive number */
64         movw    $0x4100, %ax
65         movw    $0x55aa, %bx
66         xorw    %cx, %cx
67         xorb    %dh, %dh
68         stc
69         int     $0x13
70         jc      1f
71         cmpw    $0xaa55, %bx
72         jne     1f
73         testb   $0x01, %cl
74         jz      1f
75
76         /* We have EBIOS; patch in a jump to read_sector_ebios */
77         movw    $0xeb+((read_sector_ebios-read_sector_cbios+2)<< 8), (read_sector_cbios)
78
79 1:
80         popw    %dx
81
82         /* Get (C)HS geometry */
83         movb    $0x08, %ah
84         int     $0x13
85         xorw    %ax, %ax
86         movb    %dh, %al        /* dh = number of heads */
87         incw    %ax             /* From 0-based to count */
88         pushw   %ax             /* Save heads on the stack */
89         andw    $0x3f, %cx      /* Sector count */
90         pushw   %cx             /* Save sectors on the stack */
91
92         xorl    %eax, %eax
93         pushl   %eax            /* Base */
94         pushl   %eax            /* Root */
95         call    scan_partition_table
96
97         
98         /* If we get here, we have no OS */
99         jmp     missing_os
100
101 /*
102  * read_sector: read a single sector pointed to by %eax to 0x7c00.
103  * CF is set on error.  All registers clobbered.
104  */
105 read_sector:
106 read_sector_cbios:
107         movl    %eax, %edx
108         shrl    $16, %edx
109         divw    (sectors)
110         incw    %dx
111         movw    %dx, %cx
112         xorw    %dx, %dx
113         divw    (heads)
114         /* dx = head, ax = cylinder */
115         movb    %al, %ch
116         shrw    $2, %ax
117         shrw    %ax
118         andb    $0xc0, %al
119         orb     %al, %cl
120         movb    %dl, %dh
121         movw    $bootsec, %bx
122         movw    $0x0201, %ax
123         jmp     read_common
124 read_sector_ebios:
125         movw    $dapa, %si
126         movl    %eax, 8(%si)
127         movb    $0x42, %ah
128 read_common:
129         int     $0x13
130         ret
131
132 /*
133  * read_partition_table:
134  *      Read a partition table (pointed to by %eax), and copy
135  *      the partition table into the ptab buffer.
136  *      Preserve registers.
137  */
138 ptab    = _start+446
139
140 read_partition_table:
141         pushal
142         call    read_sector
143         jc      20f
144         movw    $bootsec+446, %si
145         movw    $ptab, %di
146         movw    $(16*4/2), %cx
147         rep ; movsw
148 20:
149         popal
150         ret
151                         
152 /*
153  * scan_partition_table:
154  *      Scan a partition table currently loaded in the partition table
155  *      area.  Preserve 16-bit registers.
156  *
157  *      On stack:
158  *        18(%bp) - root (offset from MBR, or 0 for MBR)
159  *        22(%bp) - base (location of this partition table)
160  */
161         
162 scan_partition_table:
163         pusha
164         movw    %sp, %bp
165
166         /* Search for active partitions */      
167         movw    $ptab, %di
168         movw    $4, %cx
169         xorw    %ax, %ax
170 5:
171         testb   $0x80, (%di)
172         jz 6f
173         incw    %ax
174         movw    %di, %si
175 6:
176         addw    $16, %di
177         loopw   5b
178
179         cmpw    $1, %ax         /* Number of active partitions found */
180         je      boot
181         ja      too_many_active
182
183         /* No active partitions found, look for extended partitions */
184         movw    $ptab, %di
185         movb    $4, %cl         /* cx == 0 here */
186 7:
187         movb    4(%di), %al
188         cmpb    $0x05, %al      /* MS-DOS extended */
189         je      8f
190         cmpb    $0x0f, %al      /* Win9x extended */
191         je      8f
192         cmpb    $0x85, %al      /* Linux extended */
193         jne     9f
194         
195         /* It is an extended partition.  Read the extended partition and
196            try to scan it.  If the scan returns, re-load the current
197            partition table and resume scan. */
198 8:
199         movl    8(%di), %eax            /* Partition table offset */
200         movl    18(%bp), %edx           /* "Root" */
201         addl    %edx, %eax              /* Compute location of new ptab */
202         andl    %edx, %edx              /* Is this the MBR? */
203         jnz     10f
204         movl    %eax, %edx              /* Offset -> root if this was MBR */
205 10:
206         pushl   %eax                    /* Push new base */
207         pushl   %edx                    /* Push new root */
208         call    read_partition_table
209         jc 11f
210         call    scan_partition_table
211 11:
212         addw    $8, %sp
213         /* This returned, so we need to reload the current partition table */
214         movl    22(%bp), %eax
215         call    read_partition_table
216
217         /* fall through */              
218 9:
219         /* Not an extended partition */
220         addw    $16, %di
221         loopw   7b
222
223         /* Nothing found, return */
224         popa
225         ret
226
227 /*
228  * boot: invoke the actual bootstrap. (%si) points to the partition
229  *       table entry, and 22(%bp) has the partition table base.
230  */
231 boot:
232         movl    8(%si), %eax
233         addl    22(%bp), %eax
234         movl    %eax, 8(%si)
235         call    read_sector
236         jc      disk_error
237         cmpw    $0xaa55, (bootsec+0x510)
238         je      missing_os              /* Not a valid boot sector */
239         movw    $stack-6, %sp
240         popw    %dx             /* dl -> drive number */
241         popw    %di             /* es:di -> $PnP vector */
242         popw    %es
243         cli
244         jmp     bootsec
245
246 /*
247  * error messages
248  */
249 missing_os:
250         movw    $missing_os_msg, %si
251         jmp     error
252 disk_error:
253         movw    $disk_error_msg, %si
254         jmp     error
255 too_many_active:
256         movw    $too_many_active_msg, %si
257         /* jmp error */
258         
259 error:
260 2:
261         lodsb
262         andb    %al, %al
263         jz      3f
264         movb    $0x0e, %ah
265         movb    (BIOS_page), %bh
266         movb    $0x07, %bl
267         int     $0x10
268         jmp     2b
269 3:
270         int     $0x18           /* Boot failure */
271         jmp     .               /* Die */
272
273 missing_os_msg:
274         .ascii  "Missing operating system."
275         .byte   0
276 disk_error_msg:
277         .ascii  "Failed to load operating system."
278         .byte   0
279 too_many_active_msg:
280         .ascii  "Multiple active partititons."
281         .byte   0
282
283         .balign 4
284 dapa:
285         .short  16              /* Size of packet */
286         .short  1               /* Sector count */
287         .short  0x7c00          /* Buffer offset */
288         .short  0               /* Buffer segment */
289         .long   0               /* LSW of LBA */
290         .long   0               /* MSW of LBA */