xref: /netbsd-src/sys/arch/i386/stand/mbr/gptmbr.S (revision f71f5c795aa1eee82fed129e76fdaa8f2691f79f)
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