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