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