Home | History | Annotate | Line # | Download | only in bootxx
boot1.c revision 1.22
      1 /*	$NetBSD: boot1.c,v 1.22 2023/06/29 14:18:58 manu Exp $	*/
      2 
      3 /*-
      4  * Copyright (c) 2003 The NetBSD Foundation, Inc.
      5  * All rights reserved.
      6  *
      7  * This code is derived from software contributed to The NetBSD Foundation
      8  * by David Laight.
      9  *
     10  * Redistribution and use in source and binary forms, with or without
     11  * modification, are permitted provided that the following conditions
     12  * are met:
     13  * 1. Redistributions of source code must retain the above copyright
     14  *    notice, this list of conditions and the following disclaimer.
     15  * 2. Redistributions in binary form must reproduce the above copyright
     16  *    notice, this list of conditions and the following disclaimer in the
     17  *    documentation and/or other materials provided with the distribution.
     18  *
     19  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
     20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
     21  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     22  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
     23  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
     24  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
     25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
     26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
     27  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
     28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
     29  * POSSIBILITY OF SUCH DAMAGE.
     30  */
     31 
     32 #include <sys/cdefs.h>
     33 __RCSID("$NetBSD: boot1.c,v 1.22 2023/06/29 14:18:58 manu Exp $");
     34 
     35 #include <lib/libsa/stand.h>
     36 #include <lib/libkern/libkern.h>
     37 #include <biosdisk_ll.h>
     38 
     39 #include <sys/param.h>
     40 #include <sys/uuid.h>
     41 #include <sys/bootblock.h>
     42 #include <sys/disklabel.h>
     43 #include <sys/disklabel_gpt.h>
     44 #include <dev/raidframe/raidframevar.h>	/* For RF_PROTECTED_SECTORS */
     45 
     46 #define XSTR(x) #x
     47 #define STR(x) XSTR(x)
     48 
     49 static daddr_t bios_sector;
     50 
     51 static struct biosdisk_ll d;
     52 
     53 const char *boot1(uint32_t, uint64_t *);
     54 #ifndef NO_GPT
     55 static daddr_t gpt_lookup(daddr_t);
     56 #endif
     57 extern void putstr(const char *);
     58 
     59 extern struct disklabel ptn_disklabel;
     60 
     61 static int
     62 ob(void)
     63 {
     64 	return open("boot", 0);
     65 }
     66 
     67 const char *
     68 boot1(uint32_t biosdev, uint64_t *sector)
     69 {
     70 	struct stat sb;
     71 	int fd;
     72 
     73 	bios_sector = *sector;
     74 	d.dev = biosdev;
     75 
     76 	putstr("\r\nNetBSD/x86 " STR(FS) " Primary Bootstrap\r\n");
     77 
     78 	if (set_geometry(&d, NULL))
     79 		return "set_geometry\r\n";
     80 
     81 	/*
     82 	 * We default to the filesystem at the start of the
     83 	 * MBR partition
     84 	 */
     85 	fd = ob();
     86 	if (fd != -1)
     87 		goto done;
     88 	/*
     89 	 * Maybe the filesystem is enclosed in a raid set.
     90 	 * add in size of raidframe header and try again.
     91 	 * (Maybe this should only be done if the filesystem
     92 	 * magic number is absent.)
     93 	 */
     94 	bios_sector += RF_PROTECTED_SECTORS;
     95 	fd = ob();
     96 	if (fd != -1)
     97 		goto done;
     98 
     99 #ifndef NO_GPT
    100 	/*
    101 	 * Test for a GPT inside the RAID
    102 	 */
    103 	bios_sector += gpt_lookup(bios_sector);
    104 	fd = ob();
    105 	if (fd != -1)
    106 		goto done;
    107 #endif
    108 
    109 	/*
    110 	 * Nothing at the start of the MBR partition, fallback on
    111 	 * partition 'a' from the disklabel in this MBR partition.
    112 	 */
    113 	if (ptn_disklabel.d_magic != DISKMAGIC ||
    114 	    ptn_disklabel.d_magic2 != DISKMAGIC ||
    115 	    ptn_disklabel.d_partitions[0].p_fstype == FS_UNUSED)
    116 		goto done;
    117 	bios_sector = ptn_disklabel.d_partitions[0].p_offset;
    118 	*sector = bios_sector;
    119 	if (ptn_disklabel.d_partitions[0].p_fstype == FS_RAID)
    120 		bios_sector += RF_PROTECTED_SECTORS;
    121 
    122 	fd = ob();
    123 
    124 done:
    125 	if (fd == -1 || fstat(fd, &sb) == -1)
    126 		return "Can't open /boot\r\n";
    127 
    128 	biosdev = (uint32_t)sb.st_size;
    129 #if 0
    130 	if (biosdev > SECONDARY_MAX_LOAD)
    131 		return "/boot too large\r\n";
    132 #endif
    133 
    134 	if (read(fd, (void *)SECONDARY_LOAD_ADDRESS, biosdev) != biosdev)
    135 		return "/boot load failed\r\n";
    136 
    137 	if (*(uint32_t *)(SECONDARY_LOAD_ADDRESS + 4) != X86_BOOT_MAGIC_2)
    138 		return "Invalid /boot file format\r\n";
    139 
    140 	/* We need to jump to the secondary bootstrap in realmode */
    141 	return 0;
    142 }
    143 
    144 int
    145 blkdevstrategy(void *devdata, int flag, daddr_t dblk, size_t size, void *buf, size_t *rsize)
    146 {
    147 	if (flag != F_READ)
    148 		return EROFS;
    149 
    150 	if (size & (BIOSDISK_DEFAULT_SECSIZE - 1))
    151 		return EINVAL;
    152 
    153 	if (rsize)
    154 		*rsize = size;
    155 
    156 	if (size != 0 && readsects(&d, bios_sector + dblk,
    157 				   size / BIOSDISK_DEFAULT_SECSIZE,
    158 				   buf, 1) != 0)
    159 		return EIO;
    160 
    161 	return 0;
    162 }
    163 
    164 #ifndef NO_GPT
    165 static int
    166 is_unused(struct gpt_ent *ent)
    167 {
    168 	const struct uuid unused = GPT_ENT_TYPE_UNUSED;
    169 
    170 	return (memcmp(ent->ent_type, &unused, sizeof(unused)) == 0);
    171 }
    172 
    173 static int
    174 is_bootable(struct gpt_ent *ent)
    175 {
    176 	/* GPT_ENT_TYPE_NETBSD_RAID omitted as we are already in a RAID */
    177 	const struct uuid bootable[] = {
    178 		GPT_ENT_TYPE_NETBSD_FFS,
    179 		GPT_ENT_TYPE_NETBSD_LFS,
    180 		GPT_ENT_TYPE_NETBSD_CCD,
    181 		GPT_ENT_TYPE_NETBSD_CGD,
    182 	};
    183 	int i;
    184 
    185 	for (i = 0; i < sizeof(bootable) / sizeof(*bootable); i++) {
    186 		if (memcmp(ent->ent_type, &bootable[i],
    187 		    sizeof(struct uuid)) == 0)
    188 			return 1;
    189 	}
    190 
    191 	return 0;
    192 }
    193 
    194 static daddr_t
    195 gpt_lookup(daddr_t sector)
    196 {
    197 	char buf[BIOSDISK_DEFAULT_SECSIZE];
    198 	struct mbr_sector *pmbr;
    199 	const char gpt_hdr_sig[] = GPT_HDR_SIG;
    200 	struct gpt_hdr *hdr;
    201 	struct gpt_ent *ent;
    202 	uint32_t nents;
    203 	uint32_t entsz;
    204 	uint32_t entries_per_sector;
    205 	uint32_t sectors_per_entry;
    206 	uint64_t firstpart_lba = 0;
    207 	uint64_t bootable_lba = 0;
    208 	uint64_t bootme_lba = 0;
    209 	int i, j;
    210 
    211 	/*
    212 	 * Look for a PMBR
    213 	 */
    214 	if (readsects(&d, sector, 1, buf, 1) != 0)
    215 		return 0;
    216 
    217 	pmbr = (struct mbr_sector *)buf;
    218 
    219 	if (pmbr->mbr_magic != htole16(MBR_MAGIC))
    220 		return 0;
    221 
    222 	if (pmbr->mbr_parts[0].mbrp_type != MBR_PTYPE_PMBR)
    223 		return 0;
    224 
    225 	sector++; /* skip PMBR */
    226 
    227 	/*
    228 	 * Look for a GPT header
    229 	 * Space is scarce, we do not check CRC.
    230 	 */
    231 	if (readsects(&d, sector, 1, buf, 1) != 0)
    232 		return 0;
    233 
    234 	hdr = (struct gpt_hdr *)buf;
    235 
    236 	if (memcmp(gpt_hdr_sig, hdr->hdr_sig, sizeof(hdr->hdr_sig)) != 0)
    237 		return 0;
    238 
    239 	if (hdr->hdr_revision != htole32(GPT_HDR_REVISION))
    240 		return 0;
    241 
    242 	if (le32toh(hdr->hdr_size) > BIOSDISK_DEFAULT_SECSIZE)
    243 		return 0;
    244 
    245 	nents = le32toh(hdr->hdr_entries);
    246 	entsz = le32toh(hdr->hdr_entsz);
    247 
    248 	sector++; /* skip GPT header */
    249 
    250 	/*
    251 	 * Read partition table
    252 	 *
    253 	 * According to UEFI specification section 5.3.2, entries
    254 	 * are 128 * (2^n) bytes long. The most common scenario is
    255 	 * 128 bytes (n = 0) where there are 4 entries per sector.
    256 	 * If n > 2, then entries spans multiple sectors, but they
    257 	 * remain sector-aligned.
    258 	 */
    259 	entries_per_sector = BIOSDISK_DEFAULT_SECSIZE / entsz;
    260 	if (entries_per_sector == 0)
    261 		entries_per_sector = 1;
    262 
    263 	sectors_per_entry = entsz / BIOSDISK_DEFAULT_SECSIZE;
    264 	if (sectors_per_entry == 0)
    265 		sectors_per_entry = 1;
    266 
    267 	for (i = 0; i < nents; i += entries_per_sector) {
    268 		if (readsects(&d, sector, 1, buf, 1) != 0)
    269 			return 0;
    270 
    271 		sector += sectors_per_entry;
    272 
    273 		for (j = 0; j < entries_per_sector; j++) {
    274 			ent = (struct gpt_ent *)&buf[j * entsz];
    275 
    276 			if (is_unused(ent))
    277 				continue;
    278 
    279 			/* First bootme wins, we can stop there */
    280 			if (ent->ent_attr & GPT_ENT_ATTR_BOOTME) {
    281 				bootme_lba = le64toh(ent->ent_lba_start);
    282 				goto out;
    283 			}
    284 
    285 			if (firstpart_lba == 0)
    286 				firstpart_lba = le64toh(ent->ent_lba_start);
    287 
    288 			if (is_bootable(ent) && bootable_lba == 0)
    289 				bootable_lba = le64toh(ent->ent_lba_start);
    290 		}
    291 	}
    292 
    293 out:
    294 	if (bootme_lba)
    295 		return bootme_lba;
    296 
    297 	if (bootable_lba)
    298 		return bootable_lba;
    299 
    300 	if (firstpart_lba)
    301 		return firstpart_lba;
    302 
    303 	return 0;
    304 }
    305 #endif /* ! NO_GPT */
    306