etc/protocols - sync with NetBSD-8
[minix.git] / sys / arch / i386 / stand / mbr / gpt.S
blob8dd53733ef6e63d8c34b17405dd5df64aac71562
1 /*      $NetBSD: gpt.S,v 1.1 2011/01/06 01:08:49 jakllsch Exp $ */
3 /*
4  * Copyright (c) 2009 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to the NetBSD Foundation
8  * by Mike M. Volokhov, based on the mbr.S code by Wolfgang Solfrank,
9  * Frank van der Linden, and David Laight.
10  * Development of this software was supported by the
11  * Google Summer of Code program.
12  * The GSoC project was mentored by Allen Briggs and Joerg Sonnenberger.
13  *
14  * Redistribution and use in source and binary forms, with or without
15  * modification, are permitted provided that the following conditions
16  * are met:
17  * 1. Redistributions of source code must retain the above copyright
18  *    notice, this list of conditions and the following disclaimer.
19  * 2. Redistributions in binary form must reproduce the above copyright
20  *    notice, this list of conditions and the following disclaimer in the
21  *    documentation and/or other materials provided with the distribution.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
37  * x86 PC BIOS master boot code - GUID partition table format
38  */
40 /* Compile options:
41  * COM_PORT     - do serial io to specified port number
42  *                0..3 => bios port, otherwise actual io_addr
43  * COM_BAUD     - initialise serial port baud rate
44  *
45  * NO_LBA_CHECK - no check if bios supports LBA reads
46  * NO_CRC_CHECK - disable crc checks for GPT
47  * NO_BANNER    - do not output title line 'banner'
48  */
50 #ifdef COM_PORT
51 #if COM_PORT < 4
52 /* The first 4 items in the 40:xx segment are the serial port base addresses */
53 #define COM_PORT_VAL (0x400 + (COM_PORT * 2))
54 #else
55 #define COM_PORT_VAL $COM_PORT
56 #endif
58 #if !defined(COM_FREQ)
59 #define COM_FREQ 1843200
60 #endif
61 #endif
63 #include <machine/asm.h>
64 #include <sys/bootblock.h>
66 #define BOOTADDR        0x7c00
67 #define GPTBUFADDR      0xa000          /* GPT buffer (both header & array) */
70  * GPT header structure, offsets
71  */
72 #define GPTHDR_SIG_O    0               /* header signature, 8 b */
73 #define GPTHDR_REV_O    8               /* GPT revision, 4 b */
74 #define GPTHDR_SIZE_O   12              /* header size, 4 b */
75 #define GPTHDR_CRC_O    16              /* header CRC32, 4 b */
76 #define GPTHDR_RSVD_O   20              /* reserved, 4 b */
77 #define GPTHDR_MYLBA_O  24              /* this header LBA, 8 b */
78 #define GPTHDR_ALTLBA_O 32              /* alternate header LBA, 8 b */
79 #define GPTHDR_SLBA_O   40              /* first usable LBA, 8 b */
80 #define GPTHDR_ELBA_O   48              /* last usable LBA, 8 b */
81 #define GPTHDR_GUID_O   56              /* disk GUID, 16 b */
82 #define GPTHDR_PTNLBA_O 72              /* ptn. entry LBA, 8 b */
83 #define GPTHDR_PTNN_O   80              /* number of ptns, 4 b */
84 #define GPTHDR_PTNSZ_O  84              /* partition size, 4 b */
85 #define GPTHDR_PTNCRC_O 88              /* ptn. array CRC32, 4 b */
88  * GPT partition entry structure, offsets
89  */
90 #define GPTPTN_TYPE_O   0               /* ptn. type GUID, 16 b */
91 #define GPTPTN_GUID_O   16              /* ptn. unique GUID, 16 b */
92 #define GPTPTN_SLBA_O   32              /* ptn. starting LBA, 8 b */
93 #define GPTPTN_ELBA_O   40              /* ptn. ending LBA, 8 b */
94 #define GPTPTN_ATTR_O   48              /* ptn. attributes, 8 b */
95 #define GPTPTN_NAME_O   56              /* ptn. name, UTF-16, 72 b */
98  * Default values of generic disk partitioning
99  */
100 #ifndef SECTOR_SIZE
101 #define SECTOR_SIZE             512             /* 8192 bytes max */
102 #endif
103 #define GPTHDR_BLKNO            1
104 #define GPTHDR_SIZE             92              /* size of GPT header */
105 #define GPTHDR_LBASZ            1               /* default LBAs for header */
106 #define GPTHDR_PTNSZ            128             /* size of a partition entry */
107 #define GPTHDR_PTNN_MIN         128             /* minimum number of ptns */
108 #define GPTPTN_SIZE             (GPTHDR_PTNSZ * GPTHDR_PTNN_MIN)
109 #if GPTPTN_SIZE % SECTOR_SIZE == 0
110 #define GPTPTN_LBASZ            (GPTPTN_SIZE / SECTOR_SIZE)
111 #else
112 #define GPTPTN_LBASZ            (GPTPTN_SIZE / SECTOR_SIZE + 1)
113 #endif
114 #define GPT_LBASZ               (GPTHDR_LBASZ + GPTPTN_LBASZ)
117  * Minimum and maximum drive number that is considered to be valid.
118  */
119 #define MINDRV          0x80
120 #define MAXDRV          0x8f
123  * Error codes. Done this way to save space.
124  */
125 #define ERR_INVPART     'P'             /* Invalid partition table */
126 #define ERR_READ        'R'             /* Read error */
127 #define ERR_NOOS        'S'             /* Magic no. check failed for part. */
128 #define ERR_NO_LBA      'L'             /* Sector above chs limit */
130 #define set_err(err)    movb    $err, %al
132         .text
133         .code16
135  * Move ourselves out of the way first.
136  * (to the address we are linked at)
137  * and zero our bss
138  */
139 ENTRY(start)
140         xor     %ax, %ax
141         mov     %ax, %ss
142         movw    $BOOTADDR, %sp
143         mov     %ax, %es
144         mov     %ax, %ds
145         movw    $mbr, %di
146         mov     $BOOTADDR + (mbr - start), %si
147         push    %ax                     /* zero for %cs of lret */
148         push    %di
149         movw    $(bss_start - mbr), %cx
150         rep
151         movsb                           /* relocate code (zero %cx on exit) */
152         mov     $(bss_end - bss_start + 511)/512, %ch
153         rep
154         stosw                           /* zero bss */
155         lret                            /* Ensures %cs == 0 */
158  * Sanity check the drive number passed by the BIOS. Some BIOSs may not
159  * do this and pass garbage.
160  */
161 mbr:
162         cmpb    $MAXDRV, %dl            /* relies on MINDRV being 0x80 */
163         jle     1f
164         movb    $MINDRV, %dl            /* garbage in, boot disk 0 */
166         push    %dx                     /* save drive number */
167         push    %dx                     /* twice - for err_msg loop */
169 #if defined(COM_PORT) && defined(COM_BAUD)
170         mov     $com_args, %si
171         mov     $num_com_args, %cl      /* %ch is zero from above */
172         mov     COM_PORT_VAL, %dx
173 1:      lodsw
174         add     %ah, %dl
175         outb    %dx
176         loop    1b
177 #endif
179 #ifndef NO_BANNER
180         mov     $banner, %si
181         call    message
182 #endif
185  * Read and validate GUID partition tables
187  * Register use:
188  * %ax          temp
189  * %bx          temp
190  * %cx          counters (%ch was preset to zero via %cx before)
191  * %dx          disk pointer (inherited from BIOS or the check above)
192  * %bp          GPT header pointer
193  */
194 #ifndef NO_LBA_CHECK
196  * Determine whether we have int13-extensions, by calling int 13, function 41.
197  * Check for the magic number returned, and the disk packet capability.
198  * On entry %dx should contain BIOS disk number.
199  */
200 lba_check:
201         movw    $0x55aa, %bx
202         movb    $0x41, %ah
203         int     $0x13
204         set_err(ERR_NO_LBA)
205         jc      1f                      /* no int13 extensions */
206         cmpw    $0xaa55, %bx
207         jnz     1f
208         testb   $1, %cl
209         jnz     gpt
210 1:      jmp     err_msg
211 #endif
213 gpt:
214         /*
215          * Read GPT header
216          */
217         movw    $GPTBUFADDR, %bp        /* start from primary GPT layout */
218 read_gpt:
219         call    do_lba_read             /* read GPT header */
220         jc      try_gpt2
222         /*
223          * Verify GPT header
224          */
225         movw    $efihdr_sign, %si       /* verify GPT signature... */
226         movw    %bp, %di                /* ptn signature is at offset 0 */
227         movb    $sizeof_efihdr_sign, %cl /* signature length */
228         repe
229         cmpsb                           /* compare */
230         jne     try_gpt2                /* if not equal - try GPT2 */
232 #ifndef NO_CRC_CHECK
233         mov     %bp, %si                /* verify CRC32 of the header... */
234         mov     $GPTHDR_SIZE, %di       /* header boundary */
235         add     %bp, %di
236         xor     %eax, %eax
237         xchgl   %eax, GPTHDR_CRC_O(%bp) /* save and reset header CRC */
238         call    crc32
239         cmp     %eax, %ebx              /* is CRC correct? */
240         jne     try_gpt2
241 #endif
243 #if 0   /* XXX: weak check - safely disabled due to space constraints */
244         movw    $lba_sector, %si        /* verify GPT location... */
245         mov     $GPTHDR_MYLBA_O, %di
246         add     %bp, %di
247         movb    $8, %cl                 /* LBA size */
248         repe
249         cmpsb                           /* compare */
250         jne     try_gpt2                /* if not equal - try GPT2 */
251 #endif
253         /*
254          * All header checks passed - now verify GPT partitions array
255          */
256 #ifndef NO_CRC_CHECK
257         movl    GPTHDR_PTNCRC_O(%bp), %eax      /* original array checksum */
258         push    %bp                     /* save header pointer for try_gpt2 */
259 #endif
261         /*
262          * point %bp to GPT partitions array location
263          */
264         cmp     $GPTBUFADDR, %bp
265         je      1f
266         mov     $GPTBUFADDR, %bp
267         jmp     2f
268 1:      mov     $GPTBUFADDR + GPTPTN_LBASZ * SECTOR_SIZE, %bp
271 #ifndef NO_CRC_CHECK
272         mov     %bp, %si                /* array location for CRC32 check */
273         mov     $GPTPTN_SIZE, %di       /* array boundary */
274         add     %bp, %di
275         call    crc32
276         cmp     %eax, %ebx              /* is CRC correct? */
277         jne     1f                      /* if no - try GPT2 */
278         pop     %ax                     /* restore stack consistency */
279 #endif
280         jmp     gpt_parse
282 #ifndef NO_CRC_CHECK
283 1:      pop     %bp                     /* restore after unsucc. array check */
284 #endif
285 try_gpt2:
286         cmp     $GPTBUFADDR, %bp        /* is this GPT1? */
287         set_err(ERR_INVPART)            
288         jne     err_msg                 /* if no - we just tried GPT2. Stop. */
290         mov     %bp, %si                /* use [%bp] as tmp buffer */
291         movb    $0x1a, (%si)            /* init buffer size (per v.1.0) */
292         movb    $0x48, %ah              /* request extended LBA status */
293         int     $0x13                   /* ... to get GPT2 location */
294         set_err(ERR_NO_LBA)
295         jc      err_msg                 /* on error - stop */
296 #define LBA_DKINFO_OFFSET 16            /* interested offset in out buffer */
297         addw    $LBA_DKINFO_OFFSET, %si /* ... contains number of disk LBAs */
298 #undef LBA_DKINFO_OFFSET
299         movw    $lba_sector, %di
300         movb    $8, %cl                 /* LBA size */
301         rep
302         movsb                           /* do get */
303         subl    $GPT_LBASZ, lba_sector  /* calculate location of GPT2 */
304         sbbl    $0, lba_sector + 4      /* 64-bit LBA correction */
306         movw    $GPTBUFADDR + GPTPTN_LBASZ * SECTOR_SIZE, %bp
307                                         /* the GPT2 header location */
308         jmp     read_gpt                /* try once again */
311  * GPT header validation done.
312  * Now parse GPT partitions and try to boot from appropriate.
313  * Register use:
314  * %bx          partition counter
315  * %bp          partition entry pointer (already initialized on entry)
316  * %di          partition entry moving pointer
317  * %si          variables pointer
318  * %cx          counter
319  */
321 gpt_parse:
322         movw    $BOOTADDR, lba_rbuff    /* from now we will read boot code */
323         movb    $1, lba_count           /* read PBR only */
324         mov     $GPTHDR_PTNN_MIN, %bx   /* number of GUID partitions to parse */
325 do_gpt_parse:
326         movw    $bootptn_guid, %si      /* lookup the boot partition GUID */
327         movb    $0x10, %cl              /* sizeof GUID */
328         movw    %bp, %di                /* set pointer to partition entry */
329         add     %cx, %di                /* partition GUID at offset 16 */
330         repe
331         cmpsb                           /* do compare */
332         jne     try_nextptn             /* doesn't seem appropriate ptn */
334         /*
335          * Read partition boot record
336          */
337         mov     $GPTPTN_SLBA_O, %si     /* point %si to partition LBA */
338         add     %bp, %si
339         movw    $lba_sector, %di
340         movb    $8, %cl                 /* LBA size */
341         rep
342         movsb                           /* set read pointer to LBA of PBR */
343         call    do_lba_read             /* read PBR */
344         jz      try_nextptn
346         /*
347          * Check signature for valid bootcode and try to boot
348          */
349         movb    BOOTADDR, %al           /* first byte non-zero */
350         testb   %al, %al
351         jz      1f
352         movw    BOOTADDR + MBR_MAGIC_OFFSET, %ax
353 1:      cmp     $MBR_MAGIC, %ax
354         jne     try_nextptn
355 do_boot:
356         pop     %dx                     /* ... %dx - drive # */
357         movw    $lba_sector, %sp        /* ... %ecx:%ebx - boot partition LBA */
358         pop     %ebx
359         pop     %ecx
360         movl    crc32_poly, %eax        /* X86_MBR_GPT_MAGIC */
361         jmp     BOOTADDR
362                                         /* THE END */
364 try_nextptn:
365         addw    $GPTHDR_PTNSZ, %bp      /* move to next partition */
366         dec     %bx                     /* ptncounter-- */
367         jnz     do_gpt_parse
368         set_err(ERR_NOOS)               /* no bootable partitions were found */
369         /* jmp  err_msg */              /* stop */
371 /* Something went wrong...
372  * Output error code,
373  * reset disk subsystem - needed after read failure,
374  * and wait for user key
375  */
376 err_msg:
377         movb    %al, errcod
378         movw    $errtxt, %si
379         call    message
380         pop     %dx                     /* drive we errored on */
381         xor     %ax,%ax                 /* only need %ah = 0 */
382         int     $0x13                   /* reset disk subsystem */
383         int     $0x18                   /* BIOS might ask for a key */
384                                         /* press and retry boot seq. */
385 1:      sti
386         hlt
387         jmp     1b
390  * I hate #including source files, but the stuff below has to be at
391  * the correct absolute address.
392  * Clearly this could be done with a linker script.
393  */
395 #if defined(COM_PORT) && defined(COM_BAUD)
396 message:
397         pusha
398 message_1:
399         lodsb
400         test    %al, %al
401         jz      3f
402         mov     COM_PORT_VAL, %dx
403         outb    %al, %dx
404         add     $5, %dl
405 2:      inb     %dx
406         test    $0x40, %al
407         jz      2b
408         jmp     message_1
409 3:      popa
410         ret
411 #else
412 #include <message.S>
413 #endif
415 #if 0
416 #include <dump_eax.S>
417 #endif
419 #ifndef NO_CRC_CHECK
421  * The CRC32 calculation
423  * %si          address of block to hash
424  * %di          stop address
425  * %ax          scratch (but restored after exit)
426  * %dx          scratch (but restored after exit)
427  * %cx          counter
428  * %ebx         crc (returned)
429  */
430 crc32:
431         push    %dx                     /* preserve drive number */
432         push    %ax                     /* preserve original CRC */
434         xorl    %ebx, %ebx
435         decl    %ebx                    /* init value */
437         lodsb                           /* load next message byte to %al */
438         movb    $8, %cl                 /* set bit counter */
440         movb    %al, %dl
441         xorb    %bl, %dl                /* xoring with previous result */
442         shrl    $1, %ebx
443         shrb    $1, %al
444         testb   $1, %dl
445         jz      3f
446 crc32_poly = . + 3                      /* gross, but saves a few bytes */
447         xorl    $0xedb88320, %ebx       /* EFI CRC32 Polynomial */
449         loop    2b                      /* loop over bits */
450         cmp     %di, %si                /* do we reached end of message? */
451         jne     1b
452         notl    %ebx                    /* result correction */
454         pop     %ax
455         pop     %dx
456         ret
457 #endif
459 do_lba_read:
460         movw    $lba_dap, %si
461         movb    $0x42, %ah
462         int     $0x13                   /* read */
463         ret
466  * Data definition block
467  */
469 errtxt: .ascii  "Error "                /* runs into crlf if errcod set */
470 errcod: .byte   0
471 crlf:   .asciz  "\r\n"
473 #ifndef NO_BANNER
474 banner: .asciz  "NetBSD GPT\r\n"
475 #endif
477 #if defined(COM_PORT) && defined(COM_BAUD)
478 #define COM_DIVISOR (((COM_FREQ / COM_BAUD) + 8) / 16)
479 com_args:
480         .byte   0x80                    /* divisor latch enable */
481         .byte   +3                      /* io_port + 3 */
482         .byte   COM_DIVISOR & 0xff
483         .byte   -3                      /* io_port */
484         .byte   COM_DIVISOR >> 8        /* high baud */
485         .byte   +1                      /* io_port + 1 */
486         .byte   0x03                    /* 8 bit no parity */
487         .byte   +2                      /* io_port + 3 */
488 num_com_args = (. - com_args)/2
489 #endif
492  * Control block for int-13 LBA read - Disk Address Packet
493  */
494 lba_dap:
495         .byte   0x10                    /* control block length */
496         .byte   0                       /* reserved */
497 lba_count:
498         .word   GPT_LBASZ               /* sector count */
499 lba_rbuff:
500         .word   GPTBUFADDR              /* offset in segment */
501         .word   0                       /* segment */
502 lba_sector:
503         .quad   GPTHDR_BLKNO            /* sector # goes here... */
505 efihdr_sign:
506         .ascii  "EFI PART"              /* GPT header signature */
507         .long   0x00010000              /* GPT header revision */
508 sizeof_efihdr_sign = . - efihdr_sign
511  * Stuff from here on is overwritten by gpt/fdisk - the offset must not change
513  * Get amount of space to makefile can report it.
514  * (Unfortunately I can't seem to get the value reported when it is -ve)
515  */
516 mbr_space = bootptn_guid - .
519  * GUID of the bootable partition. Patchable area.
520  * Default GUID used by installer for safety checks.
521  */
522         . = start + MBR_GPT_GUID_OFFSET
523 bootptn_guid:
524         /* MBR_GPT_GUID_DEFAULT */
525         .long 0xeee69d04
526         .word 0x02f4
527         .word 0x11e0
528         .byte 0x8f,0x5d
529         .byte 0x00,0xe0,0x81,0x52,0x9a,0x6b
531 /* space for mbr_dsn */
532         . = start + MBR_DSN_OFFSET
533         .long   0
535 /* mbr_bootsel_magic */
536         . = start + MBR_BS_MAGIC_OFFSET
537         .word   0
539 /* mbr partition table */
540         . = start + MBR_PART_OFFSET
541 parttab:
542         .fill   0x40, 0x01, 0x00
544         . = start + MBR_MAGIC_OFFSET
545         .word   MBR_MAGIC
547 /* zeroed data space */
548 bss_off = 0
549 bss_start = .
550 #define BSS(name, size) name = bss_start + bss_off; bss_off = bss_off + size
551         BSS(dump_eax_buff, 16)
552         BSS(bss_end, 0)