1 /* ----------------------------------------------------------------------- * 2 * 3 * Copyright 2003-2009 H. Peter Anvin - All Rights Reserved 4 * Copyright 2009-2010 Intel Corporation; author: H. Peter Anvin 5 * Copyright (C) 2010 Shao Miller 6 * 7 * Permission is hereby granted, free of charge, to any person 8 * obtaining a copy of this software and associated documentation 9 * files (the "Software"), to deal in the Software without 10 * restriction, including without limitation the rights to use, 11 * copy, modify, merge, publish, distribute, sublicense, and/or 12 * sell copies of the Software, and to permit persons to whom 13 * the Software is furnished to do so, subject to the following 14 * conditions: 15 * 16 * The above copyright notice and this permission notice shall 17 * be included in all copies or substantial portions of the Software. 18 * 19 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 20 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 21 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 22 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 23 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 24 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 26 * OTHER DEALINGS IN THE SOFTWARE. 27 * 28 * ----------------------------------------------------------------------- */ 29 30 /** 31 * @file disk.c 32 * 33 * Deal with disks and partitions 34 */ 35 36 #include <core.h> 37 #include <dprintf.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <syslinux/disk.h> 42 43 /** 44 * Call int 13h, but with retry on failure. Especially floppies need this. 45 * 46 * @v inreg CPU register settings upon INT call 47 * @v outreg CPU register settings returned by INT call 48 * @ret (int) 0 upon success, -1 upon failure 49 */ 50 int disk_int13_retry(const com32sys_t * inreg, com32sys_t * outreg) 51 { 52 int retry = 6; /* Number of retries */ 53 com32sys_t tmpregs; 54 55 if (!outreg) 56 outreg = &tmpregs; 57 58 while (retry--) { 59 __intcall(0x13, inreg, outreg); 60 if (!(outreg->eflags.l & EFLAGS_CF)) 61 return 0; /* CF=0, OK */ 62 } 63 64 return -1; /* Error */ 65 } 66 67 /** 68 * Query disk parameters and EBIOS availability for a particular disk. 69 * 70 * @v disk The INT 0x13 disk drive number to process 71 * @v diskinfo The structure to save the queried params to 72 * @ret (int) 0 upon success, -1 upon failure 73 */ 74 int disk_get_params(int disk, struct disk_info *const diskinfo) 75 { 76 static com32sys_t inreg, outreg; 77 struct disk_ebios_eparam *eparam; 78 int rv = 0; 79 80 memset(diskinfo, 0, sizeof *diskinfo); 81 diskinfo->disk = disk; 82 diskinfo->bps = SECTOR; 83 84 /* Get EBIOS support */ 85 memset(&inreg, 0, sizeof inreg); 86 inreg.eax.b[1] = 0x41; 87 inreg.ebx.w[0] = 0x55aa; 88 inreg.edx.b[0] = disk; 89 inreg.eflags.b[0] = 0x3; /* CF set */ 90 91 __intcall(0x13, &inreg, &outreg); 92 93 if (!(outreg.eflags.l & EFLAGS_CF) && 94 outreg.ebx.w[0] == 0xaa55 && (outreg.ecx.b[0] & 1)) { 95 diskinfo->ebios = 1; 96 } 97 98 eparam = lmalloc(sizeof *eparam); 99 if (!eparam) 100 return -1; 101 102 /* Get extended disk parameters if ebios == 1 */ 103 if (diskinfo->ebios) { 104 memset(&inreg, 0, sizeof inreg); 105 inreg.eax.b[1] = 0x48; 106 inreg.edx.b[0] = disk; 107 inreg.esi.w[0] = OFFS(eparam); 108 inreg.ds = SEG(eparam); 109 110 memset(eparam, 0, sizeof *eparam); 111 eparam->len = sizeof *eparam; 112 113 __intcall(0x13, &inreg, &outreg); 114 115 if (!(outreg.eflags.l & EFLAGS_CF)) { 116 diskinfo->lbacnt = eparam->lbacnt; 117 if (eparam->bps) 118 diskinfo->bps = eparam->bps; 119 /* 120 * don't think about using geometry data returned by 121 * 48h, as it can differ from 08h a lot ... 122 */ 123 } 124 } 125 /* 126 * Get disk parameters the old way - really only useful for hard 127 * disks, but if we have a partitioned floppy it's actually our best 128 * chance... 129 */ 130 memset(&inreg, 0, sizeof inreg); 131 inreg.eax.b[1] = 0x08; 132 inreg.edx.b[0] = disk; 133 134 __intcall(0x13, &inreg, &outreg); 135 136 if (outreg.eflags.l & EFLAGS_CF) { 137 rv = diskinfo->ebios ? 0 : -1; 138 goto out; 139 } 140 141 diskinfo->spt = 0x3f & outreg.ecx.b[0]; 142 diskinfo->head = 1 + outreg.edx.b[1]; 143 diskinfo->cyl = 1 + (outreg.ecx.b[1] | ((outreg.ecx.b[0] & 0xc0u) << 2)); 144 145 if (diskinfo->spt) 146 diskinfo->cbios = 1; /* Valid geometry */ 147 else { 148 diskinfo->head = 1; 149 diskinfo->spt = 1; 150 diskinfo->cyl = 1; 151 } 152 153 if (!diskinfo->lbacnt) 154 diskinfo->lbacnt = diskinfo->cyl * diskinfo->head * diskinfo->spt; 155 156 out: 157 lfree(eparam); 158 return rv; 159 } 160 161 /** 162 * Fill inreg based on EBIOS addressing properties. 163 * 164 * @v diskinfo The disk drive to read from 165 * @v inreg Register data structure to be filled. 166 * @v lba The logical block address to begin reading at 167 * @v count The number of sectors to read 168 * @v op_code Code to write/read operation 169 * @ret lmalloc'd buf upon success, NULL upon failure 170 */ 171 static void *ebios_setup(const struct disk_info *const diskinfo, com32sys_t *inreg, 172 uint64_t lba, uint8_t count, uint8_t op_code) 173 { 174 static struct disk_ebios_dapa *dapa = NULL; 175 void *buf; 176 177 if (!dapa) { 178 dapa = lmalloc(sizeof *dapa); 179 if (!dapa) 180 return NULL; 181 } 182 183 buf = lmalloc(count * diskinfo->bps); 184 if (!buf) 185 return NULL; 186 187 dapa->len = sizeof(*dapa); 188 dapa->count = count; 189 dapa->off = OFFS(buf); 190 dapa->seg = SEG(buf); 191 dapa->lba = lba; 192 193 inreg->eax.b[1] = op_code; 194 inreg->esi.w[0] = OFFS(dapa); 195 inreg->ds = SEG(dapa); 196 inreg->edx.b[0] = diskinfo->disk; 197 198 return buf; 199 } 200 201 /** 202 * Fill inreg based on CHS addressing properties. 203 * 204 * @v diskinfo The disk drive to read from 205 * @v inreg Register data structure to be filled. 206 * @v lba The logical block address to begin reading at 207 * @v count The number of sectors to read 208 * @v op_code Code to write/read operation 209 * @ret lmalloc'd buf upon success, NULL upon failure 210 */ 211 static void *chs_setup(const struct disk_info *const diskinfo, com32sys_t *inreg, 212 uint64_t lba, uint8_t count, uint8_t op_code) 213 { 214 unsigned int c, h, s, t; 215 void *buf; 216 217 buf = lmalloc(count * diskinfo->bps); 218 if (!buf) 219 return NULL; 220 221 /* 222 * if we passed lba + count check and we get here, that means that 223 * lbacnt was calculated from chs geometry (or faked from 1/1/1), thus 224 * 32bits are perfectly enough and lbacnt corresponds to cylinder 225 * boundary 226 */ 227 s = lba % diskinfo->spt; 228 t = lba / diskinfo->spt; 229 h = t % diskinfo->head; 230 c = t / diskinfo->head; 231 232 memset(inreg, 0, sizeof *inreg); 233 inreg->eax.b[0] = count; 234 inreg->eax.b[1] = op_code; 235 inreg->ecx.b[1] = c; 236 inreg->ecx.b[0] = ((c & 0x300) >> 2) | (s+1); 237 inreg->edx.b[1] = h; 238 inreg->edx.b[0] = diskinfo->disk; 239 inreg->ebx.w[0] = OFFS(buf); 240 inreg->es = SEG(buf); 241 242 return buf; 243 } 244 245 /** 246 * Get disk block(s) and return a malloc'd buffer. 247 * 248 * @v diskinfo The disk drive to read from 249 * @v lba The logical block address to begin reading at 250 * @v count The number of sectors to read 251 * @ret data An allocated buffer with the read data 252 * 253 * Uses the disk number and information from diskinfo. Read count sectors 254 * from drive, starting at lba. Return a new buffer, or NULL upon failure. 255 */ 256 void *disk_read_sectors(const struct disk_info *const diskinfo, uint64_t lba, 257 uint8_t count) 258 { 259 com32sys_t inreg; 260 void *buf; 261 void *data = NULL; 262 uint32_t maxcnt; 263 uint32_t size = 65536; 264 265 maxcnt = (size - diskinfo->bps) / diskinfo->bps; 266 if (!count || count > maxcnt || lba + count > diskinfo->lbacnt) 267 return NULL; 268 269 memset(&inreg, 0, sizeof inreg); 270 271 if (diskinfo->ebios) 272 buf = ebios_setup(diskinfo, &inreg, lba, count, EBIOS_READ_CODE); 273 else 274 buf = chs_setup(diskinfo, &inreg, lba, count, CHS_READ_CODE); 275 276 if (!buf) 277 return NULL; 278 279 if (disk_int13_retry(&inreg, NULL)) 280 goto out; 281 282 data = malloc(count * diskinfo->bps); 283 if (data) 284 memcpy(data, buf, count * diskinfo->bps); 285 out: 286 lfree(buf); 287 return data; 288 } 289 290 /** 291 * Write disk block(s). 292 * 293 * @v diskinfo The disk drive to write to 294 * @v lba The logical block address to begin writing at 295 * @v data The data to write 296 * @v count The number of sectors to write 297 * @ret (int) 0 upon success, -1 upon failure 298 * 299 * Uses the disk number and information from diskinfo. 300 * Write sector(s) to a disk drive, starting at lba. 301 */ 302 int disk_write_sectors(const struct disk_info *const diskinfo, uint64_t lba, 303 const void *data, uint8_t count) 304 { 305 com32sys_t inreg; 306 void *buf; 307 uint32_t maxcnt; 308 uint32_t size = 65536; 309 int rv = -1; 310 311 maxcnt = (size - diskinfo->bps) / diskinfo->bps; 312 if (!count || count > maxcnt || lba + count > diskinfo->lbacnt) 313 return -1; 314 315 memset(&inreg, 0, sizeof inreg); 316 317 if (diskinfo->ebios) 318 buf = ebios_setup(diskinfo, &inreg, lba, count, EBIOS_WRITE_CODE); 319 else 320 buf = chs_setup(diskinfo, &inreg, lba, count, CHS_WRITE_CODE); 321 322 if (!buf) 323 return -1; 324 325 memcpy(buf, data, count * diskinfo->bps); 326 327 if (disk_int13_retry(&inreg, NULL)) 328 goto out; 329 330 rv = 0; /* ok */ 331 out: 332 lfree(buf); 333 return rv; 334 } 335 336 /** 337 * Write disk blocks and verify they were written. 338 * 339 * @v diskinfo The disk drive to write to 340 * @v lba The logical block address to begin writing at 341 * @v buf The data to write 342 * @v count The number of sectors to write 343 * @ret rv 0 upon success, -1 upon failure 344 * 345 * Uses the disk number and information from diskinfo. 346 * Writes sectors to a disk drive starting at lba, then reads them back 347 * to verify they were written correctly. 348 */ 349 int disk_write_verify_sectors(const struct disk_info *const diskinfo, 350 uint64_t lba, const void *buf, uint8_t count) 351 { 352 char *rb; 353 int rv; 354 355 rv = disk_write_sectors(diskinfo, lba, buf, count); 356 if (rv) 357 return rv; /* Write failure */ 358 rb = disk_read_sectors(diskinfo, lba, count); 359 if (!rb) 360 return -1; /* Readback failure */ 361 rv = memcmp(buf, rb, count * diskinfo->bps); 362 free(rb); 363 return rv ? -1 : 0; 364 } 365 366 /** 367 * Dump info about a DOS partition entry 368 * 369 * @v part The 16-byte partition entry to examine 370 */ 371 void disk_dos_part_dump(const struct disk_dos_part_entry *const part) 372 { 373 (void)part; 374 dprintf("Partition status _____ : 0x%.2x\n" 375 "Partition CHS start\n" 376 " Cylinder ___________ : 0x%.4x (%u)\n" 377 " Head _______________ : 0x%.2x (%u)\n" 378 " Sector _____________ : 0x%.2x (%u)\n" 379 "Partition type _______ : 0x%.2x\n" 380 "Partition CHS end\n" 381 " Cylinder ___________ : 0x%.4x (%u)\n" 382 " Head _______________ : 0x%.2x (%u)\n" 383 " Sector _____________ : 0x%.2x (%u)\n" 384 "Partition LBA start __ : 0x%.8x (%u)\n" 385 "Partition LBA count __ : 0x%.8x (%u)\n" 386 "-------------------------------\n", 387 part->active_flag, 388 chs_cylinder(part->start), 389 chs_cylinder(part->start), 390 chs_head(part->start), 391 chs_head(part->start), 392 chs_sector(part->start), 393 chs_sector(part->start), 394 part->ostype, 395 chs_cylinder(part->end), 396 chs_cylinder(part->end), 397 chs_head(part->end), 398 chs_head(part->end), 399 chs_sector(part->end), 400 chs_sector(part->end), 401 part->start_lba, part->start_lba, part->length, part->length); 402 } 403 404 /* Trivial error message output */ 405 static inline void error(const char *msg) 406 { 407 fputs(msg, stderr); 408 } 409 410 /** 411 * This walk-map effectively reverses the little-endian 412 * portions of a GPT disk/partition GUID for a string representation. 413 * There might be a better header for this... 414 */ 415 static const char guid_le_walk_map[] = { 416 3, -1, -1, -1, 0, 417 5, -1, 0, 418 3, -1, 0, 419 2, 1, 0, 420 1, 1, 1, 1, 1, 1 421 }; 422 423 /** 424 * Fill a buffer with a textual GUID representation. 425 * 426 * @v buf Points to a minimum array of 37 chars 427 * @v id The GUID to represent as text 428 * 429 * The buffer must be >= char[37] and will be populated 430 * with an ASCII NUL C string terminator. 431 * Example: 11111111-2222-3333-4444-444444444444 432 * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB 433 */ 434 void guid_to_str(char *buf, const struct guid *const id) 435 { 436 unsigned int i = 0; 437 const char *walker = (const char *)id; 438 439 while (i < sizeof(guid_le_walk_map)) { 440 walker += guid_le_walk_map[i]; 441 if (!guid_le_walk_map[i]) 442 *buf = '-'; 443 else { 444 *buf = ((*walker & 0xF0) >> 4) + '0'; 445 if (*buf > '9') 446 *buf += 'A' - '9' - 1; 447 buf++; 448 *buf = (*walker & 0x0F) + '0'; 449 if (*buf > '9') 450 *buf += 'A' - '9' - 1; 451 } 452 buf++; 453 i++; 454 } 455 *buf = 0; 456 } 457 458 /** 459 * Create a GUID structure from a textual GUID representation. 460 * 461 * @v buf Points to a GUID string to parse 462 * @v id Points to a GUID to be populated 463 * @ret (int) Returns 0 upon success, -1 upon failure 464 * 465 * The input buffer must be >= 32 hexadecimal chars and be 466 * terminated with an ASCII NUL. Returns non-zero on failure. 467 * Example: 11111111-2222-3333-4444-444444444444 468 * Endian: LLLLLLLL-LLLL-LLLL-BBBB-BBBBBBBBBBBB 469 */ 470 int str_to_guid(const char *buf, struct guid *const id) 471 { 472 char guid_seq[sizeof(struct guid) * 2]; 473 unsigned int i = 0; 474 char *walker = (char *)id; 475 476 while (*buf && i < sizeof(guid_seq)) { 477 switch (*buf) { 478 /* Skip these three characters */ 479 case '{': 480 case '}': 481 case '-': 482 break; 483 default: 484 /* Copy something useful to the temp. sequence */ 485 if ((*buf >= '0') && (*buf <= '9')) 486 guid_seq[i] = *buf - '0'; 487 else if ((*buf >= 'A') && (*buf <= 'F')) 488 guid_seq[i] = *buf - 'A' + 10; 489 else if ((*buf >= 'a') && (*buf <= 'f')) 490 guid_seq[i] = *buf - 'a' + 10; 491 else { 492 /* Or not */ 493 error("Illegal character in GUID!\n"); 494 return -1; 495 } 496 i++; 497 } 498 buf++; 499 } 500 /* Check for insufficient valid characters */ 501 if (i < sizeof(guid_seq)) { 502 error("Too few GUID characters!\n"); 503 return -1; 504 } 505 buf = guid_seq; 506 i = 0; 507 while (i < sizeof(guid_le_walk_map)) { 508 if (!guid_le_walk_map[i]) 509 i++; 510 walker += guid_le_walk_map[i]; 511 *walker = *buf << 4; 512 buf++; 513 *walker |= *buf; 514 buf++; 515 i++; 516 } 517 return 0; 518 } 519 520 /** 521 * Display GPT partition details. 522 * 523 * @v gpt_part The GPT partition entry to display 524 */ 525 void disk_gpt_part_dump(const struct disk_gpt_part_entry *const gpt_part) 526 { 527 unsigned int i; 528 char guid_text[37]; 529 530 dprintf("----------------------------------\n" 531 "GPT part. LBA first __ : 0x%.16llx\n" 532 "GPT part. LBA last ___ : 0x%.16llx\n" 533 "GPT part. attribs ____ : 0x%.16llx\n" 534 "GPT part. name _______ : '", 535 gpt_part->lba_first, gpt_part->lba_last, gpt_part->attribs); 536 for (i = 0; i < sizeof(gpt_part->name); i++) { 537 if (gpt_part->name[i]) 538 dprintf("%c", gpt_part->name[i]); 539 } 540 dprintf("'"); 541 guid_to_str(guid_text, &gpt_part->type); 542 dprintf("GPT part. type GUID __ : {%s}\n", guid_text); 543 guid_to_str(guid_text, &gpt_part->uid); 544 dprintf("GPT part. unique ID __ : {%s}\n", guid_text); 545 } 546 547 /** 548 * Display GPT header details. 549 * 550 * @v gpt The GPT header to display 551 */ 552 void disk_gpt_header_dump(const struct disk_gpt_header *const gpt) 553 { 554 char guid_text[37]; 555 556 printf("GPT sig ______________ : '%8.8s'\n" 557 "GPT major revision ___ : 0x%.4x\n" 558 "GPT minor revision ___ : 0x%.4x\n" 559 "GPT header size ______ : 0x%.8x\n" 560 "GPT header checksum __ : 0x%.8x\n" 561 "GPT reserved _________ : '%4.4s'\n" 562 "GPT LBA current ______ : 0x%.16llx\n" 563 "GPT LBA alternative __ : 0x%.16llx\n" 564 "GPT LBA first usable _ : 0x%.16llx\n" 565 "GPT LBA last usable __ : 0x%.16llx\n" 566 "GPT LBA part. table __ : 0x%.16llx\n" 567 "GPT partition count __ : 0x%.8x\n" 568 "GPT partition size ___ : 0x%.8x\n" 569 "GPT part. table chksum : 0x%.8x\n", 570 gpt->sig, 571 gpt->rev.fields.major, 572 gpt->rev.fields.minor, 573 gpt->hdr_size, 574 gpt->chksum, 575 gpt->reserved1, 576 gpt->lba_cur, 577 gpt->lba_alt, 578 gpt->lba_first_usable, 579 gpt->lba_last_usable, 580 gpt->lba_table, gpt->part_count, gpt->part_size, gpt->table_chksum); 581 guid_to_str(guid_text, &gpt->disk_guid); 582 printf("GPT disk GUID ________ : {%s}\n", guid_text); 583 } 584