gptmbr: another confusion of start and end fields
[profile/ivi/syslinux.git] / mbr / gptmbr.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         .code16
29         .text
30
31         .globl  bootsec
32 stack           = 0x7c00
33
34 /* Partition table header here */
35 phdr            = stack         /* Above the stack, overwritten by bootsect */
36 /* Partition table sector here */
37 /* To handle > 32K we need to play segment tricks... */
38 psec            = _phdr + 512
39
40 /* BootGUID */
41 bootguid        = _start + 0x1a8
42 /* Where we put DS:SI */
43 dssi_out        = _start + 0x1be
44
45 BIOS_page       = 0x462
46
47         /* gas/ld has issues with doing this as absolute addresses... */
48         .section ".bootsec", "a", @nobits
49         .globl  bootsec
50 bootsec:
51         .space  512
52
53         .text
54         .globl  _start
55 _start:
56         cli
57         xorw    %ax, %ax
58         movw    %ax, %ds
59         movw    %ax, %ss
60         movw    $stack, %sp
61         movw    %sp, %si
62         pushw   %es             /* 4(%bp) es:di -> $PnP header */
63         pushw   %di             /* 2(%bp) */
64         pushw   %dx             /* 0(%bp) = %dl -> drive number */
65         movw    %ax, %es
66         sti
67         cld
68
69         /* Copy down to 0:0x600 */
70         movw    $_start, %di
71         movw    $(512/2), %cx
72         rep; movsw
73
74         ljmpw   $0, $next
75
76 next:
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         movw    %sp, %bp        /* %bp -> frame pointer: LEAVE UNCHANGED */
104         pushw   %cx             /* -2(%bp) Save sectors on the stack */
105         movzbw  %dh, %ax        /* dh = max head */
106         incw    %ax             /* From 0-based max to count */
107         mulw    %cx             /* Heads*sectors -> sectors per cylinder */
108
109         /* Save sectors/cylinder on the stack */
110         pushw   %dx             /* -4(%bp) High word */
111         pushw   %ax             /* -6(%bp) Low word */
112
113         /* Load partition table header */
114         xorl    %eax,%eax
115         xorl    %edx,%edx
116         incw    %ax
117         movw    $phdr, %bx
118         call    read_sector
119
120         /* Number of partition sectors */
121         /* We assume the partition table is 32K or less, and that
122            the sector size is 512. */
123         /* Note: phdr == 6(%bp) */
124         movw    (80+6)(%bp),%cx         /* NumberOfPartitionEntries */
125         movw    (84+6)(%bp),%ax         /* SizeOfPartitionEntry */
126         pushw   %ax
127         pushw   %cx
128         mulw    %cx
129         shrw    $9,%ax
130         xchgw   %ax,%cx
131         incw    %cx
132
133         /* Starting LBA of partition array */
134         movl    (72+6)(%bp),%eax
135         movl    (76+6)(%bp),%edx
136
137         pushw   %bx
138 get_ptab:
139         call    read_sector
140         call    inc64
141         loopw   get_ptab
142
143         /* Find the boot partition */
144         popw    %si                     /* Partition table in memory */
145         popw    %cx                     /* NumberOfPartitionEntries */
146         popw    %ax                     /* SizeOfPartitionEntry */
147 find_part:
148         pushw   %cx
149         pushw   %si
150         addw    $16,%si
151         movw    $bootguid,%di
152         movw    $8,%cx
153         repe; cmpsw
154         popw    %si
155         popw    %cx
156         je found_part
157         addw    %ax,%si
158         loopw   find_part
159
160         call    error
161         .ascii  "Boot partition not found\r\n"
162
163 found_part:
164         xchgw   %ax,%cx         /* Set up %cx for rep movsb further down */
165         
166         movw    $dssi_out,%di
167         pushw   %di
168         
169         /* 80 00 00 00 ee 00 00 00
170            - bootable partition, type EFI (EE), no CHS information */
171         xorl    %eax,%eax
172         movb    $0x80,%al
173         stosl
174         movb    $0xee,%al
175         stosl
176         movl    32(%si),%eax
177         movl    36(%si),%edx
178         call    saturate_stosl          /* Partition start */
179
180         movl    40(%si),%eax
181         movl    48(%si),%edx
182         subl    32(%si),%eax
183         sbbl    36(%si),%edx
184         call    inc64
185         call    saturate_stosl          /* Partition length */
186
187         rep; movsb                      /* GPT entry follows MBR entry */
188         popw    %si
189
190 /*
191  * boot: invoke the actual bootstrap. %ds:%si points to the
192  * partition information in memory.
193  */
194 boot:
195         movl    (32+16)(%si),%eax
196         movl    (36+16)(%si),%edx
197         movw    $bootsec,%bx
198         call    read_sector
199         cmpw    $0xaa55, -2(%bx)
200         jne     missing_os      /* Not a valid boot sector */
201         movw    %bp, %sp        /* driveno == bootsec-6 */
202         popw    %dx             /* dl -> drive number */
203         popw    %di             /* es:di -> $PnP vector */
204         popw    %es
205         cli
206         jmpw    *%sp            /* %sp == bootsec */
207
208 missing_os:
209         call    error
210         .ascii  "Operating system not bootable\r\n"
211
212 saturate_stosl:
213         pushl   %eax
214         andl    %edx,%edx
215         jz 1f
216         orl     $-1,%eax
217 1:      stosl
218         popl    %eax
219         ret
220
221 inc64:
222         addl    $1,%eax
223         adcl    $0,%edx
224         ret
225
226 /*
227  * read_sector: read a single sector pointed to by %edx:%eax to
228  * %es:%bx.  CF is set on error.  All registers saved.
229  */
230 read_sector:
231         pushal
232         pushl   %edx    /* MSW of LBA */
233         pushl   %eax    /* LSW of LBA */
234         pushw   %es     /* Buffer segment */
235         pushw   %bx     /* Buffer offset */
236         pushw   $1      /* Sector count */
237         pushw   $16     /* Size of packet */
238         movw    %sp, %si
239
240         /* This chunk is skipped if we have ebios */
241         /* Do not clobber %es:%bx or %edx:%eax before this chunk! */
242 read_sector_cbios:
243         divl    -6(%bp) /* secpercyl */
244         shlb    $6, %ah
245         movb    %ah, %cl
246         movb    %al, %ch
247         xchgw   %dx, %ax
248         divb    -2(%bp) /* sectors */
249         movb    %al, %dh
250         orb     %ah, %cl
251         incw    %cx     /* Sectors are 1-based */
252         movw    $0x0201, %ax
253
254 read_common:
255         movb    (%bp), %dl /* driveno */
256         int     $0x13
257         addw    $16, %sp        /* Drop DAPA */
258         popal
259         jc      disk_error
260         addb    $2, %bh         /* bx += 512: point to the next buffer */
261         ret
262
263 disk_error:
264         call    error
265         .ascii  "Disk error on boot\r\n"
266
267 /*
268  * Print error messages.  This is invoked with "call", with the
269  * error message at the return address.
270  */
271 error:
272         popw    %si
273 2:
274         lodsb
275         movb    $0x0e, %ah
276         movb    (BIOS_page), %bh
277         movb    $0x07, %bl
278         int     $0x10           /* May destroy %bp */
279         cmpb    $10, %al        /* Newline? */
280         jne     2b
281
282         int     $0x18           /* Boot failure */
283 die:
284         hlt
285         jmp     die