[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Booting above 1024...



Dixitur illum slash@peereboom.us scribere...

>> run OpenBSD).  Now, the OpenBSD partition on this disk is the 4th primary
>> partition, and / sits above 1024.  I didn't have any issues chainloading
>> NetBSD with grub, but OpenBSD just gives the ever popular "Bad magic..."

>I don't recall who made it but someone had a "working" patch for this. Search 
>the archives for details

The two below are LBA-capable "mbr" and "biosboot" with appropiate
Makefiles (hand-edited right now, 15 minutes after awaking, so don't
blame me please).
They need a fairly current ELF system with the fixes to /boot by
Tobias Weingartner already applied. (ELF is necessary due to the
usage of the GNU as intel_syntax feature)

Also integrated is an updated "installboot.c" file, which has been
adapted to the changed structure of the tables within biosboot.

#----- cutting here may damage your screen surface -----#
# This is a shell archive.  Save it in a file, remove anything before
# this line, and then unpack it by entering "sh file".  Note, it may
# create directories; files and directories will be owned by you and
# have default permissions.
#
# This archive contains:
#
#	mbr.S
#	Makefile.mbr
#	biosboot.S
#	Makefile.biosboot
#	installboot.c
#
echo x - mbr.S
sed 's/^X//' >mbr.S << 'END-of-mbr.S'
X/* $MirBSD: mbr.S,v 1.5 2003/07/08 14:40:04 tg Exp $
X *-
X * Copyright (c) 2003 Thorsten Glaser <x86@ePost.de>
X *
X * Subject to these terms, everybody who obtained a copy of this work
X * is hereby permitted to deal in the work without restriction inclu-
X * ding without limitation the rights to use, distribute, sell, modi-
X * fy, publically perform, give away, merge or sublicense it provided
X * this notice is kept and the authors and contributors are given due
X * credit in derivates or accompanying documents.
X * This work is provided "as is" with no explicit or implicit warran-
X * ties whatsoever to the maximum extend permitted by applicable law;
X * in no event may an author or contributor be held liable for damage
X * that is, directly or indirectly, caused by the work, even if advi-
X * sed of the possibility of such damage.
X *-
X * Fully LBA and CHS capable Master Boot Record with enough space for
X * the partition table and the Microsoft NT® disc ID; including boot-
X * manager capable of booting the four primary partitions or from the
X * first floppy device, with a 10 second time-out going off on a key.
X */
X
X	.file	"mbr.S"
X	.intel_syntax noprefix
X
X	.text
X	.code16
X	.globl	_start
X_start:	xor	eax,eax
X	push	eax		/* assume a sane stack of 4 bytes */
X	popfd			/* clear eflags and interrupts */
X	mov	ax,0x07A0
X	mov	ss,ax		/* set up stack and other segments */
X	mov	sp,0x1000
X	mov	ds,ax
X	mov	es,ax
X	mov	si,0x0200	/* 7A0:0200 = 0:7C00 */
X	xor	di,di		/* 7A0:0000 = 0:7A00 */
X	mov	cx,0x0100
X	rep	movsw		/* relocate ourselfes */
X	jmp	0x07A0,offset Ldoit
X	/* we are relocated now */
XLdoit:	/*
X	 * a) Output "Hello" Text
X	 * b) if BOOTMANAGER
X	 *	Output "Choose" Text
X	 *	Handle input (0,1,2,3,RET,ESC)
X	 *    else
X	 *	Fake input RET
X	 *    endif
X	 * c) load sector (RET=0x80-Partition, ESC=Floppy, 0-3=Partition)
X	 * d) jump to sector
X	 */
X	mov	si,offset LtHelo
X	call	Lotxt
X#ifdef	BOOTMANAGER
X	xor	ax,ax		/* read CMOS clock ticks since midnight */
X	int	0x1A
X	mov	edi,183		/* 10 seconds plus one tick */
X	add	di,dx
X	adc	cl,0		/* carry over */
X	shl	ecx,16
X	add	edi,ecx
XLiloop:	mov	ah,1		/* check if key pressed */
X	int	0x16
X	jnz	Lkeyp		/* key was pressed */
X	xor	ax,ax
X	int	0x1A
X	shl	ecx,16
X	mov	cx,dx		/* current clock tick */
X	or	al,al		/* past midnight? */
X	jz	Lnwrap
X	add	ecx,1573040	/* according to Ralf Brown Interrupt List */
XLnwrap:	cmp	ecx,edi		/* time is over? */
X	jbe	Liloop		/* not yet */
X	mov	al,13		/* assume user pressed RETURN (default) */
X	jmp	Lchose
XLkeyp:	xor	ah,ah		/* get pressed key */
X	int	0x16
X	cmp	al,13		/* RETURN? */
X	je	Lchose
X	cmp	al,27		/* ESCAPE? */
X	je	Lchose
X	sub	al,0x30
X	cmp	al,3		/* '0' .. '3' ? */
X	ja	Lkeyp		/* if invalid key, stop 10-second timeout */
XLchose:	/* al=13,27,0,1,2,3 */
X#else
X	mov	al,13		/* fake a RETURN keypress */
X#endif
X	cmp	al,27
X	je	Lflopp		/* ESC -> floppy */
X	cmp	al,13
X	jne	Lpart		/* 0..3 -> partition chosen */
X	xor	al,al
X	mov	bx,offset ptbl
XLcklp:	cmp	byte ptr [bx],0x80
X	je	Lpart		/* loop over partition table, search */
X	inc	ax		/*              for active partition */
X	add	bl,0x10		/* sizeof ptbl-ent */
X	cmp	al,4
X	jb	Lcklp		/* if none found, fail */
XLbootf:	mov	si,offset LtFail
X	call	Lotxt
X	xor	ah,ah		/* wait for keypress */
X	int	0x16
X	jmp	0xF000,0xFFF0	/* reboot */
XLotxt:	lodsb
X	or	al,al		/* '\0' is end of string */
X	je	Ltxout
X	mov	ah,0x0E		/* tty a character */
X	mov	bx,7		/* light grey on black; page 0 */
X	int	0x10
X	jmp	Lotxt
XLtxout:	ret
XLpart:	mov	bx,offset ptbl
X	shl	al,4		/* log_2 sizeof ptbl-ent */
X	add	bl,al		/* can't overflow */
X	push	bx
X	/* Test for LBA support */
X	mov	ah,0x41
X	mov	bx,0x55AA
X	mov	dl,0x80
X	int	0x13
X	jc	L_chs		/* carry -> error */
X	cmp	bx,0xAA55
X	jne	L_chs		/* magic -> command known? */
X	and	cl,1
X	je	L_chs		/* bitmask: supports LBA? */
XL_lba:	pop	bx
X	/* Load the boot structure (LBA) */
X	mov	si,offset lbapbl
X	mov	eax,[bx+8]	/* offset on disc */
X	mov	[blknum],eax
X	mov	ax,[bx+2]	/* save CHS values, in case */
X	mov	[savecx],ax	/* the LBA boot fails twice */
X	mov	dx,[bx]
X	mov	dl,0x80
X	mov	[savedx],dx
X	mov	ah,0x42
X	jmp	Lboot_loop
XL_chs:	pop	bx
X	/* Load the boot structure (CHS) */
X	mov	cx,[bx+2]
X	mov	dx,[bx]
X	mov	dl,0x80
X	jmp	Lboot_chs
XLflopp:	mov	cx,1
X	xor	dx,dx
XLboot_chs:
X	mov	bx,offset bsec
X	mov	ax,0x0201
X	jmp	Lboot_loop
XLtHelo:	.ascii	"MirBSD mbr 1.03\r\n"
X#ifdef	BOOTMANAGER
XLtBmgr:	.ascii	"Press 0..3 to boot partition, ESC floppy\r\n"
X	.asciz	"RETURN/wait 10 sec: default\r\n"
X#else
X	.byte	0		/* terminate the tHelo string */
X#endif
XLtFail:	.ascii	"Failed!"
X	/* fall through */
XLtCRLF:	.asciz	"\r\n"
XLboot_loop:
X	xor	bp,bp
XLblp1:	pusha
X	mov	ax,0x0E2E	/* print a dot */
X	mov	bx,7
X	int	0x10
X	popa
X	pusha
X	int	0x13		/* try to read the PBR/bootsector */
X	popa
X	jc	Lblp2
X	mov	si,offset LtCRLF
X	call	Lotxt		/* print '\n' and jump into PBR */
X	jmp	0,0x7C00	/* offset flat bsec */
XLblp2:	pusha
X	xor	ax,ax		/* reset the drive */
X	int	0x13
X	popa
X	inc	bp
X	cmp	bp,2		/* second time it failed? */
X	jne	Lblp3
X	xor	di,di
X	cmp	[savedx],di	/* if so, check if we were LBA */
X	je	Lblp3
X	mov	cx,[savecx]	/* if so, fall back to CHS */
X	mov	dx,[savedx]
X	mov	bx,offset bsec
X	mov	ax,0x0201
XLblp3:	cmp	bp,16		/* 16 retries should be enough for everyone :) */
X	jb	Lblp1
X	jmp	Lbootf		/* failed */
Xlbapbl:	/* Parameter block for LBA */
X	.word	0x0010		/* length, reserved */
X	.word	0x0001		/* num. of sectors */
X	.long	0x00007C00	/* transfer buffer */
Xblknum:	.long	0,0		/* LBA block number */
Xsavecx:	.word	0
Xsavedx:	.word	0
X
X	/* Trailer: partition table, signature */
X#if 0
X	. = 0x01B8
X	.long	0		/* device ID/magic */
X	.word	0		/* free/reserved */
X#else
X	/* In fact, we have 6 bytes of 0x00 before
X	 * (on the disc), and ignore/overwrite what
X	 * Microsoft NT writes here (on the disc),
X	 * so we can safely skip till here:
X	 */
X	. = 0x01BE
X#endif
Xptbl:	.long	0,0,0,0
X	.long	0,0,0,0
X	.long	0,0,0,0
X	.long	0,0,0,0
X	.word	0xAA55		/* signature */
Xbsec:	/* 0x0200 is where the new boot sector is loaded */
END-of-mbr.S
echo x - Makefile.mbr
sed 's/^X//' >Makefile.mbr << 'END-of-Makefile.mbr'
X# $MirBSD: Makefile,v 1.1 2003/05/23 14:21:48 tg Exp $
X
XPROG=	mbr
XSRCS=	mbr.S
X
XNOMAN=	yes
XINSTALL_STRIP=
XLDFLAGS= -nostdlib -Ttext 0 -N -x -Bstatic
X
X# Uncomment this to build a boot manager
XCPPFLAGS+= -DBOOTMANAGER
X
X${PROG}: $(OBJS) $(DPADD)
X	$(LD) $(LDFLAGS) -o $(PROG) $(OBJS) $(LDADD)
X	@size $(PROG)
X	@if [ -x ${.OBJDIR}/${PROG} ]; then \
X		objcopy -O binary ${PROG} ${.OBJDIR}/.tmp; \
X		mv -f ${.OBJDIR}/.tmp ${.OBJDIR}/${PROG}; \
X		ls -l ${.OBJDIR}/${PROG}; \
X	fi
X
Xdisasm: ${PROG}
X	ndisasm -b 16 ${PROG} >${.CURDIR}/disasm
X
X.include <bsd.prog.mk>
END-of-Makefile.mbr
echo x - biosboot.S
sed 's/^X//' >biosboot.S << 'END-of-biosboot.S'
X/* $MirBSD: pbr.S,v 1.20 2003/07/06 20:22:28 tg Exp $
X *-
X * Copyright (c) 2003 Thorsten Glaser <x86@ePost.de>
X * Derived from ideas and meant to be compatible to the
X *  original OpenBSD first stage boot loader code from:
X * Copyright (c) 1997 Michael Shalayeff, Tobias Weingartner
X *
X * Subject to these terms, everybody who obtained a copy of this work
X * is hereby permitted to deal in the work without restriction inclu-
X * ding without limitation the rights to use, distribute, sell, modi-
X * fy, give away, merge or sublicense it provided this notice is kept
X * and the authors and contributors are given due credit in derivates
X * or accompanying documents.
X * This work is provided "as is" with no explicit or implicit warran-
X * ties whatsoever to the maximum extend permitted by applicable law;
X * in no event may an author or contributor be held liable for damage
X * that is, directly or indirectly, caused by the work, even if advi-
X * sed of the possibility of such damage.
X */
X
X#define	BLKCNT	(blkend - blktbl)
X
X	.file	"pbr.S"
X	.intel_syntax noprefix
X
X	.text
X	.code16
X	.globl	_start
XLpbl:	/* Parameter block for LBA, gets constructed here:
X	word	0x0010		(+0,2) length, reserved
X	word	0x0001		(+2,2) num. of sectors
X	long	0x00007C00	(+4,4) transfer buffer
X	long	0,0		(+8,8) LBA block number
X	 */
X_start:	jmp	Linit
X	nop
X	. = _start + 3
XLmsg:	.ascii	"Loading "
X	.word	512		/* bytes per sector */
X	.byte	1		/* sectors per cluster */
X	.word	1		/* reserved sectors */
X	.byte	0		/* FATs per volume */
XLldp:	.word	LsLBA		/* root dir ents */
X	.word	0		/* sectors per drive (FAT16s) */
X	.byte	0xF8		/* media ID */
X	.globl	blkcnt		/** block count in hint table */
X	.type	blkcnt, @function
X	.globl	bpbspt		/** used to convert LBA -> CHS */
X	.type	bpbspt, @function
X	.globl	bpbtpc		/** used to convert LBA -> CHS */
X	.type	bpbtpc, @function
Xblkcnt:	.word	BLKCNT		/* sectors per FAT */
Xbpbspt:	.word	18		/* sectors per track */
Xbpbtpc:	.word	2		/* tracks per cylinder */
X	.long	16		/* hidden sectors */
XLbmgc:	.long	BOOTMAGIC	/* sectors per drive (FAT16B) */
XLdrv:	.byte	0x80, 0		/* BIOS drive */
X	.byte	0x29		/* magic (EBPB) */
X/* the following code is 0x0F bytes long:
X	.long	0		// volume serial number
X	.asciz	"UNIX LABEL"	// volume label
X */
XLotxt:	lodsb
X	or	al,al
X	je	Lotxt1
X	mov	ah,0x0E
X	mov	bx,7
X	int	0x10
X	jmp	Lotxt
XLotxt1:	ret
X	. = 15 + Lotxt		/* ensure it fits exactly */
X	.asciz	"UFS 4.4"	/* volume fstype */
X
XLinit:	xor	eax,eax
X	push	ax
X	popf
X	mov	ss,ax
X	mov	sp,0xFFFC
X	sti
X	add	ax,0x7C0	/* not MOV - eases debugging... */
X	mov	ds,ax
X	mov	gs,ax
X	push	ax
X	push	offset Lrun
X	lret			/* should read retf */
X
XLerr:	.ascii	"bad magic."
XLldf:	.ascii	" FAILED!"
XLcrlf:	.asciz	"\r\n"
X
XLrun:	mov	[Ldrv],dl	/* boot device */
X	/* say hello */
X	mov	si,offset Lmsg
X	call	Lotxt
X	/* check for LBA */
X	mov	ah,0x41
X	mov	bx,0x55AA
X	int	0x13
X	jc	Lr_chs		/* carry -> error */
X	cmp	bx,0xAA55
X	jne	Lr_chs		/* magic -> command known? */
X	and	cl,1
X	jne	Lr_lba		/* bitmask: supports LBA? */
XLr_chs:	call	Lpaddr
XLr_lba:	/* start the first stage boot loader code */
X	mov	ax,(LOADADDR / 16)
X	mov	fs,ax		/* for ELFmagic check */
X	push	ax		/* for load address and incrementing */
X	mov	ax,0x0010
X	mov	[Lpbl],eax	/* high 16 bits are already cleared */
X	/* initialize counters */
X	mov	si,offset blktbl
X	mov	cx,[blkcnt]
X	/* --- main loop --- */
XLmlp1:	lodsb
X	/*
X	 * al contains bitmasked:
X	 * aaabbbbb
X	 * -> a=number of bytes-1 that follow	(-> dl)
X	 * -> b=number of sectors-1 to load	(-> al)
X	 * si, cx contain what is to be processed
X	 */
X
X	/* decode */
X	mov	dl,al
X	shr	dl,5
X	and	ax,31		/* sectors */
X	inc	ax
X	inc	dx
X	mov	[2+Lpbl],al
X	pop	bx
X	mov	es,bx		/* load segment */
X	mov	[6+Lpbl],bx
X	shl	ax,(9-4)	/* number of sectors
X				   times their size
X				   divided by size of one paragraph */
X	add	bx,ax		/* increment load addr. segment accordingly */
X	push	bx		/* and save for next round */
X	push	cx		/* save blkcnt */
X	mov	di,8+offset Lpbl
X	xor	ebp,ebp
X	mov	[di],ebp	/* zero out the LBA sector number */
X	mov	[di+4],ebp
X	mov	[di-4],bp	/* load offset within segment, always zero */
XLmlp2:	lodsb			/* copy over as many bytes as needed */
X	mov	[di],al
X	inc	di		/* stos? segment override forbidden */
X	dec	dl
X	jne	Lmlp2		/* faster than loop */
X	/*
X	 * at this point, we have the following:
X	 * si points to the _next_ entry to load
X	 * on top of stack, there is the number of
X	 *    entries to load, including the current one
X	 * the _next_ load address is below that on the stack
X	 * the LBA parameter block is filled in.
X	 * ES is set up to the load segment (offset = const. 0000)
X	 */
X
X	/* call subroutine that does the actual loading */
X	mov	dl,[Ldrv]
X	call	[Lldp]
X	/* progress meter */
X	mov	ax,0x0E2E
X	mov	bx,7
X	int	0x10		/* output a dot */
X	/* --- main loop --- */
X	pop	cx
X	dec	cx
X	jne	Lmlp1
X
X	/* finally /boot is loaded. compare ELFmagic */
X	mov	si,2
X	fs lodsw
X	cmp	ax,0x464C	/* last two bytes of "\x7FELF" */
X	mov	si,offset Lerr
X	jne	Lbarf
X	/* new line, for the fine look */
X	mov	si,offset Lcrlf
X	call	Lotxt
X	/* set up and jump into /boot */
X	mov	bp,[Ldrv]	/* the byte after Ldrv is always zero */
X	push	ebp		/* as well as the high half of ebp */
X	push	dword ptr [Lbmgc] /* boot magic */
X	jmp	(LINKADDR / 16),0
X	/* CHS boot routine */
XLsCHS:	push	dx
X	/* LBA -> CHS */
X	mov	edx,[8+Lpbl]
X	mov	ax,dx
X	shr	edx,16
X	div	word ptr [bpbspt]
X	inc	dx		/* quotient AX, rem DX */
X	push	dx		/* sector */
X	xor	dx,dx
X	div	word ptr [bpbtpc]
X	pop	cx		/* cylinder AX, head DX */
X	shl	ah,6
X	xchg	ah,al
X	or	cx,ax
X	mov	dh,dl
X	pop	ax
X	mov	dl,al
X	/* call the BIOS */
X	mov	ah,2
X	mov	al,[2+Lpbl]
X	xor	bx,bx
XLsINT:	mov	bp,7		/* retries */
XLsIN0:	pusha
X	int	0x13
X	popa
X	jnc	Lretn
X	pusha
X	xor	ax,ax
X	int	0x13
X	mov	ax,0x0E40	/* say it fails */
X	add	ax,bp
X	mov	bx,7
X	int	0x10
X	popa
X	mov	si,offset Lldf
X	dec	bp
X	jne	Lnchk
XLbarf:	call	Lotxt
X	xor	ah,ah
X	int	0x16
X	jmp	0xF000,0xFFF0
XLnchk:	cmp	bp,3		/* after some failed LBA, try CHS */
X	push	offset LsIN0
X	jna	Lretn
XLpaddr:	push	offset LsCHS
X	pop	[Lldp]		/* patch address of loader proc */
XLretn:	ret
XLsLBA:	push	si		/* save blktbl offset */
X	mov	ah,0x42		/* call BIOS */
X	mov	si,offset Lpbl
X	call	LsINT
X	pop	si
X	ret
X	/* --- end of code --- */
X
X	.globl	blktbl		/** hint table start */
X	.type	blktbl, @function
Xblktbl:
X	. = 0x1FE
Xblkend:	.word	0xAA55
END-of-biosboot.S
echo x - Makefile.biosboot
sed 's/^X//' >Makefile.biosboot << 'END-of-Makefile.biosboot'
X# from $MirBSD: Makefile,v 1.7 2003/06/05 20:29:24 tg Exp $
X
XSADIR=  ${.CURDIR}/..
X.include <../Makefile.inc>
X
XPROG=	biosboot
XSRCS=	biosboot.S
X
XNOMAN=	yes
XINSTALL_STRIP=
XLDFLAGS= -nostdlib -Ttext 0 -N -x -Bstatic
XCPPFLAGS+= -DLOADADDR=$(LOADADDR) -DLINKADDR=$(LINKADDR) \
X	-DBOOTMAGIC=$(BOOTMAGIC) ${DEBUGFLAGS}
X
X${PROG}: $(OBJS) $(DPADD)
X	$(LD) $(LDFLAGS) -o $(PROG) $(OBJS) $(LDADD)
X	@size $(PROG)
X
Xdisasm: ${PROG}
X	objcopy -O binary ${PROG} ${.OBJDIR}/.tmp; \
X	ndisasm -b 16 ${.OBJDIR}/.tmp >${.CURDIR}/disasm
XCLEANFILES+= .tmp
X
X.include <bsd.prog.mk>
END-of-Makefile.biosboot
echo x - installboot.c
sed 's/^X//' >installboot.c << 'END-of-installboot.c'
X/*	$MirBSD: installboot.c,v 1.23 2003/08/31 20:54:51 tg Exp $	*/
X/*	$OpenBSD: installboot.c,v 1.41 2003/08/25 23:27:43 tedu Exp $	*/
X/*	$NetBSD: installboot.c,v 1.5 1995/11/17 23:23:50 gwr Exp $ */
X
X/*
X * Copyright (c) 2003 Thorsten Glaser
X * Copyright (c) 1997 Michael Shalayeff
X * Copyright (c) 1994 Paul Kranenburg
X * All rights reserved.
X *
X * Redistribution and use in source and binary forms, with or without
X * modification, are permitted provided that the following conditions
X * are met:
X * 1. Redistributions of source code must retain the above copyright
X *    notice, this list of conditions and the following disclaimer.
X * 2. Redistributions in binary form must reproduce the above copyright
X *    notice, this list of conditions and the following disclaimer in the
X *    documentation and/or other materials provided with the distribution.
X * 3. All advertising materials mentioning features or use of this software
X *    must display the following acknowledgement:
X *      This product includes software developed by Paul Kranenburg.
X * 4. The name of the author may not be used to endorse or promote products
X *    derived from this software without specific prior written permission
X *
X * THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR
X * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
X * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
X * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
X * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
X * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
X * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
X * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
X * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
X * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
X */
X
X#include <sys/param.h>
X#include <sys/mount.h>
X#include <sys/time.h>
X#include <sys/stat.h>
X#include <sys/disklabel.h>
X#include <sys/dkio.h>
X#include <sys/ioctl.h>
X#include <ufs/ufs/dinode.h>
X#include <ufs/ufs/dir.h>
X#include <ufs/ffs/fs.h>
X#include <sys/reboot.h>
X#include <sys/errno.h>
X
X#include <uvm/uvm_extern.h>
X#include <sys/sysctl.h>
X
X#include <machine/cpu.h>
X#include <machine/biosvar.h>
X
X#include <err.h>
X#include <a.out.h>
X#include <sys/exec_elf.h>
X#include <fcntl.h>
X#include <nlist.h>
X#include <stdlib.h>
X#include <stdio.h>
X#include <string.h>
X#include <unistd.h>
X#include <util.h>
X
Xextern	char *__progname;
Xint	verbose, nowrite, nheads, nsectors, userspec = 0;
Xchar	*boot, *proto, *dev, *realdev;
Xstruct nlist nl[] = {
X#define X_BLOCK_COUNT	0
X	{{"_blkcnt"}},
X#define X_BLOCK_TABLE	1
X	{{"_blktbl"}},
X#define	X_NUM_SECS	2
X	{{"_bpbspt"}},
X#define	X_NUM_HEADS	3
X	{{"_bpbtpc"}},
X	{{NULL}}
X};
X
Xu_int8_t *block_count_p;	/* block count var. in prototype image */
Xu_int8_t *block_table_p;	/* block number array in prototype image */
Xu_int8_t *num_heads_p;		/* number of tracks per cylinder */
Xu_int8_t *num_secs_p;		/* number of sectors per track */
Xint	maxblocklen;		/* size of this array */
Xint	curblocklen = 0;	/* actually used up bytes */
X
Xint biosdev;
X
Xchar		*loadprotoblocks(char *, long *);
Xint		loadblocknums(char *, int, struct disklabel *);
Xstatic void	devread(int, void *, daddr_t, size_t, char *);
Xstatic void	usage(void);
Xstatic int	record_block(u_int8_t *, daddr_t, u_int, struct disklabel *);
X
Xstatic const char RCSId[]="$MirBSD: installboot.c,v 1.23 2003/08/31 20:54:51 tg Exp $";
X
Xstatic int record_block(u_int8_t *bt, daddr_t blk, u_int bs,
X	struct disklabel *dl);
Xstatic int do_record(u_int8_t *bt, daddr_t blk, u_int bs,
X	struct disklabel *dl);
X
X
Xstatic void
Xusage(void)
X{
X	fprintf(stderr, "usage: %s [-n] [-v] [-s sec-per-track] [-h track-per-cyl] "
X	    "boot biosboot device\n", __progname);
X	exit(1);
X}
X
Xint
Xmain(int argc, char *argv[])
X{
X	int	c;
X	int	devfd;
X	char	*protostore;
X	long	protosize;
X	struct stat sb;
X	struct disklabel dl;
X	struct dos_mbr mbr;
X	struct dos_partition *dp;
X	off_t startoff = 0;
X	int mib[4];
X	size_t size;
X	dev_t devno;
X	bios_diskinfo_t di;
X
X	nsectors = nheads = -1;
X	while ((c = getopt(argc, argv, "vnh:s:")) != -1) {
X		switch (c) {
X		case 'h':
X			nheads = atoi(optarg);
X			userspec = 1;
X			break;
X		case 's':
X			nsectors = atoi(optarg);
X			userspec = 1;
X			break;
X		case 'n':
X			/* Do not actually write the bootblock to disk */
X			nowrite = 1;
X			break;
X		case 'v':
X			/* Chat */
X			verbose = 1;
X			break;
X		default:
X			usage();
X		}
X	}
X
X	if (argc - optind < 3) {
X		usage();
X	}
X
X	boot = argv[optind];
X	proto = argv[optind + 1];
X	realdev = dev = argv[optind + 2];
X
X	/* Open and check raw disk device */
X	if ((devfd = opendev(dev, (nowrite? O_RDONLY:O_RDWR),
X			     OPENDEV_PART, &realdev)) < 0)
X		err(1, "open: %s", realdev);
X
X	if (verbose) {
X		fprintf(stderr, "boot: %s\n", boot);
X		fprintf(stderr, "proto: %s\n", proto);
X		fprintf(stderr, "device: %s\n", realdev);
X	}
X
X	if (ioctl(devfd, DIOCGDINFO, &dl) != 0)
X		err(1, "disklabel: %s", realdev);
X
X	/* check disklabel */
X	if (dl.d_magic != DISKMAGIC)
X		err(1, "bad disklabel magic=%0x8x", dl.d_magic);
X
X	/* warn on unknown disklabel types */
X	if (dl.d_type == 0)
X		warnx("disklabel type unknown");
X
X	/* Load proto blocks into core */
X	if ((protostore = loadprotoblocks(proto, &protosize)) == NULL)
X		exit(1);
X
X	/* XXX - Paranoia: Make sure size is aligned! */
X	if (protosize & (DEV_BSIZE - 1))
X		err(1, "proto %s bad size=%ld", proto, protosize);
X
X	/* Write patched proto bootblocks into the superblock */
X	if (protosize > SBSIZE - DEV_BSIZE)
X		errx(1, "proto bootblocks too big");
X
X	if (fstat(devfd, &sb) < 0)
X		err(1, "stat: %s", realdev);
X
X	if (!S_ISCHR(sb.st_mode))
X		errx(1, "%s: Not a character device", realdev);
X
X	if (nheads == -1 || nsectors == -1) {
X		mib[0] = CTL_MACHDEP;
X		mib[1] = CPU_CHR2BLK;
X		mib[2] = sb.st_rdev;
X		size = sizeof(devno);
X		if(sysctl(mib, 3, &devno, &size, NULL, 0) >= 0) {
X			devno = MAKEBOOTDEV(major(devno), 0, 0,
X			    DISKUNIT(devno), RAW_PART);
X			mib[0] = CTL_MACHDEP;
X			mib[1] = CPU_BIOS;
X			mib[2] = BIOS_DISKINFO;
X			mib[3] = devno;
X			size = sizeof(di);
X			if(sysctl(mib, 4, &di, &size, NULL, 0) >= 0) {
X				nheads = di.bios_heads;
X				nsectors = di.bios_sectors;
X			}
X		}
X	}
X
X	if (nheads == -1 || nsectors == -1)
X		fprintf(stderr, "warning: Unable to get BIOS geometry, must/should specify -h and -s\nwarning: the drive may not boot in non-LBA mode");
X
X	/* Extract and load block numbers */
X	if (loadblocknums(boot, devfd, &dl) != 0)
X		exit(1);
X
X	/* Sync filesystems (to clean in-memory superblock?) */
X	sync(); sleep(1);
X
X	if (dl.d_type != 0 && dl.d_type != DTYPE_FLOPPY &&
X	    dl.d_type != DTYPE_VND) {
X		if (lseek(devfd, (off_t)DOSBBSECTOR, SEEK_SET) < 0 ||
X		    read(devfd, &mbr, sizeof(mbr)) < sizeof(mbr))
X			err(4, "can't read master boot record");
X
X		if (mbr.dmbr_sign != DOSMBR_SIGNATURE)
X			errx(1, "broken MBR");
X
X		/* Find MirBSD partition. */
X		for (dp = mbr.dmbr_parts; dp < &mbr.dmbr_parts[NDOSPART]; dp++) {
X			if (dp->dp_size && dp->dp_typ == DOSPTYP_MIRBSD) {
X				startoff = (off_t)dp->dp_start * dl.d_secsize;
X				fprintf(stderr, "using MBR partition %ld: "
X					"type %d (0x%02x) offset %d (0x%x)\n",
X					(long)(dp - mbr.dmbr_parts),
X					dp->dp_typ, dp->dp_typ,
X					dp->dp_start, dp->dp_start);
X				break;
X			}
X		}
X		/* Find OpenBSD partition. */
X		if (dp >= &mbr.dmbr_parts[NDOSPART])
X		for (dp = mbr.dmbr_parts; dp < &mbr.dmbr_parts[NDOSPART]; dp++) {
X			if (dp->dp_size && dp->dp_typ == DOSPTYP_OPENBSD) {
X				startoff = (off_t)dp->dp_start * dl.d_secsize;
X				fprintf(stderr, "using MBR partition %ld: "
X					"type %d (0x%02x) offset %d (0x%x)\n",
X					(long)(dp - mbr.dmbr_parts),
X					dp->dp_typ, dp->dp_typ,
X					dp->dp_start, dp->dp_start);
X				break;
X			}
X		}
X		/* don't check for old part number, that is ;-p */
X		if (dp >= &mbr.dmbr_parts[NDOSPART])
X			errx(1, "no MirBSD or OpenBSD partition");
X	}
X
X	*num_heads_p = nheads;
X	*num_secs_p = nsectors;
X
X	if (!nowrite) {
X		if (lseek(devfd, startoff, SEEK_SET) < 0 ||
X		    write(devfd, protostore, protosize) != protosize)
X			err(1, "write bootstrap");
X	}
X
X	(void)close(devfd);
X
X	return 0;
X}
X
Xchar *
Xloadprotoblocks(char *fname, long *size)
X{
X	int	fd;
X	size_t	tdsize;		/* text+data size */
X	char	*bp;
X	struct	nlist *nlp;
X	Elf_Ehdr eh;
X	Elf_Word phsize;
X	Elf_Phdr *ph;
X
X	fd = -1;
X	bp = NULL;
X
X	/* Locate block number array in proto file */
X	if (nlist(fname, nl) != 0) {
X		warnx("nlist: %s: symbols not found", fname);
X		return NULL;
X	}
X	/* Validate symbol types (global data). */
X	for (nlp = nl; nlp->n_un.n_name; nlp++) {
X		if (nlp->n_type != (N_TEXT)) {
X			warnx("nlist: %s: wrong type %x", nlp->n_un.n_name,
X			nlp->n_type);
X			return NULL;
X		}
X	}
X
X	if ((fd = open(fname, O_RDONLY)) < 0) {
X		warn("open: %s", fname);
X		return NULL;
X	}
X	if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) {
X		warn("read: %s", fname);
X		goto bad;
X	}
X	if (!IS_ELF(eh)) {
X		errx(1, "%s: bad magic: 0x%02x%02x%02x%02x",
X		boot,
X		eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1],
X		eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]);
X	}
X
X	/*
X	 * We have to include the exec header in the beginning of
X	 * the buffer, and leave extra space at the end in case
X	 * the actual write to disk wants to skip the header.
X	 */
X
X	/* program load header */
X	if (eh.e_phnum != 1) {
X		errx(1, "%s: only supports one ELF load section", boot);
X	}
X	phsize = eh.e_phnum * sizeof(Elf_Phdr);
X	ph = malloc(phsize);
X	if (ph == NULL) {
X		errx(1, "%s: unable to allocate program header space",
X		    boot);
X	}
X	lseek(fd, eh.e_phoff, SEEK_SET);
X
X	if (read(fd, ph, phsize) != phsize) {
X		errx(1, "%s: unable to read program header space", boot);
X	}
X
X	tdsize = ph->p_filesz;
X
X	/*
X	 * Allocate extra space here because the caller may copy
X	 * the boot block starting at the end of the exec header.
X	 * This prevents reading beyond the end of the buffer.
X	 */
X	if ((bp = calloc(tdsize, 1)) == NULL) {
X		warnx("malloc: %s: no memory", fname);
X		goto bad;
X	}
X	/* Read the rest of the file. */
X	lseek(fd, ph->p_offset, SEEK_SET);
X	if (read(fd, bp, tdsize) != tdsize) {
X		warn("read: %s", fname);
X		goto bad;
X	}
X
X	*size = tdsize;	/* not aligned to DEV_BSIZE */
X
X	/* Calculate the symbols' locations within the proto file */
X	block_count_p = (u_int8_t *) (bp + nl[X_BLOCK_COUNT].n_value);
X	block_table_p = (u_int8_t *) (bp + nl[X_BLOCK_TABLE].n_value);
X	num_heads_p = (u_int8_t *) (bp + nl[X_NUM_HEADS].n_value);
X	num_secs_p = (u_int8_t *) (bp + nl[X_NUM_SECS].n_value);
X	maxblocklen = *block_count_p;
X
X	if (verbose) {
X		fprintf(stderr, "%s: entry point %#x\n", fname, eh.e_entry);
X		fprintf(stderr, "proto bootblock size %ld\n", *size);
X		fprintf(stderr,
X		    "room for average %d filesystem blocks (%d bytes) at %#lx\n",
X		    (int)(((double)maxblocklen)/4.5), maxblocklen,
X		    nl[X_BLOCK_TABLE].n_value);
X	}
X
X	close(fd);
X	return bp;
X
X bad:
X	if (bp)
X		free(bp);
X	if (fd >= 0)
X		close(fd);
X	return NULL;
X}
X
Xstatic void
Xdevread(int fd, void *buf, daddr_t blk, size_t size, char *msg)
X{
X	if (lseek(fd, dbtob((off_t)blk), SEEK_SET) != dbtob((off_t)blk))
X		err(1, "%s: devread: lseek", msg);
X
X	if (read(fd, buf, size) != size)
X		err(1, "%s: devread: read", msg);
X}
X
Xstatic char sblock[SBSIZE];
X
Xint
Xloadblocknums(char *boot, int devfd, struct disklabel *dl)
X{
X	int		i, fd;
X	struct stat	statbuf, sb;
X	struct statfs	statfsbuf;
X	struct partition *pl;
X	struct fs	*fs;
X	char		*buf;
X	daddr_t		blk, *ap;
X	struct ufs1_dinode	*ip;
X	int		ndb;
X	u_int8_t	*bt;
X	int mib[4];
X	size_t		size;
X	dev_t dev;
X
X	/*
X	 * Open 2nd-level boot program and record the block numbers
X	 * it occupies on the filesystem represented by 'devfd'.
X	 */
X
X	/* Make sure the (probably new) boot file is on disk. */
X	sync(); sleep(1);
X
X	if ((fd = open(boot, O_RDONLY)) < 0)
X		err(1, "open: %s", boot);
X
X	if (fstatfs(fd, &statfsbuf) != 0)
X		err(1, "statfs: %s", boot);
X
X	if (strncmp(statfsbuf.f_fstypename, "ffs", MFSNAMELEN) &&
X	    strncmp(statfsbuf.f_fstypename, "ufs", MFSNAMELEN) ) {
X		errx(1, "%s: must be on an FFS filesystem", boot);
X	}
X
X#if 0
X	if (read(fd, &eh, sizeof(eh)) != sizeof(eh)) {
X		errx(1, "read: %s", boot);
X	}
X
X	if (!IS_ELF(eh)) {
X		errx(1, "%s: bad magic: 0x%02x%02x%02x%02x",
X		boot,
X		eh.e_ident[EI_MAG0], eh.e_ident[EI_MAG1],
X		eh.e_ident[EI_MAG2], eh.e_ident[EI_MAG3]);
X	}
X#endif
X
X	if (fsync(fd) != 0)
X		err(1, "fsync: %s", boot);
X
X	if (fstat(fd, &statbuf) != 0)
X		err(1, "fstat: %s", boot);
X
X	if (fstat(devfd, &sb) != 0)
X		err(1, "fstat: %s", realdev);
X
X	/* check devices */
X	mib[0] = CTL_MACHDEP;
X	mib[1] = CPU_CHR2BLK;
X	mib[2] = sb.st_rdev;
X	size = sizeof(dev);
X	if (sysctl(mib, 3, &dev, &size, NULL, 0) >= 0)
X		if (statbuf.st_dev / MAXPARTITIONS != dev / MAXPARTITIONS)
X			errx(1, "cross-device install");
X
X	pl = &dl->d_partitions[DISKPART(statbuf.st_dev)];
X	close(fd);
X
X	/* Read superblock */
X	devread(devfd, sblock, pl->p_offset + SBLOCK, SBSIZE, "superblock");
X	fs = (struct fs *)sblock;
X
X	/* Sanity-check super-block. */
X	if (fs->fs_magic != FS_MAGIC)
X		errx(1, "Bad magic number in superblock");
X	if (fs->fs_inopb <= 0)
X		err(1, "Bad inopb=%d in superblock", fs->fs_inopb);
X
X	/* Read inode */
X	if ((buf = malloc(fs->fs_bsize)) == NULL)
X		errx(1, "No memory for filesystem block");
X
X	blk = fsbtodb(fs, ino_to_fsba(fs, statbuf.st_ino));
X	devread(devfd, buf, pl->p_offset + blk, fs->fs_bsize, "inode");
X	ip = (struct ufs1_dinode *)(buf) + ino_to_fsbo(fs, statbuf.st_ino);
X
X	/*
X	 * Have the inode.  Figure out how many blocks we need.
X	 */
X	ndb = howmany(ip->di_size, fs->fs_bsize);
X	if (ndb <= 0)
X		errx(1, "No blocks to load");
X	if (verbose)
X		fprintf(stderr, "Will load %d blocks of size %d each.\n",
X			ndb, fs->fs_bsize);
X
X	if ((dl->d_type != 0 && dl->d_type != DTYPE_FLOPPY &&
X	    dl->d_type != DTYPE_VND) || userspec ) {
X		/* adjust disklabel w/ synthetic geometry */
X		dl->d_nsectors = nsectors;
X		dl->d_secpercyl = dl->d_nsectors * nheads;
X	}
X
X	if (verbose)
X		fprintf(stderr, "Using disk geometry of %u sectors and %u heads.\n",
X			dl->d_nsectors, dl->d_secpercyl/dl->d_nsectors);
X
X	/*
X	 * Get the block numbers; we don't handle fragments
X	 */
X	ap = ip->di_db;
X	bt = block_table_p;
X	for (i = 0; i < NDADDR && *ap && ndb; i++, ap++, ndb--)
X		bt += record_block(bt, pl->p_offset + fsbtodb(fs, *ap),
X					    fs->fs_bsize / 512, dl);
X	if (ndb != 0) {
X
X		/*
X		 * Just one level of indirections; there isn't much room
X		 * for more in the 2nd-level /boot anyway.
X		 */
X		blk = fsbtodb(fs, ip->di_ib[0]);
X		devread(devfd, buf, pl->p_offset + blk, fs->fs_bsize,
X			"indirect block");
X		ap = (daddr_t *)buf;
X		for (; i < NINDIR(fs) && *ap && ndb; i++, ap++, ndb--)
X			bt += record_block(bt, pl->p_offset + fsbtodb(fs, *ap),
X					   fs->fs_bsize / 512, dl);
X	}
X
X	bt += record_block(bt, 0, 0, dl);
X
X	if (bt > (block_table_p + maxblocklen))
X		errx(1, "Too many blocks");
X
X	if (verbose)
X		fprintf(stderr, "%s: %d entries total (%d bytes)\n",
X			boot, block_count_p[0], curblocklen);
X
X	return 0;
X}
X
Xstatic int
Xrecord_block(u_int8_t *bt, daddr_t blk, u_int bs, struct disklabel *dl)
X{
X	static u_int W_num = 0;
X	static daddr_t W_ofs = 0;
X
X	int flush = 0, cache = 0, retval = 0;
X	int i;
X
X	if (!blk) {
X		++flush;
X	} else if (!W_ofs) {
X		++cache;
X	} else if (blk == (W_ofs+W_num)) {
X		++cache;
X	} else {
X		++flush;
X		++cache;
X	}
X
X	if (flush) {
X		/*
X		 * Flush the blocks cached to the disc.
X		 * Obey the track boundaries if possible.
X		 */
X		if (!W_num) goto flush_end;
X		if ((nheads == -1) || (nsectors == -1)) {
X			retval += do_record(bt+retval, W_ofs, W_num, dl);
X			goto flush_end;
X		}
X
X		i = W_ofs % nsectors;		/* sector within track -1 */
X		if (i < (nsectors-1)) {
X			i = nsectors - i;
X			if (i > W_num) i=W_num;
X			retval += do_record(bt+retval, W_ofs, i, dl);
X			W_ofs += i;
X			W_num -= i;
X		}
X
X		while (W_num > nsectors) {
X			retval += do_record(bt+retval, W_ofs, nsectors, dl);
X			W_ofs += nsectors;
X			W_num -= nsectors;
X		}
X
X		if (W_num)
X			retval += do_record(bt+retval, W_ofs, W_num, dl);
X
X	flush_end:
X		W_ofs=0; W_num=0;
X	}
X
X	if (cache) {
X		if (!W_ofs) W_ofs = blk;
X		W_num += bs;
X	}
X
X	return (retval);
X}
X
Xstatic int
Xdo_record(u_int8_t *bt, daddr_t blk, u_int bs, struct disklabel *dl)
X{
X	static u_int i = 0;
X	u_int8_t tv, len;
X	u_int64_t bk, wbk;
X	u_int j;
X	int wbs, retval = 0;
X	u_int8_t *wbt;
X
X	if ((!blk) || (!bt))
X		return 0;
X
X	if (bs > 768)	/* after that there's VGA memory */
X		errx(1, "Too many blocks in a chunk!");
X
X	wbs = bs;
X	wbt = bt;
X	bk = blk;
X	if (verbose)
X		fprintf(stderr, "%2d: %2d @%lld (0x%08llX)\n",
X		    i, bs, bk, bk);
X
X	do {
X		++i;
X
X		if (bk < 0x0000000000000100ULL)
X			len = 0;
X		else if (bk < 0x0000000000010000ULL)
X			len = 1;
X		else if (bk < 0x0000000001000000ULL)
X			len = 2;
X		else if (bk < 0x0000000100000000ULL)
X			len = 3;
X		else if (bk < 0x0000010000000000ULL)
X			len = 4;
X		else if (bk < 0x0001000000000000ULL)
X			len = 5;
X		else if (bk < 0x0100000000000000ULL)
X			len = 6;
X		else	len = 7;
X
X		if (wbs < 33)
X			tv = (len << 5) | (wbs - 1);
X		  else	tv = (len << 5) | 31;
X
X		*(wbt++) = tv;
X		wbk = bk;
X		bk += 1 + (tv & 31);
X		for (j = 0; j <= len; ++j) {
X			*(wbt++) = (wbk & 0xFF);
X			wbk >>= 8;
X		}
X
X		wbs -= 32;
X		retval += (len+2);
X	} while (wbs > 0);
X
X	*block_count_p = i;
X	curblocklen += retval;
X	return (retval);
X}
END-of-installboot.c
exit
#----- cutting here may damage your screen surface -----#

HTH&HAND,
//Thorsten
-- 
[...] if maybe ext3fs wasn't a better pick, or jfs, or maybe reiserfs, oh but
what about xfs, and if only i had waited until reiser4 was ready... in the be-
ginning, there was ffs, and in the middle, there was ffs, and at the end, there
was still ffs, and the sys admins knew it was good. :)	-- Ted Unangst über *fs