2784fb8057673c9379c635c261a87b1e3d35db6b
[external/syslinux.git] / mbr / isohdpfx.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 /*
30  * Modified MBR code used on an ISO image in hybrid mode.
31  *
32  * This doesn't follow the El Torito spec at all -- it is just a stub
33  * loader of a hard-coded offset, but that's good enough to load
34  * ISOLINUX.
35  */
36
37 #include "adjust.h"
38
39         .code16
40         .text
41
42 HYBRID_MAGIC                    = 0x7078c0fb
43 isolinux_hybrid_signature       = 0x7c00+64
44 isolinux_start_hybrid           = 0x7c00+64+4
45
46         .globl  bootsec
47 /* Important: the top 6 words on the stack are passed to isolinux.bin */
48 stack           = 0x7c00
49 partoffset      = (stack-8)
50 driveno         = (stack-14)
51 heads           = (stack-16)
52 sectors         = (stack-18)
53 ebios_flag      = (stack-20)
54 secpercyl       = (stack-24)
55
56 BIOS_kbdflags   = 0x417
57 BIOS_page       = 0x462
58
59         /* gas/ld has issues with doing this as absolute addresses... */
60         .section ".bootsec", "a", @nobits
61         .globl  bootsec
62 bootsec:
63         .space  512
64
65         .text
66         .globl  _start
67 _start:
68         .byte   0x33, 0xed      /* xorw %bp, %bp */
69         cli
70         movw    %bp, %ss
71         movw    $stack, %sp
72         sti
73         cld
74
75         /* Check to see if we have a partition table entry */
76         xorl    %ebx, %ebx
77         xorl    %ecx, %ecx
78 #ifdef PARTITION_SUPPORT
79         andw    %si, %si                /* %si == 0 -> no partition data */
80         jz      1f
81         testb   $0x7f, (%si)            /* Invalid active flag field? */
82         jnz     1f
83         cmpb    %cl, 4(%si)             /* Partition type zero == invalid? */
84         je      1f
85         cmpl    $0x58504721, %eax       /* !GPT signature in EAX? */
86         jne     2f
87         cmpb    $0xed, 4(%si)           /* EFI partition type? */
88         jne     2f
89         
90         /* We have GPT partition information */
91         movl    (32+20)(%si), %ecx
92         movl    (36+20)(%si), %ebx
93         jmp     1f
94
95         /* We have non-GPT partition information */
96 2:
97         movl    8(%si), %ecx
98 #endif
99 1:
100         /* We have no partition information */
101         pushl   %ebx                    /*  -4: partoffset_hi */
102         pushl   %ecx                    /*  -8: partoffset_lo */
103         pushw   %es                     /* -10: es:di -> $PnP header */
104         pushw   %di                     /* -12: es:di -> $PnP header */
105
106         movw    %bp, %ds
107         movw    %bp, %es
108         
109         ADJUST_DRIVE
110         pushw   %dx                     /* -14: dl -> drive number */
111
112         /* Copy down to 0:0x600 */
113         movw    $0x7c00, %si
114         movw    $_start, %di
115         movw    $(512/2), %cx
116         rep; movsw
117
118         ljmpw   $0, $next
119 next:
120
121         /* Check to see if we have EBIOS */
122         pushw   %dx             /* drive number */
123         movb    $0x41, %ah      /* %al == 0 already */
124         movw    $0x55aa, %bx
125         xorw    %cx, %cx
126         xorb    %dh, %dh
127         stc
128         int     $0x13
129         jc      1f
130         cmpw    $0xaa55, %bx
131         jne     1f
132         andw    $1,%cx          /* Bit 0 = fixed disk subset */
133         jz      1f
134
135         /* We have EBIOS; patch in the following code at
136            read_sector_cbios: movb $0x42, %ah ;  jmp read_common */
137         movl    $0xeb42b4+((read_common-read_sector_cbios-4) << 24), \
138                 (read_sector_cbios)
139         jmp     1f
140 1:
141         popw    %dx
142         pushw   %cx             /* EBIOS flag */
143
144         /* Get (C)HS geometry */
145         movb    $0x08, %ah
146         int     $0x13
147         andw    $0x3f, %cx      /* Sector count */
148         popw    %bx             /* EBIOS flag */
149         pushw   %cx             /* -16: Save sectors on the stack */
150         movzbw  %dh, %ax        /* dh = max head */
151         incw    %ax             /* From 0-based max to count */
152         pushw   %ax             /* -18: Save heads on the stack */
153         mulw    %cx             /* Heads*sectors -> sectors per cylinder */
154
155         pushw   %bx             /* -20: EBIOS flag */
156         
157         /* Save sectors/cylinder on the stack */
158         pushw   %dx             /* -22: High word */
159         pushw   %ax             /* -24: Low word */
160
161         /*
162          * Load sectors.  We do this one at a time mostly to avoid
163          * pitfalls and to share code with the stock MBR code.
164          */
165         movw    $0x7c00, %bx
166         movw    $4, %cx         /* Sector count */
167         movl    (lba_offset), %eax
168
169 2:
170         call    read_sector
171         jc      disk_error
172         incl    %eax
173         addb    $(512 >> 8), %bh
174         loopw   2b
175
176         /*
177          * Okay, that actually worked... update the stack pointer
178          * and jump into isolinux.bin...
179          */
180         cmpl    $HYBRID_MAGIC,(isolinux_hybrid_signature)
181         jne     bad_signature
182
183         cli
184         movw    $ebios_flag, %sp
185
186         /*
187          * Use a ljmpw here to work around a bug in some unknown version
188          * of gas or ld when it comes to jumping to an absolute symbol...
189          *
190          * Look more closely into it if we ever are short on space.
191          */
192         ljmpw   $0, $isolinux_start_hybrid
193
194 bad_signature:
195         call    error
196         .ascii  "isolinux.bin missing or corrupt.\r\n"
197
198 /*
199  * read_sector: read a single sector pointed to by %eax to %es:%bx.
200  * CF is set on error.  All registers saved.
201  */
202 read_sector:
203         pushal
204         xorl    %edx, %edx
205         addl    (partoffset), %eax
206         adcl    (partoffset+4), %edx
207         pushl   %edx    /* MSW of LBA */
208         pushl   %eax    /* LSW of LBA */
209         pushw   %es     /* Buffer segment */
210         pushw   %bx     /* Buffer offset */
211         pushw   $1      /* Sector count */
212         pushw   $16     /* Size of packet */
213         movw    %sp, %si
214
215         /* This chunk is skipped if we have ebios */
216         /* Do not clobber %eax before this chunk! */
217         /* This also relies on %bx and %edx as set up above. */
218 read_sector_cbios:
219         divl    (secpercyl)
220         shlb    $6, %ah
221         movb    %ah, %cl
222         movb    %al, %ch
223         xchgw   %dx, %ax
224         divb    (sectors)
225         movb    %al, %dh
226         orb     %ah, %cl
227         incw    %cx     /* Sectors are 1-based */
228         movw    $0x0201, %ax
229
230 read_common:
231         movb    (driveno), %dl
232         int     $0x13
233         leaw    16(%si), %sp    /* Drop DAPA */
234         popal
235         ret
236
237 disk_error:
238         call    error
239         .ascii  "Operating system load error.\r\n"
240
241 /*
242  * Print error messages.  This is invoked with "call", with the
243  * error message at the return address.
244  */
245 error:
246         popw    %si
247 2:
248         lodsb
249         movb    $0x0e, %ah
250         movb    (BIOS_page), %bh
251         movb    $0x07, %bl
252         int     $0x10           /* May destroy %bp */
253         cmpb    $10, %al        /* Newline? */
254         jne     2b
255
256         int     $0x18           /* Boot failure */
257 die:
258         hlt
259         jmp     die
260
261         /* Address of pointer to isolinux.bin */
262 lba_offset = _start+432