1/* -----------------------------------------------------------------------
2 *
3 *   Copyright 2007-2009 H. Peter Anvin - All Rights Reserved
4 *   Copyright 2009-2010 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#include <machine/asm.h>
30#include <sys/bootblock.h>
31
32#ifdef CTRL_80
33          .macro ADJUST_DRIVE
34          testb     $0x04, BIOS_kbdflags
35          jz        1f
36          movb      $0x80, %dl
371:
38          .endm
39#elif defined(FORCE_80)
40          .macro ADJUST_DRIVE
41          movb      $0x80, %dl
42          .endm
43#else
44          .macro ADJUST_DRIVE
45          .endm
46#endif
47
48          .code16
49          .text
50
51          .globl    bootsec
52stack               = 0x7c00
53
54/* Partition table header here */
55phdr                = stack             /* Above the stack, overwritten by bootsect */
56/* Partition table sector here */
57/* To handle > 32K we need to play segment tricks... */
58psec                = _phdr + 512
59
60/* Where we put DS:SI */
61dssi_out  = start + 0x1be
62
63BIOS_kbdflags       = 0x417
64BIOS_page = 0x462
65
66          /* gas/ld has issues with doing this as absolute addresses... */
67          .section ".bootsec", "a", @nobits
68          .globl    bootsec
69bootsec:
70          .space    512
71
72          .text
73          .globl    start
74start:
75          cli
76          xorw      %ax, %ax
77          movw      %ax, %ds
78          movw      %ax, %ss
79          movw      $stack, %sp
80          movw      %sp, %si
81          pushw     %es                 /* 4(%bp) es:di -> $PnP header */
82          pushw     %di                 /* 2(%bp) */
83          movw      %ax, %es
84          sti
85          cld
86
87          /* Copy down to 0:0x600 */
88          movw      $start, %di
89          movw      $(512/2), %cx
90          rep; movsw
91
92          ljmpw     $0, $next
93next:
94
95          ADJUST_DRIVE
96          pushw     %dx                 /* 0(%bp) = %dl -> drive number */
97
98          /* Check to see if we have EBIOS */
99          pushw     %dx                 /* drive number */
100          movb      $0x41, %ah          /* %al == 0 already */
101          movw      $0x55aa, %bx
102          xorw      %cx, %cx
103          xorb      %dh, %dh
104          stc
105          int       $0x13
106          jc        1f
107          cmpw      $0xaa55, %bx
108          jne       1f
109          shrw      %cx                 /* Bit 0 = fixed disk subset */
110          jnc       1f
111
112          /* We have EBIOS; patch in the following code at
113             read_sector_cbios: movb $0x42, %ah ;  jmp read_common */
114          movl      $0xeb42b4+((read_common-read_sector_cbios-4) << 24), \
115                    (read_sector_cbios)
116
1171:
118          popw      %dx
119
120          /* Get (C)HS geometry */
121          movb      $0x08, %ah
122          int       $0x13
123          andw      $0x3f, %cx          /* Sector count */
124          movw      %sp, %bp  /* %bp -> frame pointer: LEAVE UNCHANGED */
125          pushw     %cx                 /* -2(%bp) Save sectors on the stack */
126          movzbw    %dh, %ax  /* dh = max head */
127          incw      %ax                 /* From 0-based max to count */
128          mulw      %cx                 /* Heads*sectors -> sectors per cylinder */
129
130          /* Save sectors/cylinder on the stack */
131          pushw     %dx                 /* -4(%bp) High word */
132          pushw     %ax                 /* -6(%bp) Low word */
133
134          /* Load partition table header */
135          xorl      %eax,%eax
136          cltd
137          incw      %ax                 /* %edx:%eax = 1 */
138          movw      $phdr, %bx
139          pushw     %bx                 /* -8(%bp) phdr == bootsect */
140          call      read_sector
141
142          /* Number of partition sectors */
143          /* We assume the partition table is 32K or less, and that
144             the sector size is 512. */
145          /* Note: phdr == 6(%bp) */
146          movw      (80+6)(%bp),%cx               /* NumberOfPartitionEntries */
147          movw      (84+6)(%bp),%ax               /* SizeOfPartitionEntry */
148          pushw     %ax
149          pushw     %cx
150          mulw      %cx
151          shrw      $9,%ax
152          xchgw     %ax,%cx
153          incw      %cx
154
155          /* Starting LBA of partition array */
156          movl      (72+6)(%bp),%eax
157          movl      (76+6)(%bp),%edx
158
159          pushw     %bx
160get_ptab:
161          call      read_sector
162          call      inc64
163          loop      get_ptab
164
165          /* Find the boot partition */
166          xorw      %si,%si                       /* Nothing found yet */
167          popw      %di                           /* Partition table in memory */
168          popw      %cx                           /* NumberOfPartitionEntries */
169          popw      %ax                           /* SizeOfPartitionEntry */
170
171find_part:
172          /* If the PartitionTypeGUID is all zero, it's an empty slot */
173          movl        (%di),%edx
174          orl        4(%di),%edx
175          orl        8(%di),%edx
176          orl       12(%di),%edx
177          jz        not_this
178          testb     $0x04,48(%di)
179          jz        not_this
180          andw      %si,%si
181          jnz       found_multiple
182          movw      %di,%si
183not_this:
184          addw      %ax,%di
185          loop      find_part
186
187          andw      %si,%si
188          jnz       found_part
189
190missing_os:
191          call      error
192          .ascii    "Missing OS\r\n"
193
194found_multiple:
195          call      error
196          .ascii    "Multiple active partitions\r\n"
197
198found_part:
199          xchgw     %ax,%cx             /* Set up %cx for rep movsb further down */
200
201          movw      $dssi_out,%di
202          pushw     %di
203
204          /* 80 00 00 00 ee 00 00 00
205             - bootable partition, type EFI (EE), no CHS information */
206          xorl      %eax,%eax
207          movb      $0x80,%al
208          stosl
209          movb      $0xed,%al
210          stosl
211          movl      32(%si),%eax
212          movl      36(%si),%edx
213          call      saturate_stosl                /* Partition start */
214
215          movl      40(%si),%eax
216          movl      44(%si),%edx
217          subl      32(%si),%eax
218          sbbl      36(%si),%edx
219          call      inc64
220          call      saturate_stosl                /* Partition length */
221
222          movzwl    %cx,%eax            /* Length of GPT entry */
223          stosl
224
225          rep; movsb                              /* GPT entry follows MBR entry */
226          popw      %si
227
228/*
229 * boot: invoke the actual bootstrap. %ds:%si points to the
230 * partition information in memory.  The top word on the stack
231 * is phdr == 0x7c00 == the address of the boot sector.
232 */
233boot:
234          movl      (32+20)(%si),%eax
235          movl      (36+20)(%si),%edx
236          popw      %bx
237          call      read_sector
238          cmpw      $0xaa55, -2(%bx)
239          jne       missing_os          /* Not a valid boot sector */
240          movw      %bp, %sp  /* driveno == bootsec-6 */
241          popw      %dx                 /* dl -> drive number */
242          popw      %di                 /* es:di -> $PnP vector */
243          popw      %es
244          movl      $0x54504721,%eax /* !GPT magic number */
245          cli
246          jmpw      *%sp                /* %sp == bootsec */
247
248/*
249 * Store the value in %eax to %di iff %edx == 0, otherwise store -1.
250 * Returns the value that was actually written in %eax.
251 */
252saturate_stosl:
253          andl      %edx,%edx
254          jz 1f
255          orl       $-1,%eax
2561:        stosl
257          ret
258
259/*
260 * Increment %edx:%eax
261 */
262inc64:
263          addl      $1,%eax
264          adcl      $0,%edx
265          ret
266
267/*
268 * read_sector: read a single sector pointed to by %edx:%eax to
269 * %es:%bx.  CF is set on error.  All registers saved.
270 */
271read_sector:
272          pushal
273          pushl     %edx      /* MSW of LBA */
274          pushl     %eax      /* LSW of LBA */
275          pushw     %es       /* Buffer segment */
276          pushw     %bx       /* Buffer offset */
277          pushw     $1        /* Sector count */
278          pushw     $16       /* Size of packet */
279          movw      %sp, %si
280
281          /* This chunk is skipped if we have ebios */
282          /* Do not clobber %es:%bx or %edx:%eax before this chunk! */
283read_sector_cbios:
284          divl      -6(%bp)   /* secpercyl */
285          shlb      $6, %ah
286          movb      %ah, %cl
287          movb      %al, %ch
288          xchgw     %dx, %ax
289          divb      -2(%bp)   /* sectors */
290          movb      %al, %dh
291          orb       %ah, %cl
292          incw      %cx       /* Sectors are 1-based */
293          movw      $0x0201, %ax
294
295read_common:
296          movb      (%bp), %dl /* driveno */
297          int       $0x13
298          leaw      16(%si), %sp        /* Drop DAPA */
299          popal
300          jc        disk_error
301          addb      $2, %bh             /* bx += 512: point to the next buffer */
302          ret
303
304disk_error:
305          call      error
306          .ascii    "Disk error on boot\r\n"
307
308/*
309 * Print error messages.  This is invoked with "call", with the
310 * error message at the return address.
311 */
312error:
313          popw      %si
3142:
315          lodsb
316          movb      $0x0e, %ah
317          movb      (BIOS_page), %bh
318          movb      $0x07, %bl
319          int       $0x10               /* May destroy %bp */
320          cmpb      $10, %al  /* Newline? */
321          jne       2b
322
323          int       $0x18               /* Boot failure */
324die:
325          hlt
326          jmp       die
327
328mbr_space = end - .
329          . = MBR_DSN_OFFSET
330end:
331