ISOLINUX: fix EBIOS information passing in hybrid HDD mode
[profile/ivi/syslinux.git] / mbr / isohdpfx.S
1 /* -----------------------------------------------------------------------
2  *
3  *   Copyright 2007-2008 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 /*
29  * Modified MBR code used on an ISO image in hybrid mode.
30  *
31  * This doesn't follow the El Torito spec at all -- it is just a stub
32  * loader of a hard-coded offset, but that's good enough to load
33  * ISOLINUX.
34  */
35         
36         .code16
37         .text
38
39 HYBRID_MAGIC                    = 0x7078c0fb
40 isolinux_hybrid_signature       = 0x7c00+64
41 isolinux_start_hybrid           = 0x7c00+64+4
42
43         .globl  bootsec
44 /* Important: the top 6 words on the stack are passed to isolinux.bin */
45 stack           = 0x7c00
46 driveno         = (stack-6)
47 ebios_flag      = (stack-8)
48 sectors         = (stack-10)
49 heads           = (stack-12)
50 secpercyl       = (stack-16)
51
52 BIOS_page = 0x462
53
54         /* gas/ld has issues with doing this as absolute addresses... */
55         .section ".bootsec", "a", @nobits
56         .globl  bootsec
57 bootsec:
58         .space  512
59
60         .text
61         .globl  _start
62 _start:
63
64         cli
65         xorw    %ax, %ax
66         movw    %ax, %ds
67         movw    %ax, %ss
68         movw    $stack, %sp
69         movw    %sp, %si
70         pushw   %es             /* es:di -> $PnP header */
71         pushw   %di
72         pushw   %dx             /* dl -> drive number */
73         movw    %ax, %es
74         sti
75         cld
76
77         /* Copy down to 0:0x600 */
78         movw    $_start, %di
79         movw    $(512/2), %cx
80         rep; movsw
81
82         ljmpw   $0, $next
83
84 next:
85         /* Check to see if we have EBIOS */
86         pushw   %dx             /* drive number */
87         movb    $0x41, %ah      /* %al == 0 already */
88         movw    $0x55aa, %bx
89         xorw    %cx, %cx
90         xorb    %dh, %dh
91         stc
92         int     $0x13
93         jc      1f
94         cmpw    $0xaa55, %bx
95         jne     1f
96         andw    $1,%cx          /* Bit 0 = fixed disk subset */
97         jz      1f
98
99         /* We have EBIOS; patch in the following code at
100            read_sector_cbios: movb $0x42, %ah ;  jmp read_common */
101         movl    $0xeb42b4+((read_common-read_sector_cbios-4) << 24), \
102                 (read_sector_cbios)
103
104 1:
105         popw    %dx
106         pushw   %cx             /* Save EBIOS flag */
107
108         /* Get (C)HS geometry */
109         movb    $0x08, %ah
110         int     $0x13
111         andw    $0x3f, %cx      /* Sector count */
112         pushw   %cx             /* Save sectors on the stack */
113         movzbw  %dh, %ax        /* dh = max head */
114         incw    %ax             /* From 0-based max to count */
115         pushw   %ax             /* Save heads on the stack */
116         mulw    %cx             /* Heads*sectors -> sectors per cylinder */
117
118         /* Save sectors/cylinder on the stack */
119         pushw   %dx             /* High word */
120         pushw   %ax             /* Low word */
121
122         /*
123          * Load sectors.  We do this one at a time mostly to avoid
124          * pitfalls and to share code with the stock MBR code.
125          */
126         movw    $0x7c00, %bx
127         movw    $4, %cx         /* Sector count */
128         movl    (lba_offset), %eax
129
130 2:
131         call    read_sector
132         jc      disk_error
133         incl    %eax
134         addw    $512, %bx
135         loopw   2b
136
137         /*
138          * Okay, that actually worked... update the stack pointer
139          * and jump into isolinux.bin...
140          */
141         cmpl    $HYBRID_MAGIC,(isolinux_hybrid_signature)
142         jne     bad_signature
143         
144         cli
145         movw    $heads, %sp
146         jmp     isolinux_start_hybrid
147
148 bad_signature:
149         call    error
150         .ascii  "isolinux.bin missing or corrupt.\r\n"
151
152 /*
153  * read_sector: read a single sector pointed to by %eax to %es:%bx.
154  * CF is set on error.  All registers saved.
155  */
156 read_sector:
157         pushal
158         xorl    %edx, %edx
159         pushl   %edx    /* MSW of LBA */
160         pushl   %eax    /* LSW of LBA */
161         pushw   %es     /* Buffer segment */
162         pushw   %bx     /* Buffer offset */
163         pushw   $1      /* Sector count */
164         pushw   $16     /* Size of packet */
165         movw    %sp, %si
166
167         /* This chunk is skipped if we have ebios */
168         /* Do not clobber %eax before this chunk! */
169         /* This also relies on %bx and %edx as set up above. */
170 read_sector_cbios:
171         divl    (secpercyl)
172         shlb    $6, %ah
173         movb    %ah, %cl
174         movb    %al, %ch
175         xchgw   %dx, %ax
176         divb    (sectors)
177         movb    %al, %dh
178         orb     %ah, %cl
179         incw    %cx     /* Sectors are 1-based */
180         movw    $0x0201, %ax
181
182 read_common:
183         movb    (driveno), %dl
184         int     $0x13
185         addw    $16, %sp        /* Drop DAPA */
186         popal
187         ret
188
189 disk_error:
190         call    error
191         .ascii  "Operating system load error.\r\n"
192
193 /*
194  * Print error messages.  This is invoked with "call", with the
195  * error message at the return address.
196  */
197 error:
198         popw    %si
199 2:
200         lodsb
201         movb    $0x0e, %ah
202         movb    (BIOS_page), %bh
203         movb    $0x07, %bl
204         int     $0x10           /* May destroy %bp */
205         cmpb    $10, %al        /* Newline? */
206         jne     2b
207
208         int     $0x18           /* Boot failure */
209 die:
210         hlt
211         jmp     die
212
213         /* Address of pointer to isolinux.bin */
214 lba_offset = _start+432