1 1.11 mlelstv /* $NetBSD: efidisk.c,v 1.11 2024/01/06 21:26:43 mlelstv Exp $ */ 2 1.1 nonaka 3 1.1 nonaka /*- 4 1.1 nonaka * Copyright (c) 2016 Kimihiro Nonaka <nonaka (at) netbsd.org> 5 1.1 nonaka * All rights reserved. 6 1.1 nonaka * 7 1.1 nonaka * Redistribution and use in source and binary forms, with or without 8 1.1 nonaka * modification, are permitted provided that the following conditions 9 1.1 nonaka * are met: 10 1.1 nonaka * 1. Redistributions of source code must retain the above copyright 11 1.1 nonaka * notice, this list of conditions and the following disclaimer. 12 1.1 nonaka * 2. Redistributions in binary form must reproduce the above copyright 13 1.1 nonaka * notice, this list of conditions and the following disclaimer in the 14 1.1 nonaka * documentation and/or other materials provided with the distribution. 15 1.1 nonaka * 16 1.1 nonaka * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 1.1 nonaka * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 1.1 nonaka * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 1.1 nonaka * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 1.1 nonaka * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 1.1 nonaka * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 1.1 nonaka * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 1.1 nonaka * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 1.1 nonaka * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 1.1 nonaka * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 1.1 nonaka * SUCH DAMAGE. 27 1.1 nonaka */ 28 1.1 nonaka 29 1.2 nonaka #define FSTYPENAMES /* for sys/disklabel.h */ 30 1.2 nonaka 31 1.1 nonaka #include "efiboot.h" 32 1.1 nonaka 33 1.8 manu #include <sys/param.h> /* for howmany, required by <dev/raidframe/raidframevar.h> */ 34 1.2 nonaka #include <sys/disklabel.h> 35 1.8 manu #include <sys/disklabel_gpt.h> 36 1.2 nonaka 37 1.2 nonaka #include "biosdisk.h" 38 1.2 nonaka #include "biosdisk_ll.h" 39 1.2 nonaka #include "devopen.h" 40 1.1 nonaka #include "efidisk.h" 41 1.11 mlelstv #include "bootinfo.h" 42 1.1 nonaka 43 1.1 nonaka static struct efidiskinfo_lh efi_disklist; 44 1.1 nonaka static int nefidisks; 45 1.11 mlelstv static struct btinfo_biosgeom *bibg; 46 1.11 mlelstv static size_t bibg_len; 47 1.1 nonaka 48 1.8 manu #define MAXDEVNAME 39 /* "NAME=" + 34 char part_name */ 49 1.8 manu 50 1.8 manu #include <dev/raidframe/raidframevar.h> 51 1.8 manu #define RF_COMPONENT_INFO_OFFSET 16384 /* from sys/dev/raidframe/rf_netbsdkintf.c */ 52 1.8 manu #define RF_COMPONENT_LABEL_VERSION 2 /* from <dev/raidframe/rf_raid.h> */ 53 1.8 manu 54 1.8 manu #define RAIDFRAME_NDEV 16 /* abitrary limit to 15 raidframe devices */ 55 1.8 manu struct efi_raidframe { 56 1.8 manu int last_unit; 57 1.8 manu int serial; 58 1.8 manu const struct efidiskinfo *edi; 59 1.8 manu int parent_part; 60 1.8 manu char parent_name[MAXDEVNAME + 1]; 61 1.8 manu daddr_t offset; 62 1.8 manu daddr_t size; 63 1.8 manu }; 64 1.8 manu 65 1.8 manu static void 66 1.8 manu dealloc_biosdisk_part(struct biosdisk_partition *part, int nparts) 67 1.8 manu { 68 1.8 manu int i; 69 1.8 manu 70 1.8 manu for (i = 0; i < nparts; i++) { 71 1.8 manu if (part[i].part_name != NULL) { 72 1.8 manu dealloc(part[i].part_name, BIOSDISK_PART_NAME_LEN); 73 1.8 manu part[i].part_name = NULL; 74 1.8 manu } 75 1.8 manu } 76 1.10 riastrad 77 1.8 manu dealloc(part, sizeof(*part) * nparts); 78 1.10 riastrad 79 1.8 manu return; 80 1.8 manu } 81 1.8 manu 82 1.1 nonaka void 83 1.1 nonaka efi_disk_probe(void) 84 1.1 nonaka { 85 1.1 nonaka EFI_STATUS status; 86 1.1 nonaka UINTN i, nhandles; 87 1.1 nonaka EFI_HANDLE *handles; 88 1.1 nonaka EFI_BLOCK_IO *bio; 89 1.1 nonaka EFI_BLOCK_IO_MEDIA *media; 90 1.2 nonaka EFI_DEVICE_PATH *dp; 91 1.1 nonaka struct efidiskinfo *edi; 92 1.2 nonaka int dev, depth = -1; 93 1.1 nonaka 94 1.1 nonaka TAILQ_INIT(&efi_disklist); 95 1.1 nonaka 96 1.1 nonaka status = LibLocateHandle(ByProtocol, &BlockIoProtocol, NULL, 97 1.1 nonaka &nhandles, &handles); 98 1.1 nonaka if (EFI_ERROR(status)) 99 1.7 nonaka return; 100 1.1 nonaka 101 1.2 nonaka if (efi_bootdp != NULL) 102 1.2 nonaka depth = efi_device_path_depth(efi_bootdp, MEDIA_DEVICE_PATH); 103 1.2 nonaka 104 1.2 nonaka /* 105 1.2 nonaka * U-Boot incorrectly represents devices with a single 106 1.2 nonaka * MEDIA_DEVICE_PATH component. In that case include that 107 1.2 nonaka * component into the matching, otherwise we'll blindly select 108 1.2 nonaka * the first device. 109 1.2 nonaka */ 110 1.2 nonaka if (depth == 0) 111 1.2 nonaka depth = 1; 112 1.2 nonaka 113 1.1 nonaka for (i = 0; i < nhandles; i++) { 114 1.1 nonaka status = uefi_call_wrapper(BS->HandleProtocol, 3, handles[i], 115 1.1 nonaka &BlockIoProtocol, (void **)&bio); 116 1.1 nonaka if (EFI_ERROR(status)) 117 1.7 nonaka continue; 118 1.1 nonaka 119 1.1 nonaka media = bio->Media; 120 1.1 nonaka if (media->LogicalPartition || !media->MediaPresent) 121 1.1 nonaka continue; 122 1.1 nonaka 123 1.1 nonaka edi = alloc(sizeof(struct efidiskinfo)); 124 1.1 nonaka memset(edi, 0, sizeof(*edi)); 125 1.2 nonaka edi->type = BIOSDISK_TYPE_HD; 126 1.1 nonaka edi->bio = bio; 127 1.1 nonaka edi->media_id = media->MediaId; 128 1.1 nonaka 129 1.2 nonaka if (efi_bootdp != NULL && depth > 0) { 130 1.2 nonaka status = uefi_call_wrapper(BS->HandleProtocol, 3, 131 1.2 nonaka handles[i], &DevicePathProtocol, (void **)&dp); 132 1.2 nonaka if (EFI_ERROR(status)) 133 1.2 nonaka goto next; 134 1.2 nonaka if (efi_device_path_ncmp(efi_bootdp, dp, depth) == 0) { 135 1.2 nonaka edi->bootdev = true; 136 1.2 nonaka TAILQ_INSERT_HEAD(&efi_disklist, edi, 137 1.2 nonaka list); 138 1.2 nonaka continue; 139 1.2 nonaka } 140 1.2 nonaka } 141 1.2 nonaka next: 142 1.2 nonaka TAILQ_INSERT_TAIL(&efi_disklist, edi, list); 143 1.2 nonaka } 144 1.1 nonaka 145 1.2 nonaka FreePool(handles); 146 1.1 nonaka 147 1.6 nonaka if (efi_bootdp_type == BOOT_DEVICE_TYPE_CD) { 148 1.3 nonaka edi = TAILQ_FIRST(&efi_disklist); 149 1.3 nonaka if (edi != NULL && edi->bootdev) { 150 1.3 nonaka edi->type = BIOSDISK_TYPE_CD; 151 1.3 nonaka TAILQ_REMOVE(&efi_disklist, edi, list); 152 1.3 nonaka TAILQ_INSERT_TAIL(&efi_disklist, edi, list); 153 1.1 nonaka } 154 1.1 nonaka } 155 1.1 nonaka 156 1.1 nonaka dev = 0x80; 157 1.1 nonaka TAILQ_FOREACH(edi, &efi_disklist, list) { 158 1.1 nonaka edi->dev = dev++; 159 1.2 nonaka if (edi->type == BIOSDISK_TYPE_HD) 160 1.2 nonaka nefidisks++; 161 1.2 nonaka if (edi->bootdev) 162 1.2 nonaka boot_biosdev = edi->dev; 163 1.2 nonaka } 164 1.11 mlelstv 165 1.11 mlelstv bibg_len = sizeof(*bibg) + nefidisks * sizeof(struct bi_biosgeom_entry); 166 1.11 mlelstv bibg = alloc(bibg_len); 167 1.11 mlelstv if (bibg == NULL) 168 1.11 mlelstv return; 169 1.11 mlelstv 170 1.11 mlelstv bibg->num = nefidisks; 171 1.11 mlelstv 172 1.11 mlelstv i = 0; 173 1.11 mlelstv TAILQ_FOREACH(edi, &efi_disklist, list) { 174 1.11 mlelstv if (edi->type == BIOSDISK_TYPE_HD) { 175 1.11 mlelstv memset(&bibg->disk[i], 0, sizeof(bibg->disk[i])); 176 1.11 mlelstv bibg->disk[i].dev = edi->dev; 177 1.11 mlelstv bibg->disk[i].flags = BI_GEOM_INVALID; 178 1.11 mlelstv } 179 1.11 mlelstv ++i; 180 1.11 mlelstv } 181 1.2 nonaka } 182 1.2 nonaka 183 1.8 manu static void 184 1.8 manu efi_raidframe_probe(struct efi_raidframe *raidframe, int *raidframe_count, 185 1.8 manu const struct efidiskinfo *edi, 186 1.8 manu struct biosdisk_partition *part, int parent_part) 187 1.8 manu 188 1.8 manu { 189 1.8 manu int i = *raidframe_count; 190 1.8 manu struct RF_ComponentLabel_s label; 191 1.8 manu 192 1.8 manu if (i + 1 > RAIDFRAME_NDEV) 193 1.8 manu return; 194 1.8 manu 195 1.8 manu if (biosdisk_read_raidframe(edi->dev, part->offset, &label) != 0) 196 1.8 manu return; 197 1.8 manu 198 1.8 manu if (label.version != RF_COMPONENT_LABEL_VERSION) 199 1.8 manu return; 200 1.8 manu 201 1.8 manu raidframe[i].last_unit = label.last_unit; 202 1.8 manu raidframe[i].serial = label.serial_number; 203 1.8 manu raidframe[i].edi = edi; 204 1.8 manu raidframe[i].parent_part = parent_part; 205 1.8 manu if (part->part_name) 206 1.8 manu strlcpy(raidframe[i].parent_name, part->part_name, MAXDEVNAME); 207 1.8 manu else 208 1.8 manu raidframe[i].parent_name[0] = '\0'; 209 1.8 manu raidframe[i].offset = part->offset; 210 1.8 manu raidframe[i].size = label.__numBlocks; 211 1.8 manu 212 1.8 manu (*raidframe_count)++; 213 1.8 manu 214 1.8 manu return; 215 1.8 manu } 216 1.8 manu 217 1.2 nonaka void 218 1.2 nonaka efi_disk_show(void) 219 1.2 nonaka { 220 1.2 nonaka const struct efidiskinfo *edi; 221 1.8 manu struct efi_raidframe raidframe[RAIDFRAME_NDEV]; 222 1.8 manu int raidframe_count = 0; 223 1.2 nonaka EFI_BLOCK_IO_MEDIA *media; 224 1.2 nonaka struct biosdisk_partition *part; 225 1.2 nonaka uint64_t size; 226 1.8 manu int i, j, nparts; 227 1.2 nonaka bool first; 228 1.2 nonaka 229 1.2 nonaka TAILQ_FOREACH(edi, &efi_disklist, list) { 230 1.2 nonaka media = edi->bio->Media; 231 1.2 nonaka first = true; 232 1.2 nonaka printf("disk "); 233 1.2 nonaka switch (edi->type) { 234 1.2 nonaka case BIOSDISK_TYPE_CD: 235 1.2 nonaka printf("cd0"); 236 1.2 nonaka printf(" mediaId %u", media->MediaId); 237 1.2 nonaka if (edi->media_id != media->MediaId) 238 1.2 nonaka printf("(%u)", edi->media_id); 239 1.2 nonaka printf("\n"); 240 1.2 nonaka printf(" cd0a\n"); 241 1.2 nonaka break; 242 1.2 nonaka case BIOSDISK_TYPE_HD: 243 1.2 nonaka printf("hd%d", edi->dev & 0x7f); 244 1.2 nonaka printf(" mediaId %u", media->MediaId); 245 1.2 nonaka if (edi->media_id != media->MediaId) 246 1.2 nonaka printf("(%u)", edi->media_id); 247 1.2 nonaka printf(" size "); 248 1.2 nonaka size = (media->LastBlock + 1) * media->BlockSize; 249 1.2 nonaka if (size >= (10ULL * 1024 * 1024 * 1024)) 250 1.2 nonaka printf("%"PRIu64" GB", size / (1024 * 1024 * 1024)); 251 1.2 nonaka else 252 1.2 nonaka printf("%"PRIu64" MB", size / (1024 * 1024)); 253 1.2 nonaka printf("\n"); 254 1.2 nonaka break; 255 1.2 nonaka } 256 1.2 nonaka if (edi->type != BIOSDISK_TYPE_HD) 257 1.2 nonaka continue; 258 1.2 nonaka 259 1.8 manu if (biosdisk_readpartition(edi->dev, 0, 0, &part, &nparts)) 260 1.2 nonaka continue; 261 1.2 nonaka 262 1.2 nonaka for (i = 0; i < nparts; i++) { 263 1.2 nonaka if (part[i].size == 0) 264 1.2 nonaka continue; 265 1.2 nonaka if (part[i].fstype == FS_UNUSED) 266 1.2 nonaka continue; 267 1.8 manu if (part[i].fstype == FS_RAID) { 268 1.8 manu efi_raidframe_probe(raidframe, &raidframe_count, 269 1.8 manu edi, &part[i], i); 270 1.8 manu } 271 1.2 nonaka if (first) { 272 1.2 nonaka printf(" "); 273 1.2 nonaka first = false; 274 1.2 nonaka } 275 1.9 manu if (part[i].part_name && part[i].part_name[0]) 276 1.8 manu printf(" NAME=%s(", part[i].part_name); 277 1.8 manu else 278 1.8 manu printf(" hd%d%c(", edi->dev & 0x7f, i + 'a'); 279 1.2 nonaka if (part[i].guid != NULL) 280 1.2 nonaka printf("%s", part[i].guid->name); 281 1.2 nonaka else if (part[i].fstype < FSMAXTYPES) 282 1.2 nonaka printf("%s", fstypenames[part[i].fstype]); 283 1.2 nonaka else 284 1.2 nonaka printf("%d", part[i].fstype); 285 1.2 nonaka printf(")"); 286 1.2 nonaka } 287 1.2 nonaka if (!first) 288 1.2 nonaka printf("\n"); 289 1.8 manu dealloc_biosdisk_part(part, nparts); 290 1.8 manu } 291 1.8 manu 292 1.8 manu for (i = 0; i < raidframe_count; i++) { 293 1.8 manu size_t secsize = raidframe[i].edi->bio->Media->BlockSize; 294 1.8 manu printf("raidframe raid%d serial %d in ", 295 1.8 manu raidframe[i].last_unit, raidframe[i].serial); 296 1.8 manu if (raidframe[i].parent_name[0]) 297 1.8 manu printf("NAME=%s size ", raidframe[i].parent_name); 298 1.8 manu else 299 1.8 manu printf("hd%d%c size ", 300 1.8 manu raidframe[i].edi->dev & 0x7f, 301 1.8 manu raidframe[i].parent_part + 'a'); 302 1.8 manu if (raidframe[i].size >= (10ULL * 1024 * 1024 * 1024 / secsize)) 303 1.8 manu printf("%"PRIu64" GB", 304 1.8 manu raidframe[i].size / (1024 * 1024 * 1024 / secsize)); 305 1.8 manu else 306 1.8 manu printf("%"PRIu64" MB", 307 1.8 manu raidframe[i].size / (1024 * 1024 / secsize)); 308 1.8 manu printf("\n"); 309 1.8 manu 310 1.8 manu if (biosdisk_readpartition(raidframe[i].edi->dev, 311 1.8 manu raidframe[i].offset + RF_PROTECTED_SECTORS, 312 1.8 manu raidframe[i].size, 313 1.8 manu &part, &nparts)) 314 1.8 manu continue; 315 1.10 riastrad 316 1.8 manu first = 1; 317 1.8 manu for (j = 0; j < nparts; j++) { 318 1.8 manu bool bootme = part[j].attr & GPT_ENT_ATTR_BOOTME; 319 1.8 manu 320 1.8 manu if (part[j].size == 0) 321 1.8 manu continue; 322 1.8 manu if (part[j].fstype == FS_UNUSED) 323 1.8 manu continue; 324 1.8 manu if (part[j].fstype == FS_RAID) /* raid in raid? */ 325 1.8 manu continue; 326 1.8 manu if (first) { 327 1.8 manu printf(" "); 328 1.8 manu first = 0; 329 1.8 manu } 330 1.9 manu if (part[j].part_name && part[j].part_name[0]) 331 1.8 manu printf(" NAME=%s(", part[j].part_name); 332 1.8 manu else 333 1.8 manu printf(" raid%d%c(", 334 1.8 manu raidframe[i].last_unit, j + 'a'); 335 1.8 manu if (part[j].guid != NULL) 336 1.8 manu printf("%s", part[j].guid->name); 337 1.8 manu else if (part[j].fstype < FSMAXTYPES) 338 1.8 manu printf("%s", 339 1.8 manu fstypenames[part[j].fstype]); 340 1.8 manu else 341 1.8 manu printf("%d", part[j].fstype); 342 1.8 manu printf("%s)", bootme ? ", bootme" : ""); 343 1.8 manu } 344 1.8 manu 345 1.8 manu if (first == 0) 346 1.8 manu printf("\n"); 347 1.8 manu 348 1.8 manu dealloc_biosdisk_part(part, nparts); 349 1.1 nonaka } 350 1.1 nonaka } 351 1.1 nonaka 352 1.1 nonaka const struct efidiskinfo * 353 1.1 nonaka efidisk_getinfo(int dev) 354 1.1 nonaka { 355 1.1 nonaka const struct efidiskinfo *edi; 356 1.1 nonaka 357 1.1 nonaka TAILQ_FOREACH(edi, &efi_disklist, list) { 358 1.1 nonaka if (dev == edi->dev) 359 1.1 nonaka return edi; 360 1.1 nonaka } 361 1.1 nonaka return NULL; 362 1.1 nonaka } 363 1.1 nonaka 364 1.1 nonaka /* 365 1.1 nonaka * Return the number of hard disk drives. 366 1.1 nonaka */ 367 1.1 nonaka int 368 1.1 nonaka get_harddrives(void) 369 1.1 nonaka { 370 1.1 nonaka return nefidisks; 371 1.1 nonaka } 372 1.5 nonaka 373 1.5 nonaka int 374 1.5 nonaka efidisk_get_efi_system_partition(int dev, int *partition) 375 1.5 nonaka { 376 1.5 nonaka extern const struct uuid GET_efi; 377 1.5 nonaka const struct efidiskinfo *edi; 378 1.5 nonaka struct biosdisk_partition *part; 379 1.5 nonaka int i, nparts; 380 1.5 nonaka 381 1.5 nonaka edi = efidisk_getinfo(dev); 382 1.5 nonaka if (edi == NULL) 383 1.5 nonaka return ENXIO; 384 1.5 nonaka 385 1.5 nonaka if (edi->type != BIOSDISK_TYPE_HD) 386 1.5 nonaka return ENOTSUP; 387 1.5 nonaka 388 1.8 manu if (biosdisk_readpartition(edi->dev, 0, 0, &part, &nparts)) 389 1.5 nonaka return EIO; 390 1.5 nonaka 391 1.5 nonaka for (i = 0; i < nparts; i++) { 392 1.5 nonaka if (part[i].size == 0) 393 1.5 nonaka continue; 394 1.5 nonaka if (part[i].fstype == FS_UNUSED) 395 1.5 nonaka continue; 396 1.5 nonaka if (guid_is_equal(part[i].guid->guid, &GET_efi)) 397 1.5 nonaka break; 398 1.5 nonaka } 399 1.8 manu dealloc_biosdisk_part(part, nparts); 400 1.5 nonaka if (i == nparts) 401 1.5 nonaka return ENOENT; 402 1.5 nonaka 403 1.5 nonaka *partition = i; 404 1.5 nonaka return 0; 405 1.5 nonaka } 406 1.11 mlelstv 407 1.11 mlelstv void 408 1.11 mlelstv efidisk_getbiosgeom() 409 1.11 mlelstv { 410 1.11 mlelstv BI_ADD(bibg, BTINFO_BIOSGEOM, bibg_len); 411 1.11 mlelstv } 412 1.11 mlelstv 413