]> git.proxmox.com Git - mirror_edk2.git/commitdiff
IntelFrameworkModulePkg: Add IsaFloppyPei driver
authorjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>
Fri, 17 Jun 2011 17:42:49 +0000 (17:42 +0000)
committerjljusten <jljusten@6f19259b-4bc3-4df7-8a09-765794883524>
Fri, 17 Jun 2011 17:42:49 +0000 (17:42 +0000)
Signed-off-by: jljusten
Reviewed-by: mdkinney
git-svn-id: https://edk2.svn.sourceforge.net/svnroot/edk2/trunk/edk2@11845 6f19259b-4bc3-4df7-8a09-765794883524

IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/Fdc.h [new file with mode: 0644]
IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.c [new file with mode: 0644]
IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.h [new file with mode: 0644]
IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/IsaFloppyPei.inf [new file with mode: 0644]
IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec
IntelFrameworkModulePkg/IntelFrameworkModulePkg.dsc

diff --git a/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/Fdc.h b/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/Fdc.h
new file mode 100644 (file)
index 0000000..a6a7e42
--- /dev/null
@@ -0,0 +1,235 @@
+/** @file \r
+Definition of FDC registers and structures.\r
+\r
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+  \r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions\r
+of the BSD License which accompanies this distribution.  The\r
+full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _PEI_RECOVERY_FDC_H_\r
+#define _PEI_RECOVERY_FDC_H_\r
+\r
+//\r
+// FDC Registers\r
+//\r
+#define FDC_REGISTER_DOR  2 //Digital Output Register\r
+#define FDC_REGISTER_MSR  4 //Main Status Register\r
+#define FDC_REGISTER_DTR  5 //Data Register\r
+#define FDC_REGISTER_CCR  7 //Configuration Control Register(data rate select)\r
+#define FDC_REGISTER_DIR  7 //Digital Input Register(diskchange)\r
+//\r
+// FDC Register Bit Definitions\r
+//\r
+//\r
+// Digital Out Register(WO)\r
+//\r
+#define SELECT_DRV      BIT0  // Select Drive: 0=A 1=B\r
+#define RESET_FDC       BIT2  // Reset FDC\r
+#define INT_DMA_ENABLE  BIT3  // Enable Int & DMA\r
+#define DRVA_MOTOR_ON   BIT4  // Turn On Drive A Motor\r
+#define DRVB_MOTOR_ON   BIT5  // Turn On Drive B Motor\r
+//\r
+// Main Status Register(RO)\r
+//\r
+#define MSR_DAB BIT0  // Drive A Busy\r
+#define MSR_DBB BIT1  // Drive B Busy\r
+#define MSR_CB  BIT4  // FDC Busy\r
+#define MSR_NDM BIT5  // Non-DMA Mode\r
+#define MSR_DIO BIT6  // Data Input/Output\r
+#define MSR_RQM BIT7  // Request For Master\r
+//\r
+// Configuration Control Register(WO)\r
+//\r
+#define CCR_DRC (BIT0 | BIT1) // Data Rate select\r
+//\r
+// Digital Input Register(RO)\r
+//\r
+#define DIR_DCL     BIT7  // Disk change line\r
+#define DRC_500KBS  0x0   // 500K\r
+#define DRC_300KBS  0x01  // 300K\r
+#define DRC_250KBS  0x02  // 250K\r
+//\r
+// FDC Command Code\r
+//\r
+#define READ_DATA_CMD         0x06\r
+#define SEEK_CMD              0x0F\r
+#define RECALIBRATE_CMD       0x07\r
+#define SENSE_INT_STATUS_CMD  0x08\r
+#define SPECIFY_CMD           0x03\r
+#define SENSE_DRV_STATUS_CMD  0x04\r
+\r
+///\r
+/// CMD_MT: Multi_Track Selector\r
+/// when set , this flag selects the multi-track operating mode.\r
+/// In this mode, the FDC treats a complete cylinder under head0 and 1 as a single track\r
+///\r
+#define CMD_MT  BIT7\r
+\r
+///\r
+/// CMD_MFM: MFM/FM Mode Selector\r
+/// A one selects the double density(MFM) mode\r
+/// A zero selects single density (FM) mode\r
+///\r
+#define CMD_MFM BIT6\r
+\r
+///\r
+/// CMD_SK: Skip Flag\r
+/// When set to 1, sectors containing a deleted data address mark will automatically be skipped\r
+/// during the execution of Read Data.\r
+/// When set to 0, the sector is read or written the same as the read and write commands.\r
+///\r
+#define CMD_SK  BIT5\r
+\r
+//\r
+// FDC Status Register Bit Definitions\r
+//\r
+//\r
+// Status Register 0\r
+//\r
+#define STS0_IC (BIT7 | BIT6) // Interrupt Code\r
+#define STS0_SE BIT5          // Seek End: the FDC completed a seek or recalibrate command\r
+#define STS0_EC BIT4          // Equipment Check\r
+#define STS0_NR BIT3          // Not Ready(unused), this bit is always 0\r
+#define STS0_HA BIT2          // Head Address: the current head address\r
+//\r
+// STS0_US1 & STS0_US0: Drive Select(the current selected drive)\r
+//\r
+#define STS0_US1  BIT1  // Unit Select1\r
+#define STS0_US0  BIT0  // Unit Select0\r
+//\r
+// Status Register 1\r
+//\r
+#define STS1_EN BIT7  // End of Cylinder\r
+//\r
+// BIT6 is unused\r
+//\r
+#define STS1_DE BIT5  // Data Error: The FDC detected a CRC error in either the ID field or data field of a sector\r
+#define STS1_OR BIT4  // Overrun/Underrun: Becomes set if FDC does not receive CPU or DMA service within the required time interval\r
+//\r
+// BIT3 is unused\r
+//\r
+#define STS1_ND BIT2  // No data\r
+#define STS1_NW BIT1  // Not Writable\r
+#define STS1_MA BIT0  // Missing Address Mark\r
+\r
+//\r
+// Status Register 2\r
+//\r
+// BIT7 is unused\r
+//\r
+#define STS2_CM BIT6  // Control Mark\r
+#define STS2_DD BIT5  // Data Error in Data Field: The FDC detected a CRC error in the data field\r
+#define STS2_WC BIT4  // Wrong Cylinder: The track address from sector ID field is different from the track address maintained inside FDC\r
+//\r
+// BIT3 is unused\r
+// BIT2 is unused\r
+//\r
+#define STS2_BC BIT1  // Bad Cylinder\r
+#define STS2_MD BIT0  // Missing Address Mark in DataField\r
+\r
+//\r
+// Status Register 3\r
+//\r
+// BIT7 is unused\r
+//\r
+#define STS3_WP BIT6  // Write Protected\r
+//\r
+// BIT5 is unused\r
+//\r
+#define STS3_T0 BIT4  // Track 0\r
+//\r
+// BIT3 is unused\r
+//\r
+#define STS3_HD BIT2  // Head Address\r
+//\r
+// STS3_US1 & STS3_US0 : Drive Select\r
+//\r
+#define STS3_US1  BIT1  // Unit Select1\r
+#define STS3_US0  BIT0  // Unit Select0\r
+\r
+//\r
+// Status Register 0 Interrupt Code Description\r
+//\r
+#define IC_NT   0x0   // Normal Termination of Command\r
+#define IC_AT   0x40  // Abnormal Termination of Command\r
+#define IC_IC   0x80  // Invalid Command\r
+#define IC_ATRC 0xC0  // Abnormal Termination caused by Polling\r
+\r
+///\r
+/// Table of parameters for diskette\r
+///\r
+typedef struct {\r
+  UINT8 EndOfTrack;          ///< End of track\r
+  UINT8 GapLength;           ///< Gap length\r
+  UINT8 DataLength;          ///< Data length\r
+  UINT8 Number;              ///< Number of bytes per sector\r
+  UINT8 MaxTrackNum;\r
+  UINT8 MotorStartTime;\r
+  UINT8 MotorOffTime;\r
+  UINT8 HeadSettlingTime;\r
+  UINT8 DataTransferRate;\r
+} DISKET_PARA_TABLE;\r
+\r
+///\r
+/// Structure for FDC Command Packet 1\r
+///\r
+typedef struct {\r
+  UINT8 CommandCode;\r
+  UINT8 DiskHeadSel;\r
+  UINT8 Cylinder;\r
+  UINT8 Head;\r
+  UINT8 Sector;\r
+  UINT8 Number;\r
+  UINT8 EndOfTrack;\r
+  UINT8 GapLength;\r
+  UINT8 DataLength;\r
+} FDC_COMMAND_PACKET1;\r
+\r
+///\r
+/// Structure for FDC Command Packet 2\r
+///\r
+typedef struct {\r
+  UINT8 CommandCode;\r
+  UINT8 DiskHeadSel;\r
+} FDC_COMMAND_PACKET2;\r
+\r
+///\r
+/// Structure for FDC Specify Command\r
+///\r
+typedef struct {\r
+  UINT8 CommandCode;\r
+  UINT8 SrtHut;\r
+  UINT8 HltNd;\r
+} FDC_SPECIFY_CMD;\r
+\r
+///\r
+/// Structure for FDC Seek Command\r
+///\r
+typedef struct {\r
+  UINT8 CommandCode;\r
+  UINT8 DiskHeadSel;\r
+  UINT8 NewCylinder;\r
+} FDC_SEEK_CMD;\r
+\r
+///\r
+/// Structure for FDC Result Packet\r
+///\r
+typedef struct {\r
+  UINT8 Status0;\r
+  UINT8 Status1;\r
+  UINT8 Status2;\r
+  UINT8 CylinderNumber;\r
+  UINT8 HeaderAddress;\r
+  UINT8 Record;\r
+  UINT8 Number;\r
+} FDC_RESULT_PACKET;\r
+\r
+#endif\r
diff --git a/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.c b/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.c
new file mode 100644 (file)
index 0000000..afeb221
--- /dev/null
@@ -0,0 +1,1768 @@
+/** @file\r
+Floppy Peim to support Recovery function from Floppy device.\r
+\r
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+  \r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions\r
+of the BSD License which accompanies this distribution.  The\r
+full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+\r
+#include "FloppyPeim.h"\r
+\r
+\r
+PEI_DMA_TABLE      mRegisterTable[] = {\r
+  //\r
+  // DMA2: Clear Byte Ptr, Enable\r
+  //\r
+  {\r
+    R_8237_DMA_CBPR_CH4_7,\r
+    0\r
+  },\r
+  {\r
+    R_8237_DMA_COMMAND_CH4_7,\r
+    0\r
+  },\r
+  //\r
+  // DMA1: Clear Byte Ptr, Enable\r
+  //\r
+  {\r
+    R_8237_DMA_CBPR_CH0_3,\r
+    0\r
+  },\r
+  {\r
+    R_8237_DMA_COMMAND_CH0_3,\r
+    0\r
+  },\r
+  //\r
+  // Configure Channel 4 for Cascade Mode\r
+  // Clear DMA Request and enable DREQ\r
+  //\r
+  {\r
+    R_8237_DMA_CHMODE_CH4_7,\r
+    V_8237_DMA_CHMODE_CASCADE | 0\r
+  },\r
+  {\r
+    R_8237_DMA_STA_CH4_7,\r
+    0\r
+  },\r
+  {\r
+    R_8237_DMA_WRSMSK_CH4_7,\r
+    0\r
+  },\r
+  //\r
+  // Configure DMA1 (Channels 0-3) for Single Mode\r
+  // Clear DMA Request and enable DREQ\r
+  //\r
+  {\r
+    R_8237_DMA_CHMODE_CH0_3,\r
+    V_8237_DMA_CHMODE_SINGLE | 0\r
+  },\r
+  {\r
+    R_8237_DMA_STA_CH0_3,\r
+    0\r
+  },\r
+  {\r
+    R_8237_DMA_WRSMSK_CH0_3,\r
+    0\r
+  },\r
+  {\r
+    R_8237_DMA_CHMODE_CH0_3,\r
+    V_8237_DMA_CHMODE_SINGLE | 1\r
+  },\r
+  {\r
+    R_8237_DMA_STA_CH0_3,\r
+    1\r
+  },\r
+  {\r
+    R_8237_DMA_WRSMSK_CH0_3,\r
+    1\r
+  },\r
+  {\r
+    R_8237_DMA_CHMODE_CH0_3,\r
+    V_8237_DMA_CHMODE_SINGLE | 2\r
+  },\r
+  {\r
+    R_8237_DMA_STA_CH0_3,\r
+    2\r
+  },\r
+  {\r
+    R_8237_DMA_WRSMSK_CH0_3,\r
+    2\r
+  },\r
+  {\r
+    R_8237_DMA_CHMODE_CH0_3,\r
+    V_8237_DMA_CHMODE_SINGLE | 3\r
+  },\r
+  {\r
+    R_8237_DMA_STA_CH0_3,\r
+    3\r
+  },\r
+  {\r
+    R_8237_DMA_WRSMSK_CH0_3,\r
+    3\r
+  },\r
+  //\r
+  // Configure DMA2 (Channels 5-7) for Single Mode\r
+  // Clear DMA Request and enable DREQ\r
+  //\r
+  {\r
+    R_8237_DMA_CHMODE_CH4_7,\r
+    V_8237_DMA_CHMODE_SINGLE | 1\r
+  },\r
+  {\r
+    R_8237_DMA_STA_CH4_7,\r
+    1\r
+  },\r
+  {\r
+    R_8237_DMA_WRSMSK_CH4_7,\r
+    1\r
+  },\r
+  {\r
+    R_8237_DMA_CHMODE_CH4_7,\r
+    V_8237_DMA_CHMODE_SINGLE | 2\r
+  },\r
+  {\r
+    R_8237_DMA_STA_CH4_7,\r
+    2\r
+  },\r
+  {\r
+    R_8237_DMA_WRSMSK_CH4_7,\r
+    2\r
+  },\r
+  {\r
+    R_8237_DMA_CHMODE_CH4_7,\r
+    V_8237_DMA_CHMODE_SINGLE | 3\r
+  },\r
+  {\r
+    R_8237_DMA_STA_CH4_7,\r
+    3\r
+  },\r
+  {\r
+    R_8237_DMA_WRSMSK_CH4_7,\r
+    3\r
+  }\r
+};\r
+\r
+//\r
+// Table of diskette parameters of various diskette types \r
+//\r
+DISKET_PARA_TABLE  DiskPara[9] = {\r
+  {\r
+    0x09,\r
+    0x50,\r
+    0xff,\r
+    0x2,\r
+    0x27,\r
+    0x4,\r
+    0x25,\r
+    0x14,\r
+    0x80\r
+  },\r
+  {\r
+    0x09,\r
+    0x2a,\r
+    0xff,\r
+    0x2,\r
+    0x27,\r
+    0x4,\r
+    0x25,\r
+    0x0f,\r
+    0x40\r
+  },\r
+  {\r
+    0x0f,\r
+    0x54,\r
+    0xff,\r
+    0x2,\r
+    0x4f,\r
+    0x4,\r
+    0x25,\r
+    0x0f,\r
+    0x0\r
+  },\r
+  {\r
+    0x09,\r
+    0x50,\r
+    0xff,\r
+    0x2,\r
+    0x4f,\r
+    0x4,\r
+    0x25,\r
+    0x0f,\r
+    0x80\r
+  },\r
+  {\r
+    0x09,\r
+    0x2a,\r
+    0xff,\r
+    0x2,\r
+    0x4f,\r
+    0x4,\r
+    0x25,\r
+    0x0f,\r
+    0x80\r
+  },\r
+  {\r
+    0x12,\r
+    0x1b,\r
+    0xff,\r
+    0x2,\r
+    0x4f,\r
+    0x4,\r
+    0x25,\r
+    0x0f,\r
+    0x0\r
+  },\r
+  {\r
+    0x09,\r
+    0x2a,\r
+    0xff,\r
+    0x2,\r
+    0x4f,\r
+    0x4,\r
+    0x25,\r
+    0x0f,\r
+    0x80\r
+  },\r
+  {\r
+    0x12,\r
+    0x1b,\r
+    0xff,\r
+    0x2,\r
+    0x4f,\r
+    0x4,\r
+    0x25,\r
+    0x0f,\r
+    0x0\r
+  },\r
+  {\r
+    0x24,\r
+    0x1b,\r
+    0xff,\r
+    0x2,\r
+    0x4f,\r
+    0x4,\r
+    0x25,\r
+    0x0f,\r
+    0xc0\r
+  }\r
+};\r
+\r
+//\r
+// Byte per sector corresponding to various device types.\r
+//\r
+UINTN    BytePerSector[6] = { 0, 256, 512, 1024, 2048, 4096 };\r
+\r
+FDC_BLK_IO_DEV mBlockIoDevTemplate = {\r
+  FDC_BLK_IO_DEV_SIGNATURE,\r
+  {\r
+    FdcGetNumberOfBlockDevices,\r
+    FdcGetBlockDeviceMediaInfo,\r
+    FdcReadBlocks,\r
+  },\r
+  {\r
+    (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),\r
+    &gEfiPeiVirtualBlockIoPpiGuid,\r
+    NULL\r
+  },\r
+  0,\r
+  {{0}}\r
+};\r
+\r
+/**\r
+  Wait and check if bits for DIO and RQM of FDC Main Status Register\r
+  indicates FDC is ready for read or write.\r
+\r
+  Before writing to FDC or reading from FDC, the Host must examine\r
+  the bit7(RQM) and bit6(DIO) of the Main Status Register.\r
+  That is to say:\r
+   Command bytes can not be written to Data Register unless RQM is 1 and DIO is 0.\r
+   Result bytes can not be read from Data Register unless RQM is 1 and DIO is 1.\r
+\r
+  @param  FdcBlkIoDev       Instance of FDC_BLK_IO_DEV.\r
+  @param  DataIn            Indicates data input or output.\r
+                            TRUE means input.\r
+                            FALSE means output.\r
+  @param  TimeoutInMseconds  Timeout value to wait.\r
+  \r
+  @retval EFI_SUCCESS       FDC is ready.\r
+  @retval EFI_NOT_READY     FDC is not ready within the specified time period.\r
+\r
+**/\r
+EFI_STATUS\r
+FdcDRQReady (\r
+  IN FDC_BLK_IO_DEV   *FdcBlkIoDev,\r
+  IN BOOLEAN          DataIn,\r
+  IN UINTN            TimeoutInMseconds\r
+  )\r
+{\r
+  UINTN   Delay;\r
+  UINT8   StatusRegister;\r
+  UINT8   BitInOut;\r
+\r
+  //\r
+  // Check bit6 of Main Status Register.\r
+  //\r
+  BitInOut = 0;\r
+  if (DataIn) {\r
+    BitInOut = BIT6;\r
+  }\r
+\r
+  Delay = ((TimeoutInMseconds * STALL_1_MSECOND) / FDC_CHECK_INTERVAL) + 1;\r
+  do {\r
+    StatusRegister = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR));\r
+    if ((StatusRegister & MSR_RQM) == MSR_RQM && (StatusRegister & MSR_DIO) == BitInOut) {\r
+      //\r
+      // FDC is ready\r
+      //\r
+      break;\r
+    }\r
+\r
+    MicroSecondDelay (FDC_SHORT_DELAY);\r
+  } while (--Delay > 0);\r
+\r
+  if (Delay == 0) {\r
+    //\r
+    // FDC is not ready within the specified time period\r
+    //\r
+    return EFI_NOT_READY;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Read a byte from FDC data register.\r
+\r
+  @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.\r
+  @param  Pointer          Pointer to buffer to hold data read from FDC.\r
+\r
+  @retval EFI_SUCCESS      Byte successfully read.\r
+  @retval EFI_DEVICE_ERROR FDC is not ready.\r
+\r
+**/\r
+EFI_STATUS\r
+DataInByte (\r
+  IN  FDC_BLK_IO_DEV   *FdcBlkIoDev,\r
+  OUT UINT8            *Pointer\r
+  )\r
+{\r
+  UINT8 Data;\r
+\r
+  //\r
+  // Wait for 1ms and detect the FDC is ready to be read\r
+  //\r
+  if (FdcDRQReady (FdcBlkIoDev, TRUE, 1) != EFI_SUCCESS) {\r
+    //\r
+    // FDC is not ready.\r
+    //\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DTR));\r
+  MicroSecondDelay (FDC_SHORT_DELAY);\r
+  *Pointer = Data;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Write a byte to FDC data register.\r
+\r
+  @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.\r
+  @param  Pointer          Pointer to data to write.\r
+\r
+  @retval EFI_SUCCESS      Byte successfully written.\r
+  @retval EFI_DEVICE_ERROR FDC is not ready.\r
+\r
+**/\r
+EFI_STATUS\r
+DataOutByte (\r
+  IN FDC_BLK_IO_DEV   *FdcBlkIoDev,\r
+  IN UINT8            *Pointer\r
+  )\r
+{\r
+  UINT8 Data;\r
+\r
+  //\r
+  // Wait for 1ms and detect the FDC is ready to be written\r
+  //\r
+  if (FdcDRQReady (FdcBlkIoDev, FALSE, 1) != EFI_SUCCESS) {\r
+    //\r
+    // FDC is not ready.\r
+    //\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Data = *Pointer;\r
+  IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DTR), Data);\r
+  MicroSecondDelay (FDC_SHORT_DELAY);\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Get Sts0 and Pcn status from FDC\r
+\r
+  @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV\r
+  @param  Sts0             Value of Sts0\r
+  @param  Pcn              Value of Pcn\r
+\r
+  @retval EFI_SUCCESS      Successfully retrieved status value of Sts0 and Pcn.\r
+  @retval EFI_DEVICE_ERROR Fail to send SENSE_INT_STATUS_CMD.\r
+  @retval EFI_DEVICE_ERROR Fail to read Sts0.\r
+  @retval EFI_DEVICE_ERROR Fail to read Pcn.\r
+\r
+**/\r
+EFI_STATUS\r
+SenseIntStatus (\r
+  IN  FDC_BLK_IO_DEV   *FdcBlkIoDev,\r
+  OUT UINT8            *Sts0,\r
+  OUT UINT8            *Pcn\r
+  )\r
+{\r
+  UINT8 Command;\r
+\r
+  Command = SENSE_INT_STATUS_CMD;\r
+\r
+  if (DataOutByte (FdcBlkIoDev, &Command) != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (DataInByte (FdcBlkIoDev, Sts0) != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (DataInByte (FdcBlkIoDev, Pcn) != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Issue Specify command.\r
+\r
+  @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.\r
+\r
+  @retval EFI_SUCCESS      Specify command successfully issued.\r
+  @retval EFI_DEVICE_ERROR FDC device has errors.\r
+\r
+**/\r
+EFI_STATUS\r
+Specify (\r
+  IN FDC_BLK_IO_DEV   *FdcBlkIoDev\r
+  )\r
+{\r
+  FDC_SPECIFY_CMD Command;\r
+  UINTN           Index;\r
+  UINT8           *Pointer;\r
+\r
+  ZeroMem (&Command, sizeof (FDC_SPECIFY_CMD));\r
+  Command.CommandCode = SPECIFY_CMD;\r
+  //\r
+  // set SRT, HUT\r
+  //\r
+  Command.SrtHut = 0xdf;\r
+  //\r
+  // 0xdf;\r
+  // set HLT and DMA\r
+  //\r
+  Command.HltNd = 0x02;\r
+\r
+  Pointer            = (UINT8 *) (&Command);\r
+  for (Index = 0; Index < sizeof (FDC_SPECIFY_CMD); Index++) {\r
+    if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Wait until busy bit is cleared.\r
+\r
+  @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.\r
+  @param  DevPos           Position of FDC (Driver A or B)\r
+  @param  TimeoutInMseconds Timeout value to wait.\r
+\r
+  @retval EFI_SUCCESS      Busy bit has been cleared before timeout.\r
+  @retval EFI_TIMEOUT      Time goes out before busy bit is cleared.\r
+\r
+**/\r
+EFI_STATUS\r
+FdcWaitForBSYClear (\r
+  IN FDC_BLK_IO_DEV   *FdcBlkIoDev,\r
+  IN UINT8            DevPos,\r
+  IN UINTN            TimeoutInMseconds\r
+  )\r
+{\r
+  UINTN   Delay;\r
+  UINT8   StatusRegister;\r
+  UINT8   Mask;\r
+\r
+  //\r
+  // How to determine drive and command are busy or not: by the bits of Main Status Register\r
+  // bit0: Drive 0 busy (drive A)\r
+  // bit1: Drive 1 busy (drive B)\r
+  // bit4: Command busy\r
+  //\r
+  // set mask: for drive A set bit0 & bit4; for drive B set bit1 & bit4\r
+  //\r
+  Mask  = (UINT8) ((DevPos == 0 ? MSR_DAB : MSR_DBB) | MSR_CB);\r
+\r
+  Delay = ((TimeoutInMseconds * STALL_1_MSECOND) / FDC_CHECK_INTERVAL) + 1;\r
+\r
+  do {\r
+    StatusRegister = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR));\r
+\r
+    if ((StatusRegister & Mask) == 0x00) {\r
+      //\r
+      // not busy\r
+      //\r
+      break;\r
+    }\r
+\r
+    MicroSecondDelay (FDC_SHORT_DELAY);\r
+  } while (--Delay > 0);\r
+\r
+  if (Delay == 0) {\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Reset FDC device.\r
+\r
+  @param  FdcBlkIoDev  Instance of FDC_BLK_IO_DEV\r
+  @param  DevPos       Index of FDC device.\r
+\r
+  @retval EFI_SUCCESS      FDC device successfully reset.\r
+  @retval EFI_DEVICE_ERROR Fail to reset FDC device.\r
+\r
+**/\r
+EFI_STATUS\r
+FdcReset (\r
+  IN FDC_BLK_IO_DEV   *FdcBlkIoDev,\r
+  IN UINT8            DevPos\r
+  )\r
+{\r
+  UINT8 Data;\r
+  UINT8 Sts0;\r
+  UINT8 Pcn;\r
+  UINTN Index;\r
+\r
+  //\r
+  // Reset specified Floppy Logic Drive according to Fdd -> Disk\r
+  // Set Digital Output Register(DOR) to do reset work\r
+  //    bit0 & bit1 of DOR : Drive Select\r
+  //    bit2 : Reset bit\r
+  //    bit3 : DMA and Int bit\r
+  // Reset : A "0" written to bit2 resets the FDC, this reset will remain active until\r
+  //       a "1" is written to this bit.\r
+  // Reset step 1:\r
+  //    use bit0 & bit1 to  select the logic drive\r
+  //    write "0" to bit2\r
+  //\r
+  Data = 0x0;\r
+  Data = (UINT8) (Data | (SELECT_DRV & DevPos));\r
+  IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);\r
+\r
+  //\r
+  // Wait some time, at least 120us.\r
+  //\r
+  MicroSecondDelay (FDC_RESET_DELAY);\r
+  //\r
+  // Reset step 2:\r
+  //    write "1" to bit2\r
+  //    write "1" to bit3 : enable DMA\r
+  //\r
+  Data |= 0x0C;\r
+  IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);\r
+\r
+  MicroSecondDelay (FDC_RESET_DELAY);\r
+\r
+  //\r
+  // Wait until specified floppy logic drive is not busy\r
+  //\r
+  if (FdcWaitForBSYClear (FdcBlkIoDev, DevPos, 1) != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Set the Transfer Data Rate\r
+  //\r
+  IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_CCR), 0x0);\r
+\r
+  MicroSecondDelay (FDC_MEDIUM_DELAY);\r
+\r
+  //\r
+  // Issue Sense interrupt command for each drive (totally 4 drives)\r
+  //\r
+  for (Index = 0; Index < 4; Index++) {\r
+    if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+  //\r
+  // Issue Specify command\r
+  //\r
+  if (Specify (FdcBlkIoDev) != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Turn on the motor of floppy drive.\r
+\r
+  @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.\r
+  @param  Info             Information of floppy device.\r
+\r
+  @retval EFI_SUCCESS      Motor is successfully turned on.\r
+  @retval EFI_SUCCESS      Motor is already on.\r
+  @retval EFI_DEVICE_ERROR Busy bit of FDC cannot be cleared.\r
+\r
+**/\r
+EFI_STATUS\r
+MotorOn (\r
+  IN FDC_BLK_IO_DEV             *FdcBlkIoDev,\r
+  IN OUT PEI_FLOPPY_DEVICE_INFO *Info\r
+  )\r
+{\r
+  UINT8 Data;\r
+  UINT8 DevPos;\r
+\r
+  //\r
+  // Control of the floppy drive motors is a big pain. If motor is off, you have to turn it\r
+  // on first. But you can not leave the motor on all the time, since that would wear out the\r
+  // disk. On the other hand, if you turn the motor off after each operation, the system performance\r
+  // will be awful. The compromise used in this driver is to leave the motor on for 2 seconds after\r
+  // each operation. If a new operation is started in that interval(2s), the motor need not be\r
+  // turned on again. If no new operation is started, a timer goes off and the motor is turned off.\r
+  //\r
+  DevPos = Info->DevPos;\r
+\r
+  //\r
+  // If the Motor is already on, just return EFI_SUCCESS.\r
+  //\r
+  if (Info->MotorOn) {\r
+    return EFI_SUCCESS;\r
+  }\r
+  //\r
+  // The drive's motor is off, so need turn it on.\r
+  // First check if command and drive are busy or not.\r
+  //\r
+  if (FdcWaitForBSYClear (FdcBlkIoDev, DevPos, 1) != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // for drive A: 1CH, drive B: 2DH\r
+  //\r
+  Data = 0x0C;\r
+  Data = (UINT8) (Data | (SELECT_DRV & DevPos));\r
+  if (DevPos == 0) {\r
+    Data |= DRVA_MOTOR_ON;\r
+  } else {\r
+    Data |= DRVB_MOTOR_ON;\r
+  }\r
+\r
+  Info->MotorOn = FALSE;\r
+\r
+  //\r
+  // Turn on the motor and wait for some time to ensure it takes effect.\r
+  //\r
+  IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);\r
+  MicroSecondDelay (FDC_LONG_DELAY);\r
+\r
+  Info->MotorOn = TRUE;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Turn off the motor of floppy drive.\r
+\r
+  @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.\r
+  @param  Info             Information of floppy device.\r
+\r
+**/\r
+VOID\r
+MotorOff (\r
+  IN FDC_BLK_IO_DEV             *FdcBlkIoDev,\r
+  IN OUT PEI_FLOPPY_DEVICE_INFO *Info\r
+  )\r
+{\r
+  UINT8 Data;\r
+  UINT8 DevPos;\r
+\r
+  DevPos = Info->DevPos;\r
+\r
+  if (!Info->MotorOn) {\r
+    return;\r
+  }\r
+  //\r
+  // The motor is on, so need motor off\r
+  //\r
+  Data = 0x0C;\r
+  Data = (UINT8) (Data | (SELECT_DRV & DevPos));\r
+\r
+  IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);\r
+  MicroSecondDelay (FDC_SHORT_DELAY);\r
+\r
+  Info->MotorOn = FALSE;\r
+}\r
+\r
+/**\r
+  Recalibrate the FDC device.\r
+\r
+  @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.\r
+  @param  Info             Information of floppy device.\r
+\r
+  @retval EFI_SUCCESS      FDC successfully recalibrated.\r
+  @retval EFI_DEVICE_ERROR Fail to send RECALIBRATE_CMD.\r
+  @retval EFI_DEVICE_ERROR Fail to get status value of Sts0 and Pcn.\r
+  @retval EFI_DEVICE_ERROR Fail to recalibrate FDC device.\r
+\r
+**/\r
+EFI_STATUS\r
+Recalibrate (\r
+  IN FDC_BLK_IO_DEV             *FdcBlkIoDev,\r
+  IN OUT PEI_FLOPPY_DEVICE_INFO *Info\r
+  )\r
+{\r
+  FDC_COMMAND_PACKET2 Command;\r
+  UINTN               Index;\r
+  UINT8               Sts0;\r
+  UINT8               Pcn;\r
+  UINT8               *Pointer;\r
+  UINT8               Count;\r
+  UINT8               DevPos;\r
+\r
+  DevPos  = Info->DevPos;\r
+\r
+  //\r
+  // We would try twice.\r
+  //\r
+  Count   = 2;\r
+  while (Count > 0) {\r
+    ZeroMem (&Command, sizeof (FDC_COMMAND_PACKET2));\r
+    Command.CommandCode = RECALIBRATE_CMD;\r
+    //\r
+    // drive select\r
+    //\r
+    if (DevPos == 0) {\r
+      Command.DiskHeadSel = 0;\r
+    } else {\r
+      Command.DiskHeadSel = 1;\r
+    }\r
+\r
+    Pointer = (UINT8 *) (&Command);\r
+    for (Index = 0; Index < sizeof (FDC_COMMAND_PACKET2); Index++) {\r
+      if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+    }\r
+\r
+    MicroSecondDelay (FDC_RECALIBRATE_DELAY);\r
+\r
+    if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    if ((Sts0 & 0xf0) == BIT5 && Pcn == 0) {\r
+      //\r
+      // Recalibration is successful. \r
+      //\r
+      Info->Pcn = 0;\r
+      Info->NeedRecalibrate = FALSE;\r
+\r
+      return EFI_SUCCESS;\r
+    } else {\r
+      //\r
+      // Recalibration is not successful. Try again.\r
+      // If trial is used out, return EFI_DEVICE_ERROR.\r
+      //\r
+      Count--;\r
+      if (Count == 0) {\r
+        return EFI_DEVICE_ERROR;\r
+      }\r
+    }\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Seek for the cylinder according to given LBA.\r
+\r
+  @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.\r
+  @param  Info             Information of floppy device.\r
+  @param  Lba              LBA for which to seek for cylinder.\r
+\r
+  @retval EFI_SUCCESS      Successfully moved to the destination cylinder.\r
+  @retval EFI_SUCCESS      Destination cylinder is just the present cylinder.\r
+  @retval EFI_DEVICE_ERROR Fail to move to the destination cylinder.\r
+\r
+**/\r
+EFI_STATUS\r
+Seek (\r
+  IN     FDC_BLK_IO_DEV         *FdcBlkIoDev,\r
+  IN OUT PEI_FLOPPY_DEVICE_INFO *Info,\r
+  IN     EFI_PEI_LBA            Lba\r
+  )\r
+{\r
+  FDC_SEEK_CMD      Command;\r
+  DISKET_PARA_TABLE *Para;\r
+  UINT8             EndOfTrack;\r
+  UINT8             Head;\r
+  UINT8             Cylinder;\r
+  UINT8             Sts0;\r
+  UINT8             *Pointer;\r
+  UINT8             Pcn;\r
+  UINTN             Index;\r
+  UINT8             Gap;\r
+  UINT8             DevPos;\r
+\r
+  DevPos = Info->DevPos;\r
+  if (Info->NeedRecalibrate) {\r
+    if (Recalibrate (FdcBlkIoDev, Info) != EFI_SUCCESS) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+    //\r
+    // Recalibrate Success\r
+    //\r
+    Info->NeedRecalibrate = FALSE;\r
+  }\r
+\r
+  //\r
+  // Get the base of disk parameter information corresponding to its type.\r
+  //\r
+  Para        = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);\r
+  EndOfTrack  = Para->EndOfTrack;\r
+  //\r
+  // Calculate cylinder based on Lba and EOT\r
+  //\r
+  Cylinder = (UINT8) ((UINTN) Lba / EndOfTrack / 2);\r
+\r
+  //\r
+  // If the dest cylinder is the present cylinder, unnecessary to do the seek operation\r
+  //\r
+  if (Info->Pcn == Cylinder) {\r
+    return EFI_SUCCESS;\r
+  }\r
+\r
+  //\r
+  // Calculate the head : 0 or 1\r
+  //\r
+  Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);\r
+\r
+  ZeroMem (&Command, sizeof (FDC_SEEK_CMD));\r
+  Command.CommandCode = SEEK_CMD;\r
+  if (DevPos == 0) {\r
+    Command.DiskHeadSel = 0;\r
+  } else {\r
+    Command.DiskHeadSel = 1;\r
+  }\r
+\r
+  //\r
+  // Send command to move to destination cylinder.\r
+  //\r
+  Command.DiskHeadSel = (UINT8) (Command.DiskHeadSel | (Head << 2));\r
+  Command.NewCylinder = Cylinder;\r
+\r
+  Pointer = (UINT8 *) (&Command);\r
+  for (Index = 0; Index < sizeof (FDC_SEEK_CMD); Index++) {\r
+    if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  MicroSecondDelay (FDC_SHORT_DELAY);\r
+\r
+  //\r
+  // Calculate waiting time, which is proportional to the gap between destination\r
+  // cylinder and present cylinder.\r
+  //\r
+  if (Info->Pcn > Cylinder) {\r
+    Gap = (UINT8) (Info->Pcn - Cylinder);\r
+  } else {\r
+    Gap = (UINT8) (Cylinder - Info->Pcn);\r
+  }\r
+\r
+  MicroSecondDelay ((Gap + 1) * FDC_LONG_DELAY);\r
+\r
+  //\r
+  // Confirm if the new cylinder is the destination and status is correct.\r
+  //\r
+  if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if ((Sts0 & 0xf0) == BIT5) {\r
+    Info->Pcn             = Command.NewCylinder;\r
+    Info->NeedRecalibrate = FALSE;\r
+    return EFI_SUCCESS;\r
+  } else {\r
+    Info->NeedRecalibrate = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+}\r
+\r
+/**\r
+  Check if diskette is changed.\r
+\r
+  @param  FdcBlkIoDev       Instance of FDC_BLK_IO_DEV\r
+  @param  Info              Information of floppy device.\r
+\r
+  @retval EFI_SUCCESS       Diskette is not changed.\r
+  @retval EFI_MEDIA_CHANGED Diskette is changed.\r
+  @retval EFI_NO_MEDIA      No diskette.\r
+  @retval EFI_DEVICE_ERROR  Fail to do the seek or recalibrate operation.\r
+\r
+**/\r
+EFI_STATUS\r
+DisketChanged (\r
+  IN FDC_BLK_IO_DEV             *FdcBlkIoDev,\r
+  IN OUT PEI_FLOPPY_DEVICE_INFO *Info\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+  UINT8       Data;\r
+\r
+  //\r
+  // Check change line\r
+  //\r
+  Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DIR));\r
+\r
+  MicroSecondDelay (FDC_SHORT_DELAY);\r
+\r
+  if ((Data & DIR_DCL) == DIR_DCL) {\r
+    if (Info->Pcn != 0) {\r
+      Status = Recalibrate (FdcBlkIoDev, Info);\r
+    } else {\r
+      Status = Seek (FdcBlkIoDev, Info, 0x30);\r
+    }\r
+\r
+    if (Status != EFI_SUCCESS) {\r
+      //\r
+      // Fail to do the seek or recalibrate operation\r
+      //\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+\r
+    Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DIR));\r
+\r
+    MicroSecondDelay (FDC_SHORT_DELAY);\r
+\r
+    if ((Data & DIR_DCL) == DIR_DCL) {\r
+      return EFI_NO_MEDIA;\r
+    }\r
+\r
+    return EFI_MEDIA_CHANGED;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Detects if FDC device exists.\r
+\r
+  @param  FdcBlkIoDev  Instance of FDC_BLK_IO_DEV\r
+  @param  Info         Information of floppy device.\r
+  @param  MediaInfo    Information of floppy media.\r
+\r
+  @retval TRUE         FDC device exists and is working properly.\r
+  @retval FALSE        FDC device does not exist or cannot work properly.\r
+\r
+**/\r
+BOOLEAN\r
+DiscoverFdcDevice (\r
+  IN  FDC_BLK_IO_DEV             *FdcBlkIoDev,\r
+  IN  OUT PEI_FLOPPY_DEVICE_INFO *Info,\r
+  OUT EFI_PEI_BLOCK_IO_MEDIA     *MediaInfo\r
+  )\r
+{\r
+  EFI_STATUS        Status;\r
+  DISKET_PARA_TABLE *Para;\r
+\r
+  Status = MotorOn (FdcBlkIoDev, Info);\r
+  if (Status != EFI_SUCCESS) {\r
+    return FALSE;\r
+  }\r
+\r
+  Status = Recalibrate (FdcBlkIoDev, Info);\r
+\r
+  if (Status != EFI_SUCCESS) {\r
+    MotorOff (FdcBlkIoDev, Info);\r
+    return FALSE;\r
+  }\r
+  //\r
+  // Set Media Parameter\r
+  //\r
+  MediaInfo->DeviceType   = LegacyFloppy;\r
+  MediaInfo->MediaPresent = TRUE;\r
+\r
+  //\r
+  // Check Media\r
+  //\r
+  Status = DisketChanged (FdcBlkIoDev, Info);\r
+  switch (Status) {\r
+  case EFI_NO_MEDIA:\r
+    //\r
+    // No diskette in floppy.\r
+    //\r
+    MediaInfo->MediaPresent = FALSE;\r
+    break;\r
+\r
+  case EFI_MEDIA_CHANGED:\r
+  case EFI_SUCCESS:\r
+    //\r
+    // Diskette exists in floppy.\r
+    //\r
+    break;\r
+\r
+  default:\r
+    //\r
+    // EFI_DEVICE_ERROR\r
+    //\r
+    MotorOff (FdcBlkIoDev, Info);\r
+    return FALSE;\r
+  }\r
+\r
+  MotorOff (FdcBlkIoDev, Info);\r
+\r
+  //\r
+  // Get the base of disk parameter information corresponding to its type.\r
+  //\r
+  Para                  = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);\r
+\r
+  MediaInfo->BlockSize  = BytePerSector[Para->Number];\r
+  MediaInfo->LastBlock  = Para->EndOfTrack * 2 * (Para->MaxTrackNum + 1) - 1;\r
+\r
+  return TRUE;\r
+}\r
+\r
+/**\r
+  Enumerate floppy device\r
+\r
+  @param  FdcBlkIoDev  Instance of floppy device controller\r
+\r
+  @return Number of FDC devices.\r
+\r
+**/\r
+UINT8\r
+FdcEnumeration (\r
+  IN FDC_BLK_IO_DEV   *FdcBlkIoDev\r
+  )\r
+{\r
+  UINT8                   DevPos;\r
+  UINT8                   DevNo;\r
+  EFI_PEI_BLOCK_IO_MEDIA  MediaInfo;\r
+  EFI_STATUS              Status;\r
+\r
+  DevNo = 0;\r
+\r
+  //\r
+  // DevPos=0 means Drive A, 1 means Drive B.\r
+  //\r
+  for (DevPos = 0; DevPos < 2; DevPos++) {\r
+    //\r
+    // Detecting device presence\r
+    //\r
+    REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_PC_PRESENCE_DETECT);\r
+\r
+    //\r
+    // Reset FDC\r
+    //\r
+    Status = FdcReset (FdcBlkIoDev, DevPos);\r
+\r
+    if (EFI_ERROR (Status)) {\r
+      continue;\r
+    }\r
+\r
+    FdcBlkIoDev->DeviceInfo[DevPos].DevPos          = DevPos;\r
+    FdcBlkIoDev->DeviceInfo[DevPos].Pcn             = 0;\r
+    FdcBlkIoDev->DeviceInfo[DevPos].MotorOn         = FALSE;\r
+    FdcBlkIoDev->DeviceInfo[DevPos].NeedRecalibrate = TRUE;\r
+    FdcBlkIoDev->DeviceInfo[DevPos].Type            = FdcType1440K1440K;\r
+\r
+    //\r
+    // Discover FDC device\r
+    //\r
+    if (DiscoverFdcDevice (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DevPos]), &MediaInfo)) {\r
+      FdcBlkIoDev->DeviceInfo[DevNo].DevPos           = DevPos;\r
+\r
+      FdcBlkIoDev->DeviceInfo[DevNo].Pcn              = FdcBlkIoDev->DeviceInfo[DevPos].Pcn;\r
+      FdcBlkIoDev->DeviceInfo[DevNo].MotorOn          = FdcBlkIoDev->DeviceInfo[DevPos].MotorOn;\r
+      FdcBlkIoDev->DeviceInfo[DevNo].NeedRecalibrate  = FdcBlkIoDev->DeviceInfo[DevPos].NeedRecalibrate;\r
+      FdcBlkIoDev->DeviceInfo[DevNo].Type             = FdcBlkIoDev->DeviceInfo[DevPos].Type;\r
+\r
+      CopyMem (\r
+        &(FdcBlkIoDev->DeviceInfo[DevNo].MediaInfo),\r
+        &MediaInfo,\r
+        sizeof (EFI_PEI_BLOCK_IO_MEDIA)\r
+        );\r
+\r
+      DevNo++;\r
+    } else {\r
+      //\r
+      // Assume controller error\r
+      //\r
+      REPORT_STATUS_CODE (\r
+        EFI_ERROR_CODE | EFI_ERROR_MINOR,\r
+        EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_EC_CONTROLLER_ERROR\r
+        );\r
+    }\r
+  }\r
+\r
+  FdcBlkIoDev->DeviceCount = DevNo;\r
+  return DevNo;\r
+}\r
+\r
+/**\r
+  Checks result reflected by FDC_RESULT_PACKET.\r
+\r
+  @param  Result           FDC_RESULT_PACKET read from FDC after certain operation.\r
+  @param  Info             Information of floppy device.\r
+\r
+  @retval EFI_SUCCESS      Result is healthy.\r
+  @retval EFI_DEVICE_ERROR Result is not healthy.\r
+\r
+**/\r
+EFI_STATUS\r
+CheckResult (\r
+  IN  FDC_RESULT_PACKET         *Result,\r
+  OUT PEI_FLOPPY_DEVICE_INFO    *Info\r
+  )\r
+{\r
+  if ((Result->Status0 & STS0_IC) != IC_NT) {\r
+    if ((Result->Status0 & STS0_SE) == BIT5) {\r
+      //\r
+      // Seek error\r
+      //\r
+      Info->NeedRecalibrate = TRUE;\r
+    }\r
+\r
+    Info->NeedRecalibrate = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Check Status Register1\r
+  //\r
+  if ((Result->Status1 & (STS1_EN | STS1_DE | STS1_OR | STS1_ND | STS1_NW | STS1_MA)) != 0) {\r
+    Info->NeedRecalibrate = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Check Status Register2\r
+  //\r
+  if ((Result->Status2 & (STS2_CM | STS2_DD | STS2_WC | STS2_BC | STS2_MD)) != 0) {\r
+    Info->NeedRecalibrate = TRUE;\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Fill parameters for command packet.\r
+\r
+  @param  Info    Information of floppy device.\r
+  @param  Lba     Logical block address.\r
+  @param  Command Command for which for fill parameters.\r
+\r
+**/\r
+VOID\r
+FillPara (\r
+  IN  PEI_FLOPPY_DEVICE_INFO *Info,\r
+  IN  EFI_PEI_LBA            Lba,\r
+  OUT FDC_COMMAND_PACKET1    *Command\r
+  )\r
+{\r
+  DISKET_PARA_TABLE *Para;\r
+  UINT8             EndOfTrack;\r
+  UINT8             DevPos;\r
+\r
+  DevPos      = Info->DevPos;\r
+\r
+  //\r
+  // Get the base of disk parameter information corresponding to its type.\r
+  //\r
+  Para        = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);\r
+\r
+  EndOfTrack  = Para->EndOfTrack;\r
+\r
+  if (DevPos == 0) {\r
+    Command->DiskHeadSel = 0;\r
+  } else {\r
+    Command->DiskHeadSel = 1;\r
+  }\r
+\r
+  Command->Cylinder    = (UINT8) ((UINTN) Lba / EndOfTrack / 2);\r
+  Command->Head        = (UINT8) ((UINTN) Lba / EndOfTrack % 2);\r
+  Command->Sector      = (UINT8) ((UINT8) ((UINTN) Lba % EndOfTrack) + 1);\r
+  Command->DiskHeadSel = (UINT8) (Command->DiskHeadSel | (Command->Head << 2));\r
+  Command->Number      = Para->Number;\r
+  Command->EndOfTrack  = Para->EndOfTrack;\r
+  Command->GapLength   = Para->GapLength;\r
+  Command->DataLength  = Para->DataLength;\r
+}\r
+\r
+/**\r
+  Setup specifed FDC device.\r
+\r
+  @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.\r
+  @param  DevPos           Index of FDC device.\r
+\r
+  @retval EFI_SUCCESS      FDC device successfully set up.\r
+  @retval EFI_DEVICE_ERROR FDC device has errors.\r
+\r
+**/\r
+EFI_STATUS\r
+Setup (\r
+  IN  FDC_BLK_IO_DEV  *FdcBlkIoDev,\r
+  IN  UINT8           DevPos\r
+  )\r
+{\r
+  EFI_STATUS  Status;\r
+\r
+  IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_CCR), 0x0);\r
+\r
+  MicroSecondDelay (FDC_MEDIUM_DELAY);\r
+\r
+  Status = Specify (FdcBlkIoDev);\r
+  return Status;\r
+}\r
+\r
+/**\r
+  Setup DMA channels to read data.\r
+\r
+  @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.\r
+  @param  Buffer           Memory buffer for DMA transfer.\r
+  @param  BlockSize        the number of the bytes in one block.\r
+  @param  NumberOfBlocks   Number of blocks to read.\r
+\r
+**/\r
+VOID\r
+SetDMA (\r
+  IN FDC_BLK_IO_DEV   *FdcBlkIoDev,\r
+  IN VOID             *Buffer,\r
+  IN UINTN            BlockSize,\r
+  IN UINTN            NumberOfBlocks\r
+  )\r
+{\r
+  UINT8 Data;\r
+  UINTN Count;\r
+\r
+  //\r
+  // Mask DMA channel 2;\r
+  //\r
+  IoWrite8 (R_8237_DMA_WRSMSK_CH0_3, B_8237_DMA_WRSMSK_CMS | 2);\r
+\r
+  //\r
+  // Clear first/last flip flop\r
+  //\r
+  IoWrite8 (R_8237_DMA_CBPR_CH0_3, B_8237_DMA_WRSMSK_CMS | 2);\r
+\r
+  //\r
+  // Set mode\r
+  //\r
+  IoWrite8 (R_8237_DMA_CHMODE_CH0_3, V_8237_DMA_CHMODE_SINGLE | V_8237_DMA_CHMODE_IO2MEM | 2);\r
+\r
+  //\r
+  // Set base address and page register\r
+  //\r
+  Data = (UINT8) (UINTN) Buffer;\r
+  IoWrite8 (R_8237_DMA_BASE_CA_CH2, Data);\r
+  Data = (UINT8) ((UINTN) Buffer >> 8);\r
+  IoWrite8 (R_8237_DMA_BASE_CA_CH2, Data);\r
+\r
+  Data = (UINT8) ((UINTN) Buffer >> 16);\r
+  IoWrite8 (R_8237_DMA_MEM_LP_CH2, Data);\r
+\r
+  //\r
+  // Set count register\r
+  //\r
+  Count = BlockSize * NumberOfBlocks - 1;\r
+  Data  = (UINT8) (Count & 0xff);\r
+  IoWrite8 (R_8237_DMA_BASE_CC_CH2, Data);\r
+  Data = (UINT8) (Count >> 8);\r
+  IoWrite8 (R_8237_DMA_BASE_CC_CH2, Data);\r
+\r
+  //\r
+  // Clear channel 2 mask\r
+  //\r
+  IoWrite8 (R_8237_DMA_WRSMSK_CH0_3, 0x02);\r
+}\r
+\r
+\r
+/**\r
+  According to the block range specified by Lba and NumberOfBlocks, calculate\r
+  the number of blocks in the same sector, which can be transferred in a batch.\r
+\r
+  @param  Info           Information of floppy device.\r
+  @param  Lba            Start address of block range.\r
+  @param  NumberOfBlocks Number of blocks of the range.\r
+\r
+  @return Number of blocks in the same sector.\r
+\r
+**/\r
+UINTN\r
+GetTransferBlockCount (\r
+  IN  PEI_FLOPPY_DEVICE_INFO *Info,\r
+  IN  EFI_PEI_LBA            Lba,\r
+  IN  UINTN                  NumberOfBlocks\r
+  )\r
+{\r
+  DISKET_PARA_TABLE *Para;\r
+  UINT8             EndOfTrack;\r
+  UINT8             Head;\r
+  UINT8             SectorsInTrack;\r
+\r
+  //\r
+  // Get the base of disk parameter information corresponding to its type.\r
+  //\r
+  Para            = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);\r
+\r
+  EndOfTrack      = Para->EndOfTrack;\r
+  Head            = (UINT8) ((UINTN) Lba / EndOfTrack % 2);\r
+\r
+  SectorsInTrack  = (UINT8) (EndOfTrack * (2 - Head) - (UINT8) ((UINTN) Lba % EndOfTrack));\r
+  if (SectorsInTrack < NumberOfBlocks) {\r
+    //\r
+    // Not all the block range locates in the same sector\r
+    //\r
+    return SectorsInTrack;\r
+  } else {\r
+    //\r
+    // All the block range is in the same sector.\r
+    //\r
+    return NumberOfBlocks;\r
+  }\r
+}\r
+\r
+/**\r
+  Read data sector from FDC device.\r
+\r
+  @param  FdcBlkIoDev      Instance of FDC_BLK_IO_DEV.\r
+  @param  Info             Information of floppy device.\r
+  @param  Buffer           Buffer to setup for DMA.\r
+  @param  Lba              The start address to read.\r
+  @param  NumberOfBlocks   Number of blocks to read.\r
+\r
+  @retval EFI_SUCCESS      Data successfully read out.\r
+  @retval EFI_DEVICE_ERROR FDC device has errors.\r
+  @retval EFI_TIMEOUT      Command does not take effect in time.\r
+\r
+**/\r
+EFI_STATUS\r
+ReadDataSector (\r
+  IN     FDC_BLK_IO_DEV         *FdcBlkIoDev,\r
+  IN OUT PEI_FLOPPY_DEVICE_INFO *Info,\r
+  IN     VOID                   *Buffer,\r
+  IN     EFI_PEI_LBA            Lba,\r
+  IN     UINTN                  NumberOfBlocks\r
+  )\r
+{\r
+  EFI_STATUS          Status;\r
+  FDC_COMMAND_PACKET1 Command;\r
+  FDC_RESULT_PACKET   Result;\r
+  UINTN               Index;\r
+  UINTN               Times;\r
+  UINT8               *Pointer;\r
+\r
+  Status = Seek (FdcBlkIoDev, Info, Lba);\r
+  if (Status != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  //\r
+  // Set up DMA\r
+  //\r
+  SetDMA (FdcBlkIoDev, Buffer, Info->MediaInfo.BlockSize, NumberOfBlocks);\r
+\r
+  //\r
+  // Allocate Read command packet\r
+  //\r
+  ZeroMem (&Command, sizeof (FDC_COMMAND_PACKET1));\r
+  Command.CommandCode = READ_DATA_CMD | CMD_MT | CMD_MFM | CMD_SK;\r
+\r
+  //\r
+  // Fill parameters for command.\r
+  //\r
+  FillPara (Info, Lba, &Command);\r
+\r
+  //\r
+  // Write command bytes to FDC\r
+  //\r
+  Pointer = (UINT8 *) (&Command);\r
+  for (Index = 0; Index < sizeof (FDC_COMMAND_PACKET1); Index++) {\r
+    if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  //\r
+  // Wait for some time until command takes effect.\r
+  //\r
+  Times = (STALL_1_SECOND / FDC_CHECK_INTERVAL) + 1;\r
+  do {\r
+    if ((IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR)) & 0xc0) == 0xc0) {\r
+      break;\r
+    }\r
+\r
+    MicroSecondDelay (FDC_SHORT_DELAY);\r
+  } while (--Times > 0);\r
+\r
+  if (Times == 0) {\r
+    //\r
+    // Command fails to take effect in time, return EFI_TIMEOUT.\r
+    //\r
+    return EFI_TIMEOUT;\r
+  }\r
+\r
+  //\r
+  // Read result bytes from FDC\r
+  //\r
+  Pointer = (UINT8 *) (&Result);\r
+  for (Index = 0; Index < sizeof (FDC_RESULT_PACKET); Index++) {\r
+    if (DataInByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {\r
+      return EFI_DEVICE_ERROR;\r
+    }\r
+  }\r
+\r
+  return CheckResult (&Result, Info);\r
+}\r
+\r
+/**\r
+  Gets the count of block I/O devices that one specific block driver detects.\r
+\r
+  This function is used for getting the count of block I/O devices that one \r
+  specific block driver detects.  To the PEI ATAPI driver, it returns the number\r
+  of all the detected ATAPI devices it detects during the enumeration process. \r
+  To the PEI legacy floppy driver, it returns the number of all the legacy \r
+  devices it finds during its enumeration process. If no device is detected, \r
+  then the function will return zero.  \r
+  \r
+  @param[in]  PeiServices          General-purpose services that are available \r
+                                   to every PEIM.\r
+  @param[in]  This                 Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI \r
+                                   instance.\r
+  @param[out] NumberBlockDevices   The number of block I/O devices discovered.\r
+\r
+  @retval     EFI_SUCCESS          Operation performed successfully.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FdcGetNumberOfBlockDevices (\r
+  IN   EFI_PEI_SERVICES                  **PeiServices,\r
+  IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI     *This,\r
+  OUT  UINTN                             *NumberBlockDevices\r
+  )\r
+{\r
+  FDC_BLK_IO_DEV  *FdcBlkIoDev;\r
+\r
+  FdcBlkIoDev = NULL;\r
+\r
+  FdcBlkIoDev         = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);\r
+\r
+  *NumberBlockDevices = FdcBlkIoDev->DeviceCount;\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Gets a block device's media information.\r
+\r
+  This function will provide the caller with the specified block device's media \r
+  information. If the media changes, calling this function will update the media \r
+  information accordingly.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to every\r
+                            PEIM\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants \r
+                            to talk. Because the driver that implements Block I/O \r
+                            PPIs will manage multiple block devices, the PPIs that \r
+                            want to talk to a single device must specify the \r
+                            device index that was assigned during the enumeration\r
+                            process. This index is a number from one to \r
+                            NumberBlockDevices.\r
+  @param[out] MediaInfo     The media information of the specified block media.  \r
+                            The caller is responsible for the ownership of this \r
+                            data structure.\r
+  \r
+  @retval EFI_SUCCESS        Media information about the specified block device \r
+                             was obtained successfully.\r
+  @retval EFI_DEVICE_ERROR   Cannot get the media information due to a hardware \r
+                             error.\r
+  @retval Others             Other failure occurs.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FdcGetBlockDeviceMediaInfo (\r
+  IN   EFI_PEI_SERVICES                     **PeiServices,\r
+  IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI        *This,\r
+  IN   UINTN                                DeviceIndex,\r
+  OUT  EFI_PEI_BLOCK_IO_MEDIA               *MediaInfo\r
+  )\r
+{\r
+  UINTN           DeviceCount;\r
+  FDC_BLK_IO_DEV  *FdcBlkIoDev;\r
+  BOOLEAN         Healthy;\r
+  UINTN           Index;\r
+\r
+  FdcBlkIoDev = NULL;\r
+\r
+  if (This == NULL || MediaInfo == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);\r
+\r
+  DeviceCount = FdcBlkIoDev->DeviceCount;\r
+\r
+  //\r
+  // DeviceIndex is a value from 1 to NumberBlockDevices.\r
+  //\r
+  if ((DeviceIndex < 1) || (DeviceIndex > DeviceCount) || (DeviceIndex > 2)) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Index = DeviceIndex - 1;\r
+  //\r
+  // Probe media and retrieve latest media information\r
+  //\r
+  Healthy = DiscoverFdcDevice (\r
+              FdcBlkIoDev,\r
+              &FdcBlkIoDev->DeviceInfo[Index],\r
+              MediaInfo\r
+              );\r
+\r
+  if (!Healthy) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  CopyMem (\r
+    &(FdcBlkIoDev->DeviceInfo[Index].MediaInfo),\r
+    MediaInfo,\r
+    sizeof (EFI_PEI_BLOCK_IO_MEDIA)\r
+    );\r
+\r
+  return EFI_SUCCESS;\r
+}\r
+\r
+/**\r
+  Reads the requested number of blocks from the specified block device.\r
+\r
+  The function reads the requested number of blocks from the device. All the \r
+  blocks are read, or an error is returned. If there is no media in the device,\r
+  the function returns EFI_NO_MEDIA.\r
+\r
+  @param[in]  PeiServices   General-purpose services that are available to \r
+                            every PEIM.\r
+  @param[in]  This          Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.\r
+  @param[in]  DeviceIndex   Specifies the block device to which the function wants \r
+                            to talk. Because the driver that implements Block I/O \r
+                            PPIs will manage multiple block devices, the PPIs that \r
+                            want to talk to a single device must specify the device \r
+                            index that was assigned during the enumeration process. \r
+                            This index is a number from one to NumberBlockDevices.\r
+  @param[in]  StartLBA      The starting logical block address (LBA) to read from\r
+                            on the device\r
+  @param[in]  BufferSize    The size of the Buffer in bytes. This number must be\r
+                            a multiple of the intrinsic block size of the device.\r
+  @param[out] Buffer        A pointer to the destination buffer for the data.\r
+                            The caller is responsible for the ownership of the \r
+                            buffer.\r
+                         \r
+  @retval EFI_SUCCESS             The data was read correctly from the device.\r
+  @retval EFI_DEVICE_ERROR        The device reported an error while attempting \r
+                                  to perform the read operation.\r
+  @retval EFI_INVALID_PARAMETER   The read request contains LBAs that are not \r
+                                  valid, or the buffer is not properly aligned.\r
+  @retval EFI_NO_MEDIA            There is no media in the device.\r
+  @retval EFI_BAD_BUFFER_SIZE     The BufferSize parameter is not a multiple of\r
+                                  the intrinsic block size of the device.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FdcReadBlocks (\r
+  IN   EFI_PEI_SERVICES                  **PeiServices,\r
+  IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI     *This,\r
+  IN   UINTN                             DeviceIndex,\r
+  IN   EFI_PEI_LBA                       StartLBA,\r
+  IN   UINTN                             BufferSize,\r
+  OUT  VOID                              *Buffer\r
+  )\r
+{\r
+  EFI_PEI_BLOCK_IO_MEDIA MediaInfo;\r
+  EFI_STATUS            Status;\r
+  UINTN                 Count;\r
+  UINTN                 NumberOfBlocks;\r
+  UINTN                 BlockSize;\r
+  FDC_BLK_IO_DEV        *FdcBlkIoDev;\r
+  VOID                  *MemPage;\r
+\r
+  FdcBlkIoDev = NULL;\r
+\r
+  if (This == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);\r
+\r
+  if (Buffer == NULL) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  Status = FdcGetBlockDeviceMediaInfo (PeiServices, This, DeviceIndex, &MediaInfo);\r
+  if (Status != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  if (!MediaInfo.MediaPresent) {\r
+    return EFI_NO_MEDIA;\r
+  }\r
+\r
+  BlockSize = MediaInfo.BlockSize;\r
+\r
+  //\r
+  // If BufferSize cannot be divided by block size of FDC device,\r
+  // return EFI_BAD_BUFFER_SIZE.\r
+  //\r
+  if (BufferSize % BlockSize != 0) {\r
+    return EFI_BAD_BUFFER_SIZE;\r
+  }\r
+\r
+  NumberOfBlocks = BufferSize / BlockSize;\r
+\r
+  if ((StartLBA + NumberOfBlocks - 1) > FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].MediaInfo.LastBlock) {\r
+    return EFI_INVALID_PARAMETER;\r
+  }\r
+\r
+  MemPage = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize));\r
+  if ((MemPage == NULL) || ((UINTN) MemPage >= ISA_MAX_MEMORY_ADDRESS)) {\r
+    //\r
+    // If fail to allocate memory under ISA_MAX_MEMORY_ADDRESS, designate the address space for DMA\r
+    //\r
+    MemPage = (VOID *) ((UINTN) (UINT32) 0x0f00000);\r
+  }\r
+  Status = MotorOn (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));\r
+  if (Status != EFI_SUCCESS) {\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+\r
+  Status = Setup (FdcBlkIoDev, FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].DevPos);\r
+  if (Status != EFI_SUCCESS) {\r
+    MotorOff (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+  //\r
+  // Read data in batches.\r
+  // Blocks in the same cylinder are read out in a batch.\r
+  //\r
+  while ((Count = GetTransferBlockCount (\r
+                    &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]),\r
+                    StartLBA,\r
+                    NumberOfBlocks\r
+                    )) != 0 && Status == EFI_SUCCESS) {\r
+    Status = ReadDataSector (\r
+               FdcBlkIoDev,\r
+               &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]),\r
+               MemPage,\r
+               StartLBA,\r
+               Count\r
+               );\r
+    CopyMem (Buffer, MemPage, BlockSize * Count);\r
+    StartLBA += Count;\r
+    NumberOfBlocks -= Count;\r
+    Buffer = (VOID *) ((UINTN) Buffer + Count * BlockSize);\r
+  }\r
+\r
+  MotorOff (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));\r
+\r
+  switch (Status) {\r
+  case EFI_SUCCESS:\r
+    return EFI_SUCCESS;\r
+\r
+  default:\r
+    FdcReset (FdcBlkIoDev, FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].DevPos);\r
+    return EFI_DEVICE_ERROR;\r
+  }\r
+}\r
+\r
+/**\r
+  Initializes the floppy disk controller and installs FDC Block I/O PPI.\r
+\r
+  @param  FileHandle            Handle of the file being invoked.\r
+  @param  PeiServices           Describes the list of possible PEI Services.\r
+\r
+  @retval EFI_SUCCESS           Successfully initialized FDC and installed PPI.\r
+  @retval EFI_NOT_FOUND         Cannot find FDC device.\r
+  @retval EFI_OUT_OF_RESOURCES  Have no enough memory to create instance or descriptors.\r
+  @retval Other                 Fail to install FDC Block I/O PPI.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FdcPeimEntry (\r
+  IN  EFI_PEI_FILE_HANDLE         FileHandle,\r
+  IN  CONST EFI_PEI_SERVICES      **PeiServices\r
+  )\r
+{\r
+  EFI_STATUS            Status;\r
+  FDC_BLK_IO_DEV        *FdcBlkIoDev;\r
+  UINTN                 DeviceCount;\r
+  UINT32                Index;\r
+\r
+  Status = PeiServicesRegisterForShadow (FileHandle);\r
+  if (!EFI_ERROR (Status)) {\r
+    return Status;\r
+  }\r
+\r
+  //\r
+  // Allocate memory for instance of FDC_BLK_IO_DEV and copy initial value\r
+  // from template to it. \r
+  //\r
+  FdcBlkIoDev = AllocatePages (EFI_SIZE_TO_PAGES(sizeof (FDC_BLK_IO_DEV)));\r
+  if (FdcBlkIoDev == NULL) {\r
+    return EFI_OUT_OF_RESOURCES;\r
+  }\r
+  CopyMem (FdcBlkIoDev, &mBlockIoDevTemplate, sizeof (mBlockIoDevTemplate));\r
+\r
+  //\r
+  // Initialize DMA controller to enable all channels.\r
+  //\r
+  for (Index = 0; Index < sizeof (mRegisterTable) / sizeof (PEI_DMA_TABLE); Index++) {\r
+    IoWrite8 (mRegisterTable[Index].Register, mRegisterTable[Index].Value);\r
+  }\r
+  REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_PC_INIT);\r
+\r
+  //\r
+  // Enumerate FDC devices.\r
+  //\r
+  DeviceCount = FdcEnumeration (FdcBlkIoDev);\r
+  if (DeviceCount == 0) {\r
+    return EFI_NOT_FOUND;\r
+  }\r
+\r
+  FdcBlkIoDev->PpiDescriptor.Ppi = &FdcBlkIoDev->FdcBlkIo;\r
+\r
+  return PeiServicesInstallPpi (&FdcBlkIoDev->PpiDescriptor);\r
+}\r
diff --git a/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.h b/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.h
new file mode 100644 (file)
index 0000000..6477394
--- /dev/null
@@ -0,0 +1,246 @@
+/** @file\r
+Private include file for IsaFloppyPei PEIM.\r
+\r
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>\r
+  \r
+This program and the accompanying materials\r
+are licensed and made available under the terms and conditions\r
+of the BSD License which accompanies this distribution.  The\r
+full text of the license may be found at\r
+http://opensource.org/licenses/bsd-license.php\r
+\r
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+\r
+**/\r
+\r
+#ifndef _RECOVERY_FLOPPY_H_\r
+#define _RECOVERY_FLOPPY_H_\r
+\r
+#include <Ppi/BlockIo.h>\r
+\r
+#include <Library/DebugLib.h>\r
+#include <Library/PeimEntryPoint.h>\r
+#include <Library/PeiServicesLib.h>\r
+#include <Library/BaseMemoryLib.h>\r
+#include <Library/ReportStatusCodeLib.h>\r
+#include <Library/TimerLib.h>\r
+#include <Library/IoLib.h>\r
+#include <Library/MemoryAllocationLib.h>\r
+#include <Library/PcdLib.h>\r
+\r
+#include "Fdc.h"\r
+\r
+\r
+//\r
+// Some PC AT Compatible Device definitions\r
+//\r
+//\r
+// 8237 DMA registers\r
+//\r
+#define R_8237_DMA_BASE_CA_CH0                    0x00\r
+#define R_8237_DMA_BASE_CA_CH1                    0x02\r
+#define R_8237_DMA_BASE_CA_CH2                    0x04\r
+#define R_8237_DMA_BASE_CA_CH3                    0xd6\r
+#define R_8237_DMA_BASE_CA_CH5                    0xc4\r
+#define R_8237_DMA_BASE_CA_CH6                    0xc8\r
+#define R_8237_DMA_BASE_CA_CH7                    0xcc\r
+\r
+#define R_8237_DMA_BASE_CC_CH0                    0x01\r
+#define R_8237_DMA_BASE_CC_CH1                    0x03\r
+#define R_8237_DMA_BASE_CC_CH2                    0x05\r
+#define R_8237_DMA_BASE_CC_CH3                    0xd7\r
+#define R_8237_DMA_BASE_CC_CH5                    0xc6\r
+#define R_8237_DMA_BASE_CC_CH6                    0xca\r
+#define R_8237_DMA_BASE_CC_CH7                    0xce\r
+\r
+#define R_8237_DMA_MEM_LP_CH0                     0x87\r
+#define R_8237_DMA_MEM_LP_CH1                     0x83\r
+#define R_8237_DMA_MEM_LP_CH2                     0x81\r
+#define R_8237_DMA_MEM_LP_CH3                     0x82\r
+#define R_8237_DMA_MEM_LP_CH5                     0x8B\r
+#define R_8237_DMA_MEM_LP_CH6                     0x89\r
+#define R_8237_DMA_MEM_LP_CH7                     0x8A\r
+\r
+\r
+#define R_8237_DMA_COMMAND_CH0_3                  0x08\r
+#define R_8237_DMA_COMMAND_CH4_7                  0xd0\r
+#define   B_8237_DMA_COMMAND_GAP                  0x10\r
+#define   B_8237_DMA_COMMAND_CGE                  0x04\r
+\r
+\r
+#define R_8237_DMA_STA_CH0_3                      0x09\r
+#define R_8237_DMA_STA_CH4_7                      0xd2\r
+\r
+#define R_8237_DMA_WRSMSK_CH0_3                   0x0a\r
+#define R_8237_DMA_WRSMSK_CH4_7                   0xd4\r
+#define   B_8237_DMA_WRSMSK_CMS                   0x04\r
+\r
+\r
+#define R_8237_DMA_CHMODE_CH0_3                   0x0b\r
+#define R_8237_DMA_CHMODE_CH4_7                   0xd6\r
+#define   V_8237_DMA_CHMODE_DEMAND                0x00\r
+#define   V_8237_DMA_CHMODE_SINGLE                0x40\r
+#define   V_8237_DMA_CHMODE_CASCADE               0xc0\r
+#define   B_8237_DMA_CHMODE_DECREMENT             0x20\r
+#define   B_8237_DMA_CHMODE_INCREMENT             0x00\r
+#define   B_8237_DMA_CHMODE_AE                    0x10\r
+#define   V_8237_DMA_CHMODE_VERIFY                0\r
+#define   V_8237_DMA_CHMODE_IO2MEM                0x04\r
+#define   V_8237_DMA_CHMODE_MEM2IO                0x08\r
+\r
+#define R_8237_DMA_CBPR_CH0_3                     0x0c\r
+#define R_8237_DMA_CBPR_CH4_7                     0xd8\r
+\r
+#define R_8237_DMA_MCR_CH0_3                      0x0d\r
+#define R_8237_DMA_MCR_CH4_7                      0xda\r
+\r
+#define R_8237_DMA_CLMSK_CH0_3                    0x0e\r
+#define R_8237_DMA_CLMSK_CH4_7                    0xdc\r
+\r
+#define R_8237_DMA_WRMSK_CH0_3                    0x0f\r
+#define R_8237_DMA_WRMSK_CH4_7                    0xde\r
+\r
+///\r
+/// ISA memory range\r
+///\r
+#define ISA_MAX_MEMORY_ADDRESS  0x1000000 \r
+\r
+//\r
+// Macro for time delay & interval\r
+//\r
+#define STALL_1_SECOND           1000000\r
+#define STALL_1_MSECOND          1000\r
+#define FDC_CHECK_INTERVAL       50\r
+\r
+#define FDC_SHORT_DELAY          50\r
+#define FDC_MEDIUM_DELAY         100\r
+#define FDC_LONG_DELAY           4000\r
+#define FDC_RESET_DELAY          2000\r
+#define FDC_RECALIBRATE_DELAY    250000\r
+\r
+typedef enum {\r
+  FdcType360K360K  = 0,\r
+  FdcType360K1200K,\r
+  FdcType1200K1200K,\r
+  FdcType720K720K,\r
+  FdcType720K1440K,\r
+  FdcType1440K1440K,\r
+  FdcType720K2880K,\r
+  FdcType1440K2880K,\r
+  FdcType2880K2880K\r
+} FDC_DISKET_TYPE;\r
+\r
+typedef struct {\r
+  UINT8 Register;\r
+  UINT8 Value;\r
+} PEI_DMA_TABLE;\r
+\r
+typedef struct {\r
+  UINT8                      DevPos;\r
+  UINT8                      Pcn;\r
+  BOOLEAN                    MotorOn;\r
+  BOOLEAN                    NeedRecalibrate;\r
+  FDC_DISKET_TYPE            Type;\r
+  EFI_PEI_BLOCK_IO_MEDIA     MediaInfo;\r
+} PEI_FLOPPY_DEVICE_INFO;\r
+\r
+#define FDC_BLK_IO_DEV_SIGNATURE  SIGNATURE_32 ('F', 'b', 'i', 'o')\r
+\r
+typedef struct {\r
+  UINTN                           Signature;\r
+  EFI_PEI_RECOVERY_BLOCK_IO_PPI   FdcBlkIo;\r
+  EFI_PEI_PPI_DESCRIPTOR          PpiDescriptor;\r
+  UINTN                           DeviceCount;\r
+  PEI_FLOPPY_DEVICE_INFO          DeviceInfo[2];\r
+} FDC_BLK_IO_DEV;\r
+\r
+#define PEI_RECOVERY_FDC_FROM_BLKIO_THIS(a) CR (a, FDC_BLK_IO_DEV, FdcBlkIo, FDC_BLK_IO_DEV_SIGNATURE)\r
+\r
+//\r
+// PEI Recovery Block I/O PPI\r
+//\r
+\r
+/**\r
+  Get the number of FDC devices.\r
+\r
+  This function implements EFI_PEI_RECOVERY_BLOCK_IO_PPI.GetNumberOfBlockDevices.\r
+  It get the number of FDC devices in the system.\r
+\r
+  @param  PeiServices           An indirect pointer to the PEI Services Table published by the PEI Foundation.\r
+  @param  This                  Pointer to this PPI instance.\r
+  @param  NumberBlockDevices    Pointer to the the number of FDC devices for output.\r
+\r
+  @retval EFI_SUCCESS           Number of FDC devices is retrieved successfully.\r
+  @retval EFI_INVALID_PARAMETER Parameter This is NULL.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FdcGetNumberOfBlockDevices (\r
+  IN   EFI_PEI_SERVICES                  **PeiServices,\r
+  IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI     *This,\r
+  OUT  UINTN                             *NumberBlockDevices\r
+  );\r
+\r
+/**\r
+  Get the specified media information.\r
+\r
+  This function implements EFI_PEI_RECOVERY_BLOCK_IO_PPI.GetBlockDeviceMediaInfo.\r
+  It gets the specified media information.\r
+\r
+  @param  PeiServices           An indirect pointer to the PEI Services Table published by the PEI Foundation.\r
+  @param  This                  Pointer to this PPI instance.\r
+  @param  DeviceIndex           Index of FDC device to get information.\r
+  @param  MediaInfo             Pointer to the media info buffer for output.\r
+\r
+  @retval EFI_SUCCESS           Number of FDC devices is retrieved successfully.\r
+  @retval EFI_INVALID_PARAMETER Parameter This is NULL.\r
+  @retval EFI_INVALID_PARAMETER Parameter MediaInfo is NULL.\r
+  @retval EFI_INVALID_PARAMETER DeviceIndex is not valid.\r
+  @retval EFI_DEVICE_ERROR      FDC device does not exist or has errors.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FdcGetBlockDeviceMediaInfo (\r
+  IN   EFI_PEI_SERVICES                     **PeiServices,\r
+  IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI        *This,\r
+  IN   UINTN                                DeviceIndex,\r
+  OUT  EFI_PEI_BLOCK_IO_MEDIA               *MediaInfo\r
+  );\r
+\r
+/**\r
+  Get the requested number of blocks from the specified FDC device.\r
+\r
+  This function implements EFI_PEI_RECOVERY_BLOCK_IO_PPI.ReadBlocks.\r
+  It reads the requested number of blocks from the specified FDC device.\r
+\r
+  @param  PeiServices           An indirect pointer to the PEI Services Table published by the PEI Foundation.\r
+  @param  This                  Pointer to this PPI instance.\r
+  @param  DeviceIndex           Index of FDC device to get information.\r
+  @param  StartLba              The start LBA to read from.\r
+  @param  BufferSize            The size of range to read.\r
+  @param  Buffer                Buffer to hold the data read from FDC.\r
+\r
+  @retval EFI_SUCCESS           Number of FDC devices is retrieved successfully.\r
+  @retval EFI_INVALID_PARAMETER Parameter This is NULL.\r
+  @retval EFI_INVALID_PARAMETER Parameter Buffer is NULL.\r
+  @retval EFI_INVALID_PARAMETER Parameter BufferSize cannot be divided by block size of FDC device.\r
+  @retval EFI_NO_MEDIA          No media present.\r
+  @retval EFI_DEVICE_ERROR      FDC device has error.\r
+  @retval Others                Fail to read blocks.\r
+\r
+**/\r
+EFI_STATUS\r
+EFIAPI\r
+FdcReadBlocks (\r
+  IN   EFI_PEI_SERVICES                  **PeiServices,\r
+  IN   EFI_PEI_RECOVERY_BLOCK_IO_PPI     *This,\r
+  IN   UINTN                             DeviceIndex,\r
+  IN   EFI_PEI_LBA                       StartLba,\r
+  IN   UINTN                             BufferSize,\r
+  OUT  VOID                              *Buffer\r
+  );\r
+\r
+#endif\r
diff --git a/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/IsaFloppyPei.inf b/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/IsaFloppyPei.inf
new file mode 100644 (file)
index 0000000..2e2c4f3
--- /dev/null
@@ -0,0 +1,68 @@
+## @file\r
+# ISA Floppy PEIM to support recovery boot via floppy disk.\r
+#\r
+# This module detects Floppy devices. If found, it will install BlockIo PPI.\r
+#  This module is only dispatched in Recovery Boot mode.\r
+#\r
+# Copyright (c) 2006 - 2011, Intel Corporation. All rights reserved.<BR>\r
+#\r
+# This program and the accompanying materials\r
+# are licensed and made available under the terms and conditions\r
+# of the BSD License which accompanies this distribution.  The\r
+# full text of the license may be found at\r
+# http://opensource.org/licenses/bsd-license.php\r
+#\r
+# THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,\r
+# WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.\r
+#\r
+##\r
+\r
+################################################################################\r
+#\r
+# Defines Section - statements that will be processed to create a Makefile.\r
+#\r
+################################################################################\r
+[Defines]\r
+  INF_VERSION                    = 0x00010005\r
+  BASE_NAME                      = IsaFloppyPei\r
+  FILE_GUID                      = 7F6E0A24-DBFD-43df-9755-0292D7D3DD48\r
+  MODULE_TYPE                    = PEIM\r
+  VERSION_STRING                 = 1.0\r
+\r
+  ENTRY_POINT                    = FdcPeimEntry\r
+\r
+#\r
+# The following information is for reference only and not required by the build tools.\r
+#\r
+#  VALID_ARCHITECTURES           = IA32 X64 IPF EBC (EBC is for build only)\r
+#\r
+\r
+[Sources]\r
+  FloppyPeim.c\r
+  FloppyPeim.h\r
+  Fdc.h\r
+\r
+[Packages]\r
+  IntelFrameworkModulePkg/IntelFrameworkModulePkg.dec\r
+  MdePkg/MdePkg.dec\r
+\r
+[LibraryClasses]\r
+  IoLib\r
+  TimerLib\r
+  ReportStatusCodeLib\r
+  BaseMemoryLib\r
+  PeiServicesLib\r
+  PeimEntryPoint\r
+  DebugLib\r
+  MemoryAllocationLib\r
+  PcdLib\r
+\r
+[Ppis]\r
+  gEfiPeiVirtualBlockIoPpiGuid    # PPI ALWAYS_PRODUCED\r
+\r
+[Pcd]\r
+  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdFdcBaseAddress\r
+\r
+[Depex]\r
+  gEfiPeiMemoryDiscoveredPpiGuid AND gEfiPeiBootInRecoveryModePpiGuid\r
+\r
index 1679914c009de8cc895103a2e54b9c811f880df0..2af942d454481c8c5a332df40dc8e62d747ed8c5 100644 (file)
   ## Error level for hardware recorder. If value 0, platform does not support feature of hardware error record.\r
   #  This PCD should be set as HII type PCD by platform integrator mapped to variable L"HwErrRecSupport"\r
   gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdHardwareErrorRecordLevel|0|UINT16|0x40000002\r
+\r
+\r
+[PcdsFixedAtBuild, PcdsDynamic, PcdsDynamicEx, PcdsPatchableInModule]\r
+  ## I/O Base address of floppy device controller.\r
+  gEfiIntelFrameworkModulePkgTokenSpaceGuid.PcdFdcBaseAddress|0x3f0|UINT16|0x30000000\r
+\r
index d73b413f1b365d28c06478533a8baa37fe5f5e3b..fd2898e25935891546041b8e841fedba62f1fedd 100644 (file)
@@ -68,6 +68,7 @@
   DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf\r
   UefiRuntimeLib|MdePkg/Library/UefiRuntimeLib/UefiRuntimeLib.inf\r
   PcdLib|MdePkg/Library/DxePcdLib/DxePcdLib.inf\r
+  IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf\r
 \r
 [LibraryClasses.common.PEIM]\r
   HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf\r
   IntelFrameworkModulePkg/Bus/Pci/IdeBusDxe/IdeBusDxe.inf\r
   IntelFrameworkModulePkg/Bus/Isa/IsaBusDxe/IsaBusDxe.inf\r
   IntelFrameworkModulePkg/Bus/Isa/IsaIoDxe/IsaIoDxe.inf\r
+  IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/IsaFloppyPei.inf\r
   IntelFrameworkModulePkg/Bus/Isa/IsaFloppyDxe/IsaFloppyDxe.inf\r
   IntelFrameworkModulePkg/Bus/Isa/IsaSerialDxe/IsaSerialDxe.inf\r
   IntelFrameworkModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2keyboardDxe.inf\r