MBR: add HLT to prevent system from melting on failure
[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 sectors         = (stack-8)
35 secpercyl       = (stack-12)
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         shrw    %cx             /* Bit 0 = fixed disk subset */
81         jnc     1f
82
83         /* We have EBIOS; patch in the following code at
84            read_sector_cbios: movb $0x42, %ah ;  jmp read_common */
85         movl    $0xeb42b4+((read_common-read_sector_cbios-4) << 24), \
86                 (read_sector_cbios)
87
88 1:
89         popw    %dx
90
91         /* Get (C)HS geometry */
92         movb    $0x08, %ah
93         int     $0x13
94         andw    $0x3f, %cx      /* Sector count */
95         pushw   %cx             /* Save sectors on the stack */
96         xorw    %ax, %ax
97         pushw   %ax             /* High word of sectors/cylinder */
98         movb    %dh, %al        /* dh = number of heads */
99         incw    %ax             /* From 0-based to count */
100         mulw    %cx             /* Heads*sectors -> sectors per cylinder */
101         pushw   %ax             /* Save sectors/cylinder on the stack */
102
103         xorl    %eax, %eax      /* Base */
104         cdq                     /* Root (%edx <- 0) */
105         call    scan_partition_table
106
107         /* If we get here, we have no OS */
108         jmp     missing_os
109
110 /*
111  * read_sector: read a single sector pointed to by %eax to 0x7c00.
112  * CF is set on error.  All registers saved.
113  */
114 read_sector:
115         pushal
116         xorl    %edx, %edx
117         movw    $bootsec, %bx
118         pushl   %edx    /* MSW of LBA */
119         pushl   %eax    /* LSW of LBA */
120         pushw   %es     /* Buffer segment */
121         pushw   %bx     /* Buffer offset */
122         pushw   $1      /* Sector count */
123         pushw   $16     /* Size of packet */
124         movw    %sp, %si
125
126         /* This chunk is skipped if we have ebios */
127         /* Do not clobber %eax before this chunk! */
128 read_sector_cbios:
129         divl    (secpercyl)
130         shlb    $6, %ah
131         movb    %ah, %cl
132         movb    %al, %ch
133         xchgw   %dx, %ax
134         divb    (sectors)
135         movb    %al, %dh
136         orb     %ah, %cl
137         incw    %cx     /* Sectors are 1-based */
138         movw    $0x0201, %ax
139
140 read_common:
141         movb    (driveno), %dl
142         int     $0x13
143         addw    $16, %sp        /* Drop DAPA */
144         popal
145         ret
146
147 /*
148  * read_partition_table:
149  *      Read a partition table (pointed to by %eax), and copy
150  *      the partition table into the ptab buffer.
151  *
152  *      Clobbers %si, %di, and %cx, other registers preserved.
153  *      %cx = 0 on exit.
154  *
155  *      On error, CF is set and ptab is overwritten with junk.
156  */
157 ptab    = _start+446
158
159 read_partition_table:
160         call    read_sector
161         movw    $bootsec+446, %si
162         movw    $ptab, %di
163         movw    $(16*4/2), %cx
164         rep ; movsw
165         ret
166
167 /*
168  * scan_partition_table:
169  *      Scan a partition table currently loaded in the partition table
170  *      area.  Preserve all registers.
171  *
172  *      On entry:
173  *        %eax - base (location of this partition table)
174  *        %edx - root (offset from MBR, or 0 for MBR)
175  *
176  *      These get pushed into stack slots:
177  *        28(%bp) - %eax - base
178  *        20(%bp) - %edx - root
179  */
180
181 scan_partition_table:
182         pushal
183         movw    %sp, %bp
184
185         /* Search for active partitions */
186         movw    $ptab, %bx
187         movw    $4, %cx
188         xorw    %ax, %ax
189         push    %bx
190         push    %cx
191 5:
192         testb   $0x80, (%bx)
193         jz      6f
194         incw    %ax
195         movw    %bx, %si
196 6:
197         addw    $16, %bx
198         loopw   5b
199
200         decw    %ax             /* Number of active partitions found */
201         jz      boot
202         jns     too_many_active
203
204         /* No active partitions found, look for extended partitions */
205         popw    %bx             /* %bx <- ptab */
206         popw    %cx             /* %cx <- 4    */
207 7:
208         movb    4(%bx), %al
209         cmpb    $0x0f, %al      /* 0x0f = Win9x extended */
210         je      8f
211         andb    $~0x80, %al     /* 0x85 = Linux extended */
212         cmpb    $0x05, %al      /* 0x05 = MS-DOS extended */
213         jne     9f
214
215         /* It is an extended partition.  Read the extended partition and
216            try to scan it.  If the scan returns, re-load the current
217            partition table and resume scan. */
218 8:
219         movl    8(%bx), %eax            /* Partition table offset */
220         movl    20(%bp), %edx           /* "Root" */
221         addl    %edx, %eax              /* Compute location of new ptab */
222         andl    %edx, %edx              /* Is this the MBR? */
223         jnz     10f
224         movl    %eax, %edx              /* Offset -> root if this was MBR */
225 10:
226         call    read_partition_table
227         jc      11f
228         call    scan_partition_table
229 11:
230         /* This returned, so we need to reload the current partition table */
231         movl    28(%bp), %eax           /* "Base" */
232         call    read_partition_table
233
234         /* fall through */
235 9:
236         /* Not an extended partition */
237         addw    $16, %bx
238         loopw   7b
239
240         /* Nothing found, return */
241         popal
242         ret
243
244 /*
245  * boot: invoke the actual bootstrap. (%si) points to the partition
246  *       table entry, and 28(%bp) has the partition table base.
247  */
248 boot:
249         movl    8(%si), %eax
250         addl    28(%bp), %eax
251         movl    %eax, 8(%si)    /* Adjust in-memory partition table entry */
252         call    read_sector
253         jc      disk_error
254         cmpw    $0xaa55, (bootsec+510)
255         jne     missing_os              /* Not a valid boot sector */
256         movw    $driveno, %sp
257         popw    %dx             /* dl -> drive number */
258         popw    %di             /* es:di -> $PnP vector */
259         popw    %es
260         cli
261         jmp     bootsec
262
263 /*
264  * error messages
265  */
266 missing_os:
267         movw    $missing_os_msg, %si
268         jmp     error
269 disk_error:
270         movw    $disk_error_msg, %si
271         jmp     error
272 too_many_active:
273         movw    $too_many_active_msg, %si
274         /* jmp error */
275
276 error:
277 2:
278         lodsb
279         andb    %al, %al
280         jz      3f
281         movb    $0x0e, %ah
282         movb    (BIOS_page), %bh
283         movb    $0x07, %bl
284         int     $0x10
285         jmp     2b
286 3:
287         int     $0x18           /* Boot failure */
288 die:
289         hlt
290         jmp     die
291
292 missing_os_msg:
293         .ascii  "Missing operating system.\r\n"
294         .byte   0
295 disk_error_msg:
296         .ascii  "Operating system load error.\r\n"
297         .byte   0
298 too_many_active_msg:
299         .ascii  "Multiple active partitions.\r\n"
300         .byte   0