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