1 /** @file 2 Floppy Peim to support Recovery function from Floppy device. 3 4 Copyright (c) 2006 - 2014, Intel Corporation. All rights reserved.<BR> 5 6 This program and the accompanying materials 7 are licensed and made available under the terms and conditions 8 of the BSD License which accompanies this distribution. The 9 full text of the license may be found at 10 http://opensource.org/licenses/bsd-license.php 11 12 THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, 13 WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. 14 15 **/ 16 17 18 #include "FloppyPeim.h" 19 20 21 PEI_DMA_TABLE mRegisterTable[] = { 22 // 23 // DMA2: Clear Byte Ptr, Enable 24 // 25 { 26 R_8237_DMA_CBPR_CH4_7, 27 0 28 }, 29 { 30 R_8237_DMA_COMMAND_CH4_7, 31 0 32 }, 33 // 34 // DMA1: Clear Byte Ptr, Enable 35 // 36 { 37 R_8237_DMA_CBPR_CH0_3, 38 0 39 }, 40 { 41 R_8237_DMA_COMMAND_CH0_3, 42 0 43 }, 44 // 45 // Configure Channel 4 for Cascade Mode 46 // Clear DMA Request and enable DREQ 47 // 48 { 49 R_8237_DMA_CHMODE_CH4_7, 50 V_8237_DMA_CHMODE_CASCADE | 0 51 }, 52 { 53 R_8237_DMA_STA_CH4_7, 54 0 55 }, 56 { 57 R_8237_DMA_WRSMSK_CH4_7, 58 0 59 }, 60 // 61 // Configure DMA1 (Channels 0-3) for Single Mode 62 // Clear DMA Request and enable DREQ 63 // 64 { 65 R_8237_DMA_CHMODE_CH0_3, 66 V_8237_DMA_CHMODE_SINGLE | 0 67 }, 68 { 69 R_8237_DMA_STA_CH0_3, 70 0 71 }, 72 { 73 R_8237_DMA_WRSMSK_CH0_3, 74 0 75 }, 76 { 77 R_8237_DMA_CHMODE_CH0_3, 78 V_8237_DMA_CHMODE_SINGLE | 1 79 }, 80 { 81 R_8237_DMA_STA_CH0_3, 82 1 83 }, 84 { 85 R_8237_DMA_WRSMSK_CH0_3, 86 1 87 }, 88 { 89 R_8237_DMA_CHMODE_CH0_3, 90 V_8237_DMA_CHMODE_SINGLE | 2 91 }, 92 { 93 R_8237_DMA_STA_CH0_3, 94 2 95 }, 96 { 97 R_8237_DMA_WRSMSK_CH0_3, 98 2 99 }, 100 { 101 R_8237_DMA_CHMODE_CH0_3, 102 V_8237_DMA_CHMODE_SINGLE | 3 103 }, 104 { 105 R_8237_DMA_STA_CH0_3, 106 3 107 }, 108 { 109 R_8237_DMA_WRSMSK_CH0_3, 110 3 111 }, 112 // 113 // Configure DMA2 (Channels 5-7) for Single Mode 114 // Clear DMA Request and enable DREQ 115 // 116 { 117 R_8237_DMA_CHMODE_CH4_7, 118 V_8237_DMA_CHMODE_SINGLE | 1 119 }, 120 { 121 R_8237_DMA_STA_CH4_7, 122 1 123 }, 124 { 125 R_8237_DMA_WRSMSK_CH4_7, 126 1 127 }, 128 { 129 R_8237_DMA_CHMODE_CH4_7, 130 V_8237_DMA_CHMODE_SINGLE | 2 131 }, 132 { 133 R_8237_DMA_STA_CH4_7, 134 2 135 }, 136 { 137 R_8237_DMA_WRSMSK_CH4_7, 138 2 139 }, 140 { 141 R_8237_DMA_CHMODE_CH4_7, 142 V_8237_DMA_CHMODE_SINGLE | 3 143 }, 144 { 145 R_8237_DMA_STA_CH4_7, 146 3 147 }, 148 { 149 R_8237_DMA_WRSMSK_CH4_7, 150 3 151 } 152 }; 153 154 // 155 // Table of diskette parameters of various diskette types 156 // 157 DISKET_PARA_TABLE DiskPara[9] = { 158 { 159 0x09, 160 0x50, 161 0xff, 162 0x2, 163 0x27, 164 0x4, 165 0x25, 166 0x14, 167 0x80 168 }, 169 { 170 0x09, 171 0x2a, 172 0xff, 173 0x2, 174 0x27, 175 0x4, 176 0x25, 177 0x0f, 178 0x40 179 }, 180 { 181 0x0f, 182 0x54, 183 0xff, 184 0x2, 185 0x4f, 186 0x4, 187 0x25, 188 0x0f, 189 0x0 190 }, 191 { 192 0x09, 193 0x50, 194 0xff, 195 0x2, 196 0x4f, 197 0x4, 198 0x25, 199 0x0f, 200 0x80 201 }, 202 { 203 0x09, 204 0x2a, 205 0xff, 206 0x2, 207 0x4f, 208 0x4, 209 0x25, 210 0x0f, 211 0x80 212 }, 213 { 214 0x12, 215 0x1b, 216 0xff, 217 0x2, 218 0x4f, 219 0x4, 220 0x25, 221 0x0f, 222 0x0 223 }, 224 { 225 0x09, 226 0x2a, 227 0xff, 228 0x2, 229 0x4f, 230 0x4, 231 0x25, 232 0x0f, 233 0x80 234 }, 235 { 236 0x12, 237 0x1b, 238 0xff, 239 0x2, 240 0x4f, 241 0x4, 242 0x25, 243 0x0f, 244 0x0 245 }, 246 { 247 0x24, 248 0x1b, 249 0xff, 250 0x2, 251 0x4f, 252 0x4, 253 0x25, 254 0x0f, 255 0xc0 256 } 257 }; 258 259 // 260 // Byte per sector corresponding to various device types. 261 // 262 UINTN BytePerSector[6] = { 0, 256, 512, 1024, 2048, 4096 }; 263 264 FDC_BLK_IO_DEV mBlockIoDevTemplate = { 265 FDC_BLK_IO_DEV_SIGNATURE, 266 { 267 FdcGetNumberOfBlockDevices, 268 FdcGetBlockDeviceMediaInfo, 269 FdcReadBlocks, 270 }, 271 { 272 (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST), 273 &gEfiPeiVirtualBlockIoPpiGuid, 274 NULL 275 }, 276 0, 277 {{0}} 278 }; 279 280 /** 281 Wait and check if bits for DIO and RQM of FDC Main Status Register 282 indicates FDC is ready for read or write. 283 284 Before writing to FDC or reading from FDC, the Host must examine 285 the bit7(RQM) and bit6(DIO) of the Main Status Register. 286 That is to say: 287 Command bytes can not be written to Data Register unless RQM is 1 and DIO is 0. 288 Result bytes can not be read from Data Register unless RQM is 1 and DIO is 1. 289 290 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV. 291 @param DataIn Indicates data input or output. 292 TRUE means input. 293 FALSE means output. 294 @param TimeoutInMseconds Timeout value to wait. 295 296 @retval EFI_SUCCESS FDC is ready. 297 @retval EFI_NOT_READY FDC is not ready within the specified time period. 298 299 **/ 300 EFI_STATUS 301 FdcDRQReady ( 302 IN FDC_BLK_IO_DEV *FdcBlkIoDev, 303 IN BOOLEAN DataIn, 304 IN UINTN TimeoutInMseconds 305 ) 306 { 307 UINTN Delay; 308 UINT8 StatusRegister; 309 UINT8 BitInOut; 310 311 // 312 // Check bit6 of Main Status Register. 313 // 314 BitInOut = 0; 315 if (DataIn) { 316 BitInOut = BIT6; 317 } 318 319 Delay = ((TimeoutInMseconds * STALL_1_MSECOND) / FDC_CHECK_INTERVAL) + 1; 320 do { 321 StatusRegister = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR)); 322 if ((StatusRegister & MSR_RQM) == MSR_RQM && (StatusRegister & MSR_DIO) == BitInOut) { 323 // 324 // FDC is ready 325 // 326 break; 327 } 328 329 MicroSecondDelay (FDC_SHORT_DELAY); 330 } while (--Delay > 0); 331 332 if (Delay == 0) { 333 // 334 // FDC is not ready within the specified time period 335 // 336 return EFI_NOT_READY; 337 } 338 339 return EFI_SUCCESS; 340 } 341 342 /** 343 Read a byte from FDC data register. 344 345 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV. 346 @param Pointer Pointer to buffer to hold data read from FDC. 347 348 @retval EFI_SUCCESS Byte successfully read. 349 @retval EFI_DEVICE_ERROR FDC is not ready. 350 351 **/ 352 EFI_STATUS 353 DataInByte ( 354 IN FDC_BLK_IO_DEV *FdcBlkIoDev, 355 OUT UINT8 *Pointer 356 ) 357 { 358 UINT8 Data; 359 360 // 361 // Wait for 1ms and detect the FDC is ready to be read 362 // 363 if (FdcDRQReady (FdcBlkIoDev, TRUE, 1) != EFI_SUCCESS) { 364 // 365 // FDC is not ready. 366 // 367 return EFI_DEVICE_ERROR; 368 } 369 370 Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DTR)); 371 MicroSecondDelay (FDC_SHORT_DELAY); 372 *Pointer = Data; 373 374 return EFI_SUCCESS; 375 } 376 377 /** 378 Write a byte to FDC data register. 379 380 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV. 381 @param Pointer Pointer to data to write. 382 383 @retval EFI_SUCCESS Byte successfully written. 384 @retval EFI_DEVICE_ERROR FDC is not ready. 385 386 **/ 387 EFI_STATUS 388 DataOutByte ( 389 IN FDC_BLK_IO_DEV *FdcBlkIoDev, 390 IN UINT8 *Pointer 391 ) 392 { 393 UINT8 Data; 394 395 // 396 // Wait for 1ms and detect the FDC is ready to be written 397 // 398 if (FdcDRQReady (FdcBlkIoDev, FALSE, 1) != EFI_SUCCESS) { 399 // 400 // FDC is not ready. 401 // 402 return EFI_DEVICE_ERROR; 403 } 404 405 Data = *Pointer; 406 IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DTR), Data); 407 MicroSecondDelay (FDC_SHORT_DELAY); 408 409 return EFI_SUCCESS; 410 } 411 412 /** 413 Get Sts0 and Pcn status from FDC 414 415 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV 416 @param Sts0 Value of Sts0 417 @param Pcn Value of Pcn 418 419 @retval EFI_SUCCESS Successfully retrieved status value of Sts0 and Pcn. 420 @retval EFI_DEVICE_ERROR Fail to send SENSE_INT_STATUS_CMD. 421 @retval EFI_DEVICE_ERROR Fail to read Sts0. 422 @retval EFI_DEVICE_ERROR Fail to read Pcn. 423 424 **/ 425 EFI_STATUS 426 SenseIntStatus ( 427 IN FDC_BLK_IO_DEV *FdcBlkIoDev, 428 OUT UINT8 *Sts0, 429 OUT UINT8 *Pcn 430 ) 431 { 432 UINT8 Command; 433 434 Command = SENSE_INT_STATUS_CMD; 435 436 if (DataOutByte (FdcBlkIoDev, &Command) != EFI_SUCCESS) { 437 return EFI_DEVICE_ERROR; 438 } 439 440 if (DataInByte (FdcBlkIoDev, Sts0) != EFI_SUCCESS) { 441 return EFI_DEVICE_ERROR; 442 } 443 444 if (DataInByte (FdcBlkIoDev, Pcn) != EFI_SUCCESS) { 445 return EFI_DEVICE_ERROR; 446 } 447 448 return EFI_SUCCESS; 449 } 450 451 /** 452 Issue Specify command. 453 454 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV. 455 456 @retval EFI_SUCCESS Specify command successfully issued. 457 @retval EFI_DEVICE_ERROR FDC device has errors. 458 459 **/ 460 EFI_STATUS 461 Specify ( 462 IN FDC_BLK_IO_DEV *FdcBlkIoDev 463 ) 464 { 465 FDC_SPECIFY_CMD Command; 466 UINTN Index; 467 UINT8 *Pointer; 468 469 ZeroMem (&Command, sizeof (FDC_SPECIFY_CMD)); 470 Command.CommandCode = SPECIFY_CMD; 471 // 472 // set SRT, HUT 473 // 474 Command.SrtHut = 0xdf; 475 // 476 // 0xdf; 477 // set HLT and DMA 478 // 479 Command.HltNd = 0x02; 480 481 Pointer = (UINT8 *) (&Command); 482 for (Index = 0; Index < sizeof (FDC_SPECIFY_CMD); Index++) { 483 if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) { 484 return EFI_DEVICE_ERROR; 485 } 486 } 487 488 return EFI_SUCCESS; 489 } 490 491 /** 492 Wait until busy bit is cleared. 493 494 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV. 495 @param DevPos Position of FDC (Driver A or B) 496 @param TimeoutInMseconds Timeout value to wait. 497 498 @retval EFI_SUCCESS Busy bit has been cleared before timeout. 499 @retval EFI_TIMEOUT Time goes out before busy bit is cleared. 500 501 **/ 502 EFI_STATUS 503 FdcWaitForBSYClear ( 504 IN FDC_BLK_IO_DEV *FdcBlkIoDev, 505 IN UINT8 DevPos, 506 IN UINTN TimeoutInMseconds 507 ) 508 { 509 UINTN Delay; 510 UINT8 StatusRegister; 511 UINT8 Mask; 512 513 // 514 // How to determine drive and command are busy or not: by the bits of Main Status Register 515 // bit0: Drive 0 busy (drive A) 516 // bit1: Drive 1 busy (drive B) 517 // bit4: Command busy 518 // 519 // set mask: for drive A set bit0 & bit4; for drive B set bit1 & bit4 520 // 521 Mask = (UINT8) ((DevPos == 0 ? MSR_DAB : MSR_DBB) | MSR_CB); 522 523 Delay = ((TimeoutInMseconds * STALL_1_MSECOND) / FDC_CHECK_INTERVAL) + 1; 524 525 do { 526 StatusRegister = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR)); 527 528 if ((StatusRegister & Mask) == 0x00) { 529 // 530 // not busy 531 // 532 break; 533 } 534 535 MicroSecondDelay (FDC_SHORT_DELAY); 536 } while (--Delay > 0); 537 538 if (Delay == 0) { 539 return EFI_TIMEOUT; 540 } 541 542 return EFI_SUCCESS; 543 } 544 545 /** 546 Reset FDC device. 547 548 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV 549 @param DevPos Index of FDC device. 550 551 @retval EFI_SUCCESS FDC device successfully reset. 552 @retval EFI_DEVICE_ERROR Fail to reset FDC device. 553 554 **/ 555 EFI_STATUS 556 FdcReset ( 557 IN FDC_BLK_IO_DEV *FdcBlkIoDev, 558 IN UINT8 DevPos 559 ) 560 { 561 UINT8 Data; 562 UINT8 Sts0; 563 UINT8 Pcn; 564 UINTN Index; 565 566 // 567 // Reset specified Floppy Logic Drive according to Fdd -> Disk 568 // Set Digital Output Register(DOR) to do reset work 569 // bit0 & bit1 of DOR : Drive Select 570 // bit2 : Reset bit 571 // bit3 : DMA and Int bit 572 // Reset : A "0" written to bit2 resets the FDC, this reset will remain active until 573 // a "1" is written to this bit. 574 // Reset step 1: 575 // use bit0 & bit1 to select the logic drive 576 // write "0" to bit2 577 // 578 Data = 0x0; 579 Data = (UINT8) (Data | (SELECT_DRV & DevPos)); 580 IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data); 581 582 // 583 // Wait some time, at least 120us. 584 // 585 MicroSecondDelay (FDC_RESET_DELAY); 586 // 587 // Reset step 2: 588 // write "1" to bit2 589 // write "1" to bit3 : enable DMA 590 // 591 Data |= 0x0C; 592 IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data); 593 594 MicroSecondDelay (FDC_RESET_DELAY); 595 596 // 597 // Wait until specified floppy logic drive is not busy 598 // 599 if (FdcWaitForBSYClear (FdcBlkIoDev, DevPos, 1) != EFI_SUCCESS) { 600 return EFI_DEVICE_ERROR; 601 } 602 // 603 // Set the Transfer Data Rate 604 // 605 IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_CCR), 0x0); 606 607 MicroSecondDelay (FDC_MEDIUM_DELAY); 608 609 // 610 // Issue Sense interrupt command for each drive (totally 4 drives) 611 // 612 for (Index = 0; Index < 4; Index++) { 613 if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) { 614 return EFI_DEVICE_ERROR; 615 } 616 } 617 // 618 // Issue Specify command 619 // 620 if (Specify (FdcBlkIoDev) != EFI_SUCCESS) { 621 return EFI_DEVICE_ERROR; 622 } 623 624 return EFI_SUCCESS; 625 } 626 627 /** 628 Turn on the motor of floppy drive. 629 630 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV. 631 @param Info Information of floppy device. 632 633 @retval EFI_SUCCESS Motor is successfully turned on. 634 @retval EFI_SUCCESS Motor is already on. 635 @retval EFI_DEVICE_ERROR Busy bit of FDC cannot be cleared. 636 637 **/ 638 EFI_STATUS 639 MotorOn ( 640 IN FDC_BLK_IO_DEV *FdcBlkIoDev, 641 IN OUT PEI_FLOPPY_DEVICE_INFO *Info 642 ) 643 { 644 UINT8 Data; 645 UINT8 DevPos; 646 647 // 648 // Control of the floppy drive motors is a big pain. If motor is off, you have to turn it 649 // on first. But you can not leave the motor on all the time, since that would wear out the 650 // disk. On the other hand, if you turn the motor off after each operation, the system performance 651 // will be awful. The compromise used in this driver is to leave the motor on for 2 seconds after 652 // each operation. If a new operation is started in that interval(2s), the motor need not be 653 // turned on again. If no new operation is started, a timer goes off and the motor is turned off. 654 // 655 DevPos = Info->DevPos; 656 657 // 658 // If the Motor is already on, just return EFI_SUCCESS. 659 // 660 if (Info->MotorOn) { 661 return EFI_SUCCESS; 662 } 663 // 664 // The drive's motor is off, so need turn it on. 665 // First check if command and drive are busy or not. 666 // 667 if (FdcWaitForBSYClear (FdcBlkIoDev, DevPos, 1) != EFI_SUCCESS) { 668 return EFI_DEVICE_ERROR; 669 } 670 // 671 // for drive A: 1CH, drive B: 2DH 672 // 673 Data = 0x0C; 674 Data = (UINT8) (Data | (SELECT_DRV & DevPos)); 675 if (DevPos == 0) { 676 Data |= DRVA_MOTOR_ON; 677 } else { 678 Data |= DRVB_MOTOR_ON; 679 } 680 681 Info->MotorOn = FALSE; 682 683 // 684 // Turn on the motor and wait for some time to ensure it takes effect. 685 // 686 IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data); 687 MicroSecondDelay (FDC_LONG_DELAY); 688 689 Info->MotorOn = TRUE; 690 691 return EFI_SUCCESS; 692 } 693 694 /** 695 Turn off the motor of floppy drive. 696 697 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV. 698 @param Info Information of floppy device. 699 700 **/ 701 VOID 702 MotorOff ( 703 IN FDC_BLK_IO_DEV *FdcBlkIoDev, 704 IN OUT PEI_FLOPPY_DEVICE_INFO *Info 705 ) 706 { 707 UINT8 Data; 708 UINT8 DevPos; 709 710 DevPos = Info->DevPos; 711 712 if (!Info->MotorOn) { 713 return; 714 } 715 // 716 // The motor is on, so need motor off 717 // 718 Data = 0x0C; 719 Data = (UINT8) (Data | (SELECT_DRV & DevPos)); 720 721 IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data); 722 MicroSecondDelay (FDC_SHORT_DELAY); 723 724 Info->MotorOn = FALSE; 725 } 726 727 /** 728 Recalibrate the FDC device. 729 730 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV. 731 @param Info Information of floppy device. 732 733 @retval EFI_SUCCESS FDC successfully recalibrated. 734 @retval EFI_DEVICE_ERROR Fail to send RECALIBRATE_CMD. 735 @retval EFI_DEVICE_ERROR Fail to get status value of Sts0 and Pcn. 736 @retval EFI_DEVICE_ERROR Fail to recalibrate FDC device. 737 738 **/ 739 EFI_STATUS 740 Recalibrate ( 741 IN FDC_BLK_IO_DEV *FdcBlkIoDev, 742 IN OUT PEI_FLOPPY_DEVICE_INFO *Info 743 ) 744 { 745 FDC_COMMAND_PACKET2 Command; 746 UINTN Index; 747 UINT8 Sts0; 748 UINT8 Pcn; 749 UINT8 *Pointer; 750 UINT8 Count; 751 UINT8 DevPos; 752 753 DevPos = Info->DevPos; 754 755 // 756 // We would try twice. 757 // 758 Count = 2; 759 while (Count > 0) { 760 ZeroMem (&Command, sizeof (FDC_COMMAND_PACKET2)); 761 Command.CommandCode = RECALIBRATE_CMD; 762 // 763 // drive select 764 // 765 if (DevPos == 0) { 766 Command.DiskHeadSel = 0; 767 } else { 768 Command.DiskHeadSel = 1; 769 } 770 771 Pointer = (UINT8 *) (&Command); 772 for (Index = 0; Index < sizeof (FDC_COMMAND_PACKET2); Index++) { 773 if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) { 774 return EFI_DEVICE_ERROR; 775 } 776 } 777 778 MicroSecondDelay (FDC_RECALIBRATE_DELAY); 779 780 if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) { 781 return EFI_DEVICE_ERROR; 782 } 783 784 if ((Sts0 & 0xf0) == BIT5 && Pcn == 0) { 785 // 786 // Recalibration is successful. 787 // 788 Info->Pcn = 0; 789 Info->NeedRecalibrate = FALSE; 790 791 return EFI_SUCCESS; 792 } else { 793 // 794 // Recalibration is not successful. Try again. 795 // If trial is used out, return EFI_DEVICE_ERROR. 796 // 797 Count--; 798 if (Count == 0) { 799 return EFI_DEVICE_ERROR; 800 } 801 } 802 } 803 804 return EFI_SUCCESS; 805 } 806 807 /** 808 Seek for the cylinder according to given LBA. 809 810 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV. 811 @param Info Information of floppy device. 812 @param Lba LBA for which to seek for cylinder. 813 814 @retval EFI_SUCCESS Successfully moved to the destination cylinder. 815 @retval EFI_SUCCESS Destination cylinder is just the present cylinder. 816 @retval EFI_DEVICE_ERROR Fail to move to the destination cylinder. 817 818 **/ 819 EFI_STATUS 820 Seek ( 821 IN FDC_BLK_IO_DEV *FdcBlkIoDev, 822 IN OUT PEI_FLOPPY_DEVICE_INFO *Info, 823 IN EFI_PEI_LBA Lba 824 ) 825 { 826 FDC_SEEK_CMD Command; 827 DISKET_PARA_TABLE *Para; 828 UINT8 EndOfTrack; 829 UINT8 Head; 830 UINT8 Cylinder; 831 UINT8 Sts0; 832 UINT8 *Pointer; 833 UINT8 Pcn; 834 UINTN Index; 835 UINT8 Gap; 836 UINT8 DevPos; 837 838 DevPos = Info->DevPos; 839 if (Info->NeedRecalibrate) { 840 if (Recalibrate (FdcBlkIoDev, Info) != EFI_SUCCESS) { 841 return EFI_DEVICE_ERROR; 842 } 843 // 844 // Recalibrate Success 845 // 846 Info->NeedRecalibrate = FALSE; 847 } 848 849 // 850 // Get the base of disk parameter information corresponding to its type. 851 // 852 Para = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type); 853 EndOfTrack = Para->EndOfTrack; 854 // 855 // Calculate cylinder based on Lba and EOT 856 // 857 Cylinder = (UINT8) ((UINTN) Lba / EndOfTrack / 2); 858 859 // 860 // If the dest cylinder is the present cylinder, unnecessary to do the seek operation 861 // 862 if (Info->Pcn == Cylinder) { 863 return EFI_SUCCESS; 864 } 865 866 // 867 // Calculate the head : 0 or 1 868 // 869 Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2); 870 871 ZeroMem (&Command, sizeof (FDC_SEEK_CMD)); 872 Command.CommandCode = SEEK_CMD; 873 if (DevPos == 0) { 874 Command.DiskHeadSel = 0; 875 } else { 876 Command.DiskHeadSel = 1; 877 } 878 879 // 880 // Send command to move to destination cylinder. 881 // 882 Command.DiskHeadSel = (UINT8) (Command.DiskHeadSel | (Head << 2)); 883 Command.NewCylinder = Cylinder; 884 885 Pointer = (UINT8 *) (&Command); 886 for (Index = 0; Index < sizeof (FDC_SEEK_CMD); Index++) { 887 if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) { 888 return EFI_DEVICE_ERROR; 889 } 890 } 891 892 MicroSecondDelay (FDC_SHORT_DELAY); 893 894 // 895 // Calculate waiting time, which is proportional to the gap between destination 896 // cylinder and present cylinder. 897 // 898 if (Info->Pcn > Cylinder) { 899 Gap = (UINT8) (Info->Pcn - Cylinder); 900 } else { 901 Gap = (UINT8) (Cylinder - Info->Pcn); 902 } 903 904 MicroSecondDelay ((Gap + 1) * FDC_LONG_DELAY); 905 906 // 907 // Confirm if the new cylinder is the destination and status is correct. 908 // 909 if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) { 910 return EFI_DEVICE_ERROR; 911 } 912 913 if ((Sts0 & 0xf0) == BIT5) { 914 Info->Pcn = Command.NewCylinder; 915 Info->NeedRecalibrate = FALSE; 916 return EFI_SUCCESS; 917 } else { 918 Info->NeedRecalibrate = TRUE; 919 return EFI_DEVICE_ERROR; 920 } 921 } 922 923 /** 924 Check if diskette is changed. 925 926 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV 927 @param Info Information of floppy device. 928 929 @retval EFI_SUCCESS Diskette is not changed. 930 @retval EFI_MEDIA_CHANGED Diskette is changed. 931 @retval EFI_NO_MEDIA No diskette. 932 @retval EFI_DEVICE_ERROR Fail to do the seek or recalibrate operation. 933 934 **/ 935 EFI_STATUS 936 DisketChanged ( 937 IN FDC_BLK_IO_DEV *FdcBlkIoDev, 938 IN OUT PEI_FLOPPY_DEVICE_INFO *Info 939 ) 940 { 941 EFI_STATUS Status; 942 UINT8 Data; 943 944 // 945 // Check change line 946 // 947 Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DIR)); 948 949 MicroSecondDelay (FDC_SHORT_DELAY); 950 951 if ((Data & DIR_DCL) == DIR_DCL) { 952 if (Info->Pcn != 0) { 953 Status = Recalibrate (FdcBlkIoDev, Info); 954 } else { 955 Status = Seek (FdcBlkIoDev, Info, 0x30); 956 } 957 958 if (Status != EFI_SUCCESS) { 959 // 960 // Fail to do the seek or recalibrate operation 961 // 962 return EFI_DEVICE_ERROR; 963 } 964 965 Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DIR)); 966 967 MicroSecondDelay (FDC_SHORT_DELAY); 968 969 if ((Data & DIR_DCL) == DIR_DCL) { 970 return EFI_NO_MEDIA; 971 } 972 973 return EFI_MEDIA_CHANGED; 974 } 975 976 return EFI_SUCCESS; 977 } 978 979 /** 980 Detects if FDC device exists. 981 982 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV 983 @param Info Information of floppy device. 984 @param MediaInfo Information of floppy media. 985 986 @retval TRUE FDC device exists and is working properly. 987 @retval FALSE FDC device does not exist or cannot work properly. 988 989 **/ 990 BOOLEAN 991 DiscoverFdcDevice ( 992 IN FDC_BLK_IO_DEV *FdcBlkIoDev, 993 IN OUT PEI_FLOPPY_DEVICE_INFO *Info, 994 OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo 995 ) 996 { 997 EFI_STATUS Status; 998 DISKET_PARA_TABLE *Para; 999 1000 Status = MotorOn (FdcBlkIoDev, Info); 1001 if (Status != EFI_SUCCESS) { 1002 return FALSE; 1003 } 1004 1005 Status = Recalibrate (FdcBlkIoDev, Info); 1006 1007 if (Status != EFI_SUCCESS) { 1008 MotorOff (FdcBlkIoDev, Info); 1009 return FALSE; 1010 } 1011 // 1012 // Set Media Parameter 1013 // 1014 MediaInfo->DeviceType = LegacyFloppy; 1015 MediaInfo->MediaPresent = TRUE; 1016 1017 // 1018 // Check Media 1019 // 1020 Status = DisketChanged (FdcBlkIoDev, Info); 1021 if (Status == EFI_NO_MEDIA) { 1022 // 1023 // No diskette in floppy. 1024 // 1025 MediaInfo->MediaPresent = FALSE; 1026 } else if (Status != EFI_MEDIA_CHANGED && Status != EFI_SUCCESS) { 1027 // 1028 // EFI_DEVICE_ERROR 1029 // 1030 MotorOff (FdcBlkIoDev, Info); 1031 return FALSE; 1032 } 1033 1034 MotorOff (FdcBlkIoDev, Info); 1035 1036 // 1037 // Get the base of disk parameter information corresponding to its type. 1038 // 1039 Para = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type); 1040 1041 MediaInfo->BlockSize = BytePerSector[Para->Number]; 1042 MediaInfo->LastBlock = Para->EndOfTrack * 2 * (Para->MaxTrackNum + 1) - 1; 1043 1044 return TRUE; 1045 } 1046 1047 /** 1048 Enumerate floppy device 1049 1050 @param FdcBlkIoDev Instance of floppy device controller 1051 1052 @return Number of FDC devices. 1053 1054 **/ 1055 UINT8 1056 FdcEnumeration ( 1057 IN FDC_BLK_IO_DEV *FdcBlkIoDev 1058 ) 1059 { 1060 UINT8 DevPos; 1061 UINT8 DevNo; 1062 EFI_PEI_BLOCK_IO_MEDIA MediaInfo; 1063 EFI_STATUS Status; 1064 1065 DevNo = 0; 1066 1067 // 1068 // DevPos=0 means Drive A, 1 means Drive B. 1069 // 1070 for (DevPos = 0; DevPos < 2; DevPos++) { 1071 // 1072 // Detecting device presence 1073 // 1074 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_PC_PRESENCE_DETECT); 1075 1076 // 1077 // Reset FDC 1078 // 1079 Status = FdcReset (FdcBlkIoDev, DevPos); 1080 1081 if (EFI_ERROR (Status)) { 1082 continue; 1083 } 1084 1085 FdcBlkIoDev->DeviceInfo[DevPos].DevPos = DevPos; 1086 FdcBlkIoDev->DeviceInfo[DevPos].Pcn = 0; 1087 FdcBlkIoDev->DeviceInfo[DevPos].MotorOn = FALSE; 1088 FdcBlkIoDev->DeviceInfo[DevPos].NeedRecalibrate = TRUE; 1089 FdcBlkIoDev->DeviceInfo[DevPos].Type = FdcType1440K1440K; 1090 1091 // 1092 // Discover FDC device 1093 // 1094 if (DiscoverFdcDevice (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DevPos]), &MediaInfo)) { 1095 FdcBlkIoDev->DeviceInfo[DevNo].DevPos = DevPos; 1096 1097 FdcBlkIoDev->DeviceInfo[DevNo].Pcn = FdcBlkIoDev->DeviceInfo[DevPos].Pcn; 1098 FdcBlkIoDev->DeviceInfo[DevNo].MotorOn = FdcBlkIoDev->DeviceInfo[DevPos].MotorOn; 1099 FdcBlkIoDev->DeviceInfo[DevNo].NeedRecalibrate = FdcBlkIoDev->DeviceInfo[DevPos].NeedRecalibrate; 1100 FdcBlkIoDev->DeviceInfo[DevNo].Type = FdcBlkIoDev->DeviceInfo[DevPos].Type; 1101 1102 CopyMem ( 1103 &(FdcBlkIoDev->DeviceInfo[DevNo].MediaInfo), 1104 &MediaInfo, 1105 sizeof (EFI_PEI_BLOCK_IO_MEDIA) 1106 ); 1107 1108 DevNo++; 1109 } else { 1110 // 1111 // Assume controller error 1112 // 1113 REPORT_STATUS_CODE ( 1114 EFI_ERROR_CODE | EFI_ERROR_MINOR, 1115 EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_EC_CONTROLLER_ERROR 1116 ); 1117 } 1118 } 1119 1120 FdcBlkIoDev->DeviceCount = DevNo; 1121 return DevNo; 1122 } 1123 1124 /** 1125 Checks result reflected by FDC_RESULT_PACKET. 1126 1127 @param Result FDC_RESULT_PACKET read from FDC after certain operation. 1128 @param Info Information of floppy device. 1129 1130 @retval EFI_SUCCESS Result is healthy. 1131 @retval EFI_DEVICE_ERROR Result is not healthy. 1132 1133 **/ 1134 EFI_STATUS 1135 CheckResult ( 1136 IN FDC_RESULT_PACKET *Result, 1137 OUT PEI_FLOPPY_DEVICE_INFO *Info 1138 ) 1139 { 1140 if ((Result->Status0 & STS0_IC) != IC_NT) { 1141 if ((Result->Status0 & STS0_SE) == BIT5) { 1142 // 1143 // Seek error 1144 // 1145 Info->NeedRecalibrate = TRUE; 1146 } 1147 1148 Info->NeedRecalibrate = TRUE; 1149 return EFI_DEVICE_ERROR; 1150 } 1151 // 1152 // Check Status Register1 1153 // 1154 if ((Result->Status1 & (STS1_EN | STS1_DE | STS1_OR | STS1_ND | STS1_NW | STS1_MA)) != 0) { 1155 Info->NeedRecalibrate = TRUE; 1156 return EFI_DEVICE_ERROR; 1157 } 1158 // 1159 // Check Status Register2 1160 // 1161 if ((Result->Status2 & (STS2_CM | STS2_DD | STS2_WC | STS2_BC | STS2_MD)) != 0) { 1162 Info->NeedRecalibrate = TRUE; 1163 return EFI_DEVICE_ERROR; 1164 } 1165 1166 return EFI_SUCCESS; 1167 } 1168 1169 /** 1170 Fill parameters for command packet. 1171 1172 @param Info Information of floppy device. 1173 @param Lba Logical block address. 1174 @param Command Command for which for fill parameters. 1175 1176 **/ 1177 VOID 1178 FillPara ( 1179 IN PEI_FLOPPY_DEVICE_INFO *Info, 1180 IN EFI_PEI_LBA Lba, 1181 OUT FDC_COMMAND_PACKET1 *Command 1182 ) 1183 { 1184 DISKET_PARA_TABLE *Para; 1185 UINT8 EndOfTrack; 1186 UINT8 DevPos; 1187 1188 DevPos = Info->DevPos; 1189 1190 // 1191 // Get the base of disk parameter information corresponding to its type. 1192 // 1193 Para = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type); 1194 1195 EndOfTrack = Para->EndOfTrack; 1196 1197 if (DevPos == 0) { 1198 Command->DiskHeadSel = 0; 1199 } else { 1200 Command->DiskHeadSel = 1; 1201 } 1202 1203 Command->Cylinder = (UINT8) ((UINTN) Lba / EndOfTrack / 2); 1204 Command->Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2); 1205 Command->Sector = (UINT8) ((UINT8) ((UINTN) Lba % EndOfTrack) + 1); 1206 Command->DiskHeadSel = (UINT8) (Command->DiskHeadSel | (Command->Head << 2)); 1207 Command->Number = Para->Number; 1208 Command->EndOfTrack = Para->EndOfTrack; 1209 Command->GapLength = Para->GapLength; 1210 Command->DataLength = Para->DataLength; 1211 } 1212 1213 /** 1214 Setup specifed FDC device. 1215 1216 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV. 1217 @param DevPos Index of FDC device. 1218 1219 @retval EFI_SUCCESS FDC device successfully set up. 1220 @retval EFI_DEVICE_ERROR FDC device has errors. 1221 1222 **/ 1223 EFI_STATUS 1224 Setup ( 1225 IN FDC_BLK_IO_DEV *FdcBlkIoDev, 1226 IN UINT8 DevPos 1227 ) 1228 { 1229 EFI_STATUS Status; 1230 1231 IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_CCR), 0x0); 1232 1233 MicroSecondDelay (FDC_MEDIUM_DELAY); 1234 1235 Status = Specify (FdcBlkIoDev); 1236 return Status; 1237 } 1238 1239 /** 1240 Setup DMA channels to read data. 1241 1242 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV. 1243 @param Buffer Memory buffer for DMA transfer. 1244 @param BlockSize the number of the bytes in one block. 1245 @param NumberOfBlocks Number of blocks to read. 1246 1247 **/ 1248 VOID 1249 SetDMA ( 1250 IN FDC_BLK_IO_DEV *FdcBlkIoDev, 1251 IN VOID *Buffer, 1252 IN UINTN BlockSize, 1253 IN UINTN NumberOfBlocks 1254 ) 1255 { 1256 UINT8 Data; 1257 UINTN Count; 1258 1259 // 1260 // Mask DMA channel 2; 1261 // 1262 IoWrite8 (R_8237_DMA_WRSMSK_CH0_3, B_8237_DMA_WRSMSK_CMS | 2); 1263 1264 // 1265 // Clear first/last flip flop 1266 // 1267 IoWrite8 (R_8237_DMA_CBPR_CH0_3, B_8237_DMA_WRSMSK_CMS | 2); 1268 1269 // 1270 // Set mode 1271 // 1272 IoWrite8 (R_8237_DMA_CHMODE_CH0_3, V_8237_DMA_CHMODE_SINGLE | V_8237_DMA_CHMODE_IO2MEM | 2); 1273 1274 // 1275 // Set base address and page register 1276 // 1277 Data = (UINT8) (UINTN) Buffer; 1278 IoWrite8 (R_8237_DMA_BASE_CA_CH2, Data); 1279 Data = (UINT8) ((UINTN) Buffer >> 8); 1280 IoWrite8 (R_8237_DMA_BASE_CA_CH2, Data); 1281 1282 Data = (UINT8) ((UINTN) Buffer >> 16); 1283 IoWrite8 (R_8237_DMA_MEM_LP_CH2, Data); 1284 1285 // 1286 // Set count register 1287 // 1288 Count = BlockSize * NumberOfBlocks - 1; 1289 Data = (UINT8) (Count & 0xff); 1290 IoWrite8 (R_8237_DMA_BASE_CC_CH2, Data); 1291 Data = (UINT8) (Count >> 8); 1292 IoWrite8 (R_8237_DMA_BASE_CC_CH2, Data); 1293 1294 // 1295 // Clear channel 2 mask 1296 // 1297 IoWrite8 (R_8237_DMA_WRSMSK_CH0_3, 0x02); 1298 } 1299 1300 1301 /** 1302 According to the block range specified by Lba and NumberOfBlocks, calculate 1303 the number of blocks in the same sector, which can be transferred in a batch. 1304 1305 @param Info Information of floppy device. 1306 @param Lba Start address of block range. 1307 @param NumberOfBlocks Number of blocks of the range. 1308 1309 @return Number of blocks in the same sector. 1310 1311 **/ 1312 UINTN 1313 GetTransferBlockCount ( 1314 IN PEI_FLOPPY_DEVICE_INFO *Info, 1315 IN EFI_PEI_LBA Lba, 1316 IN UINTN NumberOfBlocks 1317 ) 1318 { 1319 DISKET_PARA_TABLE *Para; 1320 UINT8 EndOfTrack; 1321 UINT8 Head; 1322 UINT8 SectorsInTrack; 1323 1324 // 1325 // Get the base of disk parameter information corresponding to its type. 1326 // 1327 Para = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type); 1328 1329 EndOfTrack = Para->EndOfTrack; 1330 Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2); 1331 1332 SectorsInTrack = (UINT8) (EndOfTrack * (2 - Head) - (UINT8) ((UINTN) Lba % EndOfTrack)); 1333 if (SectorsInTrack < NumberOfBlocks) { 1334 // 1335 // Not all the block range locates in the same sector 1336 // 1337 return SectorsInTrack; 1338 } else { 1339 // 1340 // All the block range is in the same sector. 1341 // 1342 return NumberOfBlocks; 1343 } 1344 } 1345 1346 /** 1347 Read data sector from FDC device. 1348 1349 @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV. 1350 @param Info Information of floppy device. 1351 @param Buffer Buffer to setup for DMA. 1352 @param Lba The start address to read. 1353 @param NumberOfBlocks Number of blocks to read. 1354 1355 @retval EFI_SUCCESS Data successfully read out. 1356 @retval EFI_DEVICE_ERROR FDC device has errors. 1357 @retval EFI_TIMEOUT Command does not take effect in time. 1358 1359 **/ 1360 EFI_STATUS 1361 ReadDataSector ( 1362 IN FDC_BLK_IO_DEV *FdcBlkIoDev, 1363 IN OUT PEI_FLOPPY_DEVICE_INFO *Info, 1364 IN VOID *Buffer, 1365 IN EFI_PEI_LBA Lba, 1366 IN UINTN NumberOfBlocks 1367 ) 1368 { 1369 EFI_STATUS Status; 1370 FDC_COMMAND_PACKET1 Command; 1371 FDC_RESULT_PACKET Result; 1372 UINTN Index; 1373 UINTN Times; 1374 UINT8 *Pointer; 1375 1376 Status = Seek (FdcBlkIoDev, Info, Lba); 1377 if (Status != EFI_SUCCESS) { 1378 return EFI_DEVICE_ERROR; 1379 } 1380 1381 // 1382 // Set up DMA 1383 // 1384 SetDMA (FdcBlkIoDev, Buffer, Info->MediaInfo.BlockSize, NumberOfBlocks); 1385 1386 // 1387 // Allocate Read command packet 1388 // 1389 ZeroMem (&Command, sizeof (FDC_COMMAND_PACKET1)); 1390 Command.CommandCode = READ_DATA_CMD | CMD_MT | CMD_MFM | CMD_SK; 1391 1392 // 1393 // Fill parameters for command. 1394 // 1395 FillPara (Info, Lba, &Command); 1396 1397 // 1398 // Write command bytes to FDC 1399 // 1400 Pointer = (UINT8 *) (&Command); 1401 for (Index = 0; Index < sizeof (FDC_COMMAND_PACKET1); Index++) { 1402 if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) { 1403 return EFI_DEVICE_ERROR; 1404 } 1405 } 1406 1407 // 1408 // Wait for some time until command takes effect. 1409 // 1410 Times = (STALL_1_SECOND / FDC_CHECK_INTERVAL) + 1; 1411 do { 1412 if ((IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR)) & 0xc0) == 0xc0) { 1413 break; 1414 } 1415 1416 MicroSecondDelay (FDC_SHORT_DELAY); 1417 } while (--Times > 0); 1418 1419 if (Times == 0) { 1420 // 1421 // Command fails to take effect in time, return EFI_TIMEOUT. 1422 // 1423 return EFI_TIMEOUT; 1424 } 1425 1426 // 1427 // Read result bytes from FDC 1428 // 1429 Pointer = (UINT8 *) (&Result); 1430 for (Index = 0; Index < sizeof (FDC_RESULT_PACKET); Index++) { 1431 if (DataInByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) { 1432 return EFI_DEVICE_ERROR; 1433 } 1434 } 1435 1436 return CheckResult (&Result, Info); 1437 } 1438 1439 /** 1440 Gets the count of block I/O devices that one specific block driver detects. 1441 1442 This function is used for getting the count of block I/O devices that one 1443 specific block driver detects. To the PEI ATAPI driver, it returns the number 1444 of all the detected ATAPI devices it detects during the enumeration process. 1445 To the PEI legacy floppy driver, it returns the number of all the legacy 1446 devices it finds during its enumeration process. If no device is detected, 1447 then the function will return zero. 1448 1449 @param[in] PeiServices General-purpose services that are available 1450 to every PEIM. 1451 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI 1452 instance. 1453 @param[out] NumberBlockDevices The number of block I/O devices discovered. 1454 1455 @retval EFI_SUCCESS Operation performed successfully. 1456 1457 **/ 1458 EFI_STATUS 1459 EFIAPI 1460 FdcGetNumberOfBlockDevices ( 1461 IN EFI_PEI_SERVICES **PeiServices, 1462 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, 1463 OUT UINTN *NumberBlockDevices 1464 ) 1465 { 1466 FDC_BLK_IO_DEV *FdcBlkIoDev; 1467 1468 FdcBlkIoDev = NULL; 1469 1470 FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This); 1471 1472 *NumberBlockDevices = FdcBlkIoDev->DeviceCount; 1473 1474 return EFI_SUCCESS; 1475 } 1476 1477 /** 1478 Gets a block device's media information. 1479 1480 This function will provide the caller with the specified block device's media 1481 information. If the media changes, calling this function will update the media 1482 information accordingly. 1483 1484 @param[in] PeiServices General-purpose services that are available to every 1485 PEIM 1486 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance. 1487 @param[in] DeviceIndex Specifies the block device to which the function wants 1488 to talk. Because the driver that implements Block I/O 1489 PPIs will manage multiple block devices, the PPIs that 1490 want to talk to a single device must specify the 1491 device index that was assigned during the enumeration 1492 process. This index is a number from one to 1493 NumberBlockDevices. 1494 @param[out] MediaInfo The media information of the specified block media. 1495 The caller is responsible for the ownership of this 1496 data structure. 1497 1498 @retval EFI_SUCCESS Media information about the specified block device 1499 was obtained successfully. 1500 @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware 1501 error. 1502 @retval Others Other failure occurs. 1503 1504 **/ 1505 EFI_STATUS 1506 EFIAPI 1507 FdcGetBlockDeviceMediaInfo ( 1508 IN EFI_PEI_SERVICES **PeiServices, 1509 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, 1510 IN UINTN DeviceIndex, 1511 OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo 1512 ) 1513 { 1514 UINTN DeviceCount; 1515 FDC_BLK_IO_DEV *FdcBlkIoDev; 1516 BOOLEAN Healthy; 1517 UINTN Index; 1518 1519 FdcBlkIoDev = NULL; 1520 1521 if (This == NULL || MediaInfo == NULL) { 1522 return EFI_INVALID_PARAMETER; 1523 } 1524 1525 FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This); 1526 1527 DeviceCount = FdcBlkIoDev->DeviceCount; 1528 1529 // 1530 // DeviceIndex is a value from 1 to NumberBlockDevices. 1531 // 1532 if ((DeviceIndex < 1) || (DeviceIndex > DeviceCount) || (DeviceIndex > 2)) { 1533 return EFI_INVALID_PARAMETER; 1534 } 1535 1536 Index = DeviceIndex - 1; 1537 // 1538 // Probe media and retrieve latest media information 1539 // 1540 Healthy = DiscoverFdcDevice ( 1541 FdcBlkIoDev, 1542 &FdcBlkIoDev->DeviceInfo[Index], 1543 MediaInfo 1544 ); 1545 1546 if (!Healthy) { 1547 return EFI_DEVICE_ERROR; 1548 } 1549 1550 CopyMem ( 1551 &(FdcBlkIoDev->DeviceInfo[Index].MediaInfo), 1552 MediaInfo, 1553 sizeof (EFI_PEI_BLOCK_IO_MEDIA) 1554 ); 1555 1556 return EFI_SUCCESS; 1557 } 1558 1559 /** 1560 Reads the requested number of blocks from the specified block device. 1561 1562 The function reads the requested number of blocks from the device. All the 1563 blocks are read, or an error is returned. If there is no media in the device, 1564 the function returns EFI_NO_MEDIA. 1565 1566 @param[in] PeiServices General-purpose services that are available to 1567 every PEIM. 1568 @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance. 1569 @param[in] DeviceIndex Specifies the block device to which the function wants 1570 to talk. Because the driver that implements Block I/O 1571 PPIs will manage multiple block devices, the PPIs that 1572 want to talk to a single device must specify the device 1573 index that was assigned during the enumeration process. 1574 This index is a number from one to NumberBlockDevices. 1575 @param[in] StartLBA The starting logical block address (LBA) to read from 1576 on the device 1577 @param[in] BufferSize The size of the Buffer in bytes. This number must be 1578 a multiple of the intrinsic block size of the device. 1579 @param[out] Buffer A pointer to the destination buffer for the data. 1580 The caller is responsible for the ownership of the 1581 buffer. 1582 1583 @retval EFI_SUCCESS The data was read correctly from the device. 1584 @retval EFI_DEVICE_ERROR The device reported an error while attempting 1585 to perform the read operation. 1586 @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not 1587 valid, or the buffer is not properly aligned. 1588 @retval EFI_NO_MEDIA There is no media in the device. 1589 @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of 1590 the intrinsic block size of the device. 1591 1592 **/ 1593 EFI_STATUS 1594 EFIAPI 1595 FdcReadBlocks ( 1596 IN EFI_PEI_SERVICES **PeiServices, 1597 IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This, 1598 IN UINTN DeviceIndex, 1599 IN EFI_PEI_LBA StartLBA, 1600 IN UINTN BufferSize, 1601 OUT VOID *Buffer 1602 ) 1603 { 1604 EFI_PEI_BLOCK_IO_MEDIA MediaInfo; 1605 EFI_STATUS Status; 1606 UINTN Count; 1607 UINTN NumberOfBlocks; 1608 UINTN BlockSize; 1609 FDC_BLK_IO_DEV *FdcBlkIoDev; 1610 VOID *MemPage; 1611 1612 FdcBlkIoDev = NULL; 1613 ZeroMem (&MediaInfo, sizeof (EFI_PEI_BLOCK_IO_MEDIA)); 1614 1615 if (This == NULL) { 1616 return EFI_INVALID_PARAMETER; 1617 } 1618 1619 FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This); 1620 1621 if (Buffer == NULL) { 1622 return EFI_INVALID_PARAMETER; 1623 } 1624 1625 Status = FdcGetBlockDeviceMediaInfo (PeiServices, This, DeviceIndex, &MediaInfo); 1626 if (Status != EFI_SUCCESS) { 1627 return EFI_DEVICE_ERROR; 1628 } 1629 1630 if (!MediaInfo.MediaPresent) { 1631 return EFI_NO_MEDIA; 1632 } 1633 1634 BlockSize = MediaInfo.BlockSize; 1635 1636 // 1637 // If BufferSize cannot be divided by block size of FDC device, 1638 // return EFI_BAD_BUFFER_SIZE. 1639 // 1640 if (BufferSize % BlockSize != 0) { 1641 return EFI_BAD_BUFFER_SIZE; 1642 } 1643 1644 NumberOfBlocks = BufferSize / BlockSize; 1645 1646 if ((StartLBA + NumberOfBlocks - 1) > FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].MediaInfo.LastBlock) { 1647 return EFI_INVALID_PARAMETER; 1648 } 1649 1650 MemPage = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize)); 1651 if ((MemPage == NULL) || ((UINTN) MemPage >= ISA_MAX_MEMORY_ADDRESS)) { 1652 // 1653 // If fail to allocate memory under ISA_MAX_MEMORY_ADDRESS, designate the address space for DMA 1654 // 1655 MemPage = (VOID *) ((UINTN) (UINT32) 0x0f00000); 1656 } 1657 Status = MotorOn (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1])); 1658 if (Status != EFI_SUCCESS) { 1659 return EFI_DEVICE_ERROR; 1660 } 1661 1662 Status = Setup (FdcBlkIoDev, FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].DevPos); 1663 if (Status != EFI_SUCCESS) { 1664 MotorOff (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1])); 1665 return EFI_DEVICE_ERROR; 1666 } 1667 // 1668 // Read data in batches. 1669 // Blocks in the same cylinder are read out in a batch. 1670 // 1671 while ((Count = GetTransferBlockCount ( 1672 &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]), 1673 StartLBA, 1674 NumberOfBlocks 1675 )) != 0 && Status == EFI_SUCCESS) { 1676 Status = ReadDataSector ( 1677 FdcBlkIoDev, 1678 &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]), 1679 MemPage, 1680 StartLBA, 1681 Count 1682 ); 1683 CopyMem (Buffer, MemPage, BlockSize * Count); 1684 StartLBA += Count; 1685 NumberOfBlocks -= Count; 1686 Buffer = (VOID *) ((UINTN) Buffer + Count * BlockSize); 1687 } 1688 1689 MotorOff (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1])); 1690 1691 switch (Status) { 1692 case EFI_SUCCESS: 1693 return EFI_SUCCESS; 1694 1695 default: 1696 FdcReset (FdcBlkIoDev, FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].DevPos); 1697 return EFI_DEVICE_ERROR; 1698 } 1699 } 1700 1701 /** 1702 Initializes the floppy disk controller and installs FDC Block I/O PPI. 1703 1704 @param FileHandle Handle of the file being invoked. 1705 @param PeiServices Describes the list of possible PEI Services. 1706 1707 @retval EFI_SUCCESS Successfully initialized FDC and installed PPI. 1708 @retval EFI_NOT_FOUND Cannot find FDC device. 1709 @retval EFI_OUT_OF_RESOURCES Have no enough memory to create instance or descriptors. 1710 @retval Other Fail to install FDC Block I/O PPI. 1711 1712 **/ 1713 EFI_STATUS 1714 EFIAPI 1715 FdcPeimEntry ( 1716 IN EFI_PEI_FILE_HANDLE FileHandle, 1717 IN CONST EFI_PEI_SERVICES **PeiServices 1718 ) 1719 { 1720 EFI_STATUS Status; 1721 FDC_BLK_IO_DEV *FdcBlkIoDev; 1722 UINTN DeviceCount; 1723 UINT32 Index; 1724 1725 Status = PeiServicesRegisterForShadow (FileHandle); 1726 if (!EFI_ERROR (Status)) { 1727 return Status; 1728 } 1729 1730 // 1731 // Allocate memory for instance of FDC_BLK_IO_DEV and copy initial value 1732 // from template to it. 1733 // 1734 FdcBlkIoDev = AllocatePages (EFI_SIZE_TO_PAGES(sizeof (FDC_BLK_IO_DEV))); 1735 if (FdcBlkIoDev == NULL) { 1736 return EFI_OUT_OF_RESOURCES; 1737 } 1738 CopyMem (FdcBlkIoDev, &mBlockIoDevTemplate, sizeof (mBlockIoDevTemplate)); 1739 1740 // 1741 // Initialize DMA controller to enable all channels. 1742 // 1743 for (Index = 0; Index < sizeof (mRegisterTable) / sizeof (PEI_DMA_TABLE); Index++) { 1744 IoWrite8 (mRegisterTable[Index].Register, mRegisterTable[Index].Value); 1745 } 1746 REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_PC_INIT); 1747 1748 // 1749 // Enumerate FDC devices. 1750 // 1751 DeviceCount = FdcEnumeration (FdcBlkIoDev); 1752 if (DeviceCount == 0) { 1753 return EFI_NOT_FOUND; 1754 } 1755 1756 FdcBlkIoDev->PpiDescriptor.Ppi = &FdcBlkIoDev->FdcBlkIo; 1757 1758 return PeiServicesInstallPpi (&FdcBlkIoDev->PpiDescriptor); 1759 } 1760