diff options
Diffstat (limited to 'IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.c')
-rw-r--r-- | IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.c | 1768 |
1 files changed, 1768 insertions, 0 deletions
diff --git a/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.c b/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.c new file mode 100644 index 0000000000..afeb2213c0 --- /dev/null +++ b/IntelFrameworkModulePkg/Bus/Isa/IsaFloppyPei/FloppyPeim.c @@ -0,0 +1,1768 @@ +/** @file
+Floppy Peim to support Recovery function from Floppy device.
+
+Copyright (c) 2006 - 2010, Intel Corporation. All rights reserved.<BR>
+
+This program and the accompanying materials
+are licensed and made available under the terms and conditions
+of the BSD License which accompanies this distribution. The
+full text of the license may be found at
+http://opensource.org/licenses/bsd-license.php
+
+THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
+WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
+
+**/
+
+
+#include "FloppyPeim.h"
+
+
+PEI_DMA_TABLE mRegisterTable[] = {
+ //
+ // DMA2: Clear Byte Ptr, Enable
+ //
+ {
+ R_8237_DMA_CBPR_CH4_7,
+ 0
+ },
+ {
+ R_8237_DMA_COMMAND_CH4_7,
+ 0
+ },
+ //
+ // DMA1: Clear Byte Ptr, Enable
+ //
+ {
+ R_8237_DMA_CBPR_CH0_3,
+ 0
+ },
+ {
+ R_8237_DMA_COMMAND_CH0_3,
+ 0
+ },
+ //
+ // Configure Channel 4 for Cascade Mode
+ // Clear DMA Request and enable DREQ
+ //
+ {
+ R_8237_DMA_CHMODE_CH4_7,
+ V_8237_DMA_CHMODE_CASCADE | 0
+ },
+ {
+ R_8237_DMA_STA_CH4_7,
+ 0
+ },
+ {
+ R_8237_DMA_WRSMSK_CH4_7,
+ 0
+ },
+ //
+ // Configure DMA1 (Channels 0-3) for Single Mode
+ // Clear DMA Request and enable DREQ
+ //
+ {
+ R_8237_DMA_CHMODE_CH0_3,
+ V_8237_DMA_CHMODE_SINGLE | 0
+ },
+ {
+ R_8237_DMA_STA_CH0_3,
+ 0
+ },
+ {
+ R_8237_DMA_WRSMSK_CH0_3,
+ 0
+ },
+ {
+ R_8237_DMA_CHMODE_CH0_3,
+ V_8237_DMA_CHMODE_SINGLE | 1
+ },
+ {
+ R_8237_DMA_STA_CH0_3,
+ 1
+ },
+ {
+ R_8237_DMA_WRSMSK_CH0_3,
+ 1
+ },
+ {
+ R_8237_DMA_CHMODE_CH0_3,
+ V_8237_DMA_CHMODE_SINGLE | 2
+ },
+ {
+ R_8237_DMA_STA_CH0_3,
+ 2
+ },
+ {
+ R_8237_DMA_WRSMSK_CH0_3,
+ 2
+ },
+ {
+ R_8237_DMA_CHMODE_CH0_3,
+ V_8237_DMA_CHMODE_SINGLE | 3
+ },
+ {
+ R_8237_DMA_STA_CH0_3,
+ 3
+ },
+ {
+ R_8237_DMA_WRSMSK_CH0_3,
+ 3
+ },
+ //
+ // Configure DMA2 (Channels 5-7) for Single Mode
+ // Clear DMA Request and enable DREQ
+ //
+ {
+ R_8237_DMA_CHMODE_CH4_7,
+ V_8237_DMA_CHMODE_SINGLE | 1
+ },
+ {
+ R_8237_DMA_STA_CH4_7,
+ 1
+ },
+ {
+ R_8237_DMA_WRSMSK_CH4_7,
+ 1
+ },
+ {
+ R_8237_DMA_CHMODE_CH4_7,
+ V_8237_DMA_CHMODE_SINGLE | 2
+ },
+ {
+ R_8237_DMA_STA_CH4_7,
+ 2
+ },
+ {
+ R_8237_DMA_WRSMSK_CH4_7,
+ 2
+ },
+ {
+ R_8237_DMA_CHMODE_CH4_7,
+ V_8237_DMA_CHMODE_SINGLE | 3
+ },
+ {
+ R_8237_DMA_STA_CH4_7,
+ 3
+ },
+ {
+ R_8237_DMA_WRSMSK_CH4_7,
+ 3
+ }
+};
+
+//
+// Table of diskette parameters of various diskette types
+//
+DISKET_PARA_TABLE DiskPara[9] = {
+ {
+ 0x09,
+ 0x50,
+ 0xff,
+ 0x2,
+ 0x27,
+ 0x4,
+ 0x25,
+ 0x14,
+ 0x80
+ },
+ {
+ 0x09,
+ 0x2a,
+ 0xff,
+ 0x2,
+ 0x27,
+ 0x4,
+ 0x25,
+ 0x0f,
+ 0x40
+ },
+ {
+ 0x0f,
+ 0x54,
+ 0xff,
+ 0x2,
+ 0x4f,
+ 0x4,
+ 0x25,
+ 0x0f,
+ 0x0
+ },
+ {
+ 0x09,
+ 0x50,
+ 0xff,
+ 0x2,
+ 0x4f,
+ 0x4,
+ 0x25,
+ 0x0f,
+ 0x80
+ },
+ {
+ 0x09,
+ 0x2a,
+ 0xff,
+ 0x2,
+ 0x4f,
+ 0x4,
+ 0x25,
+ 0x0f,
+ 0x80
+ },
+ {
+ 0x12,
+ 0x1b,
+ 0xff,
+ 0x2,
+ 0x4f,
+ 0x4,
+ 0x25,
+ 0x0f,
+ 0x0
+ },
+ {
+ 0x09,
+ 0x2a,
+ 0xff,
+ 0x2,
+ 0x4f,
+ 0x4,
+ 0x25,
+ 0x0f,
+ 0x80
+ },
+ {
+ 0x12,
+ 0x1b,
+ 0xff,
+ 0x2,
+ 0x4f,
+ 0x4,
+ 0x25,
+ 0x0f,
+ 0x0
+ },
+ {
+ 0x24,
+ 0x1b,
+ 0xff,
+ 0x2,
+ 0x4f,
+ 0x4,
+ 0x25,
+ 0x0f,
+ 0xc0
+ }
+};
+
+//
+// Byte per sector corresponding to various device types.
+//
+UINTN BytePerSector[6] = { 0, 256, 512, 1024, 2048, 4096 };
+
+FDC_BLK_IO_DEV mBlockIoDevTemplate = {
+ FDC_BLK_IO_DEV_SIGNATURE,
+ {
+ FdcGetNumberOfBlockDevices,
+ FdcGetBlockDeviceMediaInfo,
+ FdcReadBlocks,
+ },
+ {
+ (EFI_PEI_PPI_DESCRIPTOR_PPI | EFI_PEI_PPI_DESCRIPTOR_TERMINATE_LIST),
+ &gEfiPeiVirtualBlockIoPpiGuid,
+ NULL
+ },
+ 0,
+ {{0}}
+};
+
+/**
+ Wait and check if bits for DIO and RQM of FDC Main Status Register
+ indicates FDC is ready for read or write.
+
+ Before writing to FDC or reading from FDC, the Host must examine
+ the bit7(RQM) and bit6(DIO) of the Main Status Register.
+ That is to say:
+ Command bytes can not be written to Data Register unless RQM is 1 and DIO is 0.
+ Result bytes can not be read from Data Register unless RQM is 1 and DIO is 1.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param DataIn Indicates data input or output.
+ TRUE means input.
+ FALSE means output.
+ @param TimeoutInMseconds Timeout value to wait.
+
+ @retval EFI_SUCCESS FDC is ready.
+ @retval EFI_NOT_READY FDC is not ready within the specified time period.
+
+**/
+EFI_STATUS
+FdcDRQReady (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN BOOLEAN DataIn,
+ IN UINTN TimeoutInMseconds
+ )
+{
+ UINTN Delay;
+ UINT8 StatusRegister;
+ UINT8 BitInOut;
+
+ //
+ // Check bit6 of Main Status Register.
+ //
+ BitInOut = 0;
+ if (DataIn) {
+ BitInOut = BIT6;
+ }
+
+ Delay = ((TimeoutInMseconds * STALL_1_MSECOND) / FDC_CHECK_INTERVAL) + 1;
+ do {
+ StatusRegister = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR));
+ if ((StatusRegister & MSR_RQM) == MSR_RQM && (StatusRegister & MSR_DIO) == BitInOut) {
+ //
+ // FDC is ready
+ //
+ break;
+ }
+
+ MicroSecondDelay (FDC_SHORT_DELAY);
+ } while (--Delay > 0);
+
+ if (Delay == 0) {
+ //
+ // FDC is not ready within the specified time period
+ //
+ return EFI_NOT_READY;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Read a byte from FDC data register.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param Pointer Pointer to buffer to hold data read from FDC.
+
+ @retval EFI_SUCCESS Byte successfully read.
+ @retval EFI_DEVICE_ERROR FDC is not ready.
+
+**/
+EFI_STATUS
+DataInByte (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ OUT UINT8 *Pointer
+ )
+{
+ UINT8 Data;
+
+ //
+ // Wait for 1ms and detect the FDC is ready to be read
+ //
+ if (FdcDRQReady (FdcBlkIoDev, TRUE, 1) != EFI_SUCCESS) {
+ //
+ // FDC is not ready.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DTR));
+ MicroSecondDelay (FDC_SHORT_DELAY);
+ *Pointer = Data;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Write a byte to FDC data register.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param Pointer Pointer to data to write.
+
+ @retval EFI_SUCCESS Byte successfully written.
+ @retval EFI_DEVICE_ERROR FDC is not ready.
+
+**/
+EFI_STATUS
+DataOutByte (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN UINT8 *Pointer
+ )
+{
+ UINT8 Data;
+
+ //
+ // Wait for 1ms and detect the FDC is ready to be written
+ //
+ if (FdcDRQReady (FdcBlkIoDev, FALSE, 1) != EFI_SUCCESS) {
+ //
+ // FDC is not ready.
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ Data = *Pointer;
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DTR), Data);
+ MicroSecondDelay (FDC_SHORT_DELAY);
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Get Sts0 and Pcn status from FDC
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV
+ @param Sts0 Value of Sts0
+ @param Pcn Value of Pcn
+
+ @retval EFI_SUCCESS Successfully retrieved status value of Sts0 and Pcn.
+ @retval EFI_DEVICE_ERROR Fail to send SENSE_INT_STATUS_CMD.
+ @retval EFI_DEVICE_ERROR Fail to read Sts0.
+ @retval EFI_DEVICE_ERROR Fail to read Pcn.
+
+**/
+EFI_STATUS
+SenseIntStatus (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ OUT UINT8 *Sts0,
+ OUT UINT8 *Pcn
+ )
+{
+ UINT8 Command;
+
+ Command = SENSE_INT_STATUS_CMD;
+
+ if (DataOutByte (FdcBlkIoDev, &Command) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (DataInByte (FdcBlkIoDev, Sts0) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (DataInByte (FdcBlkIoDev, Pcn) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Issue Specify command.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+
+ @retval EFI_SUCCESS Specify command successfully issued.
+ @retval EFI_DEVICE_ERROR FDC device has errors.
+
+**/
+EFI_STATUS
+Specify (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev
+ )
+{
+ FDC_SPECIFY_CMD Command;
+ UINTN Index;
+ UINT8 *Pointer;
+
+ ZeroMem (&Command, sizeof (FDC_SPECIFY_CMD));
+ Command.CommandCode = SPECIFY_CMD;
+ //
+ // set SRT, HUT
+ //
+ Command.SrtHut = 0xdf;
+ //
+ // 0xdf;
+ // set HLT and DMA
+ //
+ Command.HltNd = 0x02;
+
+ Pointer = (UINT8 *) (&Command);
+ for (Index = 0; Index < sizeof (FDC_SPECIFY_CMD); Index++) {
+ if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Wait until busy bit is cleared.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param DevPos Position of FDC (Driver A or B)
+ @param TimeoutInMseconds Timeout value to wait.
+
+ @retval EFI_SUCCESS Busy bit has been cleared before timeout.
+ @retval EFI_TIMEOUT Time goes out before busy bit is cleared.
+
+**/
+EFI_STATUS
+FdcWaitForBSYClear (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN UINT8 DevPos,
+ IN UINTN TimeoutInMseconds
+ )
+{
+ UINTN Delay;
+ UINT8 StatusRegister;
+ UINT8 Mask;
+
+ //
+ // How to determine drive and command are busy or not: by the bits of Main Status Register
+ // bit0: Drive 0 busy (drive A)
+ // bit1: Drive 1 busy (drive B)
+ // bit4: Command busy
+ //
+ // set mask: for drive A set bit0 & bit4; for drive B set bit1 & bit4
+ //
+ Mask = (UINT8) ((DevPos == 0 ? MSR_DAB : MSR_DBB) | MSR_CB);
+
+ Delay = ((TimeoutInMseconds * STALL_1_MSECOND) / FDC_CHECK_INTERVAL) + 1;
+
+ do {
+ StatusRegister = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR));
+
+ if ((StatusRegister & Mask) == 0x00) {
+ //
+ // not busy
+ //
+ break;
+ }
+
+ MicroSecondDelay (FDC_SHORT_DELAY);
+ } while (--Delay > 0);
+
+ if (Delay == 0) {
+ return EFI_TIMEOUT;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Reset FDC device.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV
+ @param DevPos Index of FDC device.
+
+ @retval EFI_SUCCESS FDC device successfully reset.
+ @retval EFI_DEVICE_ERROR Fail to reset FDC device.
+
+**/
+EFI_STATUS
+FdcReset (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN UINT8 DevPos
+ )
+{
+ UINT8 Data;
+ UINT8 Sts0;
+ UINT8 Pcn;
+ UINTN Index;
+
+ //
+ // Reset specified Floppy Logic Drive according to Fdd -> Disk
+ // Set Digital Output Register(DOR) to do reset work
+ // bit0 & bit1 of DOR : Drive Select
+ // bit2 : Reset bit
+ // bit3 : DMA and Int bit
+ // Reset : A "0" written to bit2 resets the FDC, this reset will remain active until
+ // a "1" is written to this bit.
+ // Reset step 1:
+ // use bit0 & bit1 to select the logic drive
+ // write "0" to bit2
+ //
+ Data = 0x0;
+ Data = (UINT8) (Data | (SELECT_DRV & DevPos));
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);
+
+ //
+ // Wait some time, at least 120us.
+ //
+ MicroSecondDelay (FDC_RESET_DELAY);
+ //
+ // Reset step 2:
+ // write "1" to bit2
+ // write "1" to bit3 : enable DMA
+ //
+ Data |= 0x0C;
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);
+
+ MicroSecondDelay (FDC_RESET_DELAY);
+
+ //
+ // Wait until specified floppy logic drive is not busy
+ //
+ if (FdcWaitForBSYClear (FdcBlkIoDev, DevPos, 1) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Set the Transfer Data Rate
+ //
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_CCR), 0x0);
+
+ MicroSecondDelay (FDC_MEDIUM_DELAY);
+
+ //
+ // Issue Sense interrupt command for each drive (totally 4 drives)
+ //
+ for (Index = 0; Index < 4; Index++) {
+ if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+ //
+ // Issue Specify command
+ //
+ if (Specify (FdcBlkIoDev) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Turn on the motor of floppy drive.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param Info Information of floppy device.
+
+ @retval EFI_SUCCESS Motor is successfully turned on.
+ @retval EFI_SUCCESS Motor is already on.
+ @retval EFI_DEVICE_ERROR Busy bit of FDC cannot be cleared.
+
+**/
+EFI_STATUS
+MotorOn (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info
+ )
+{
+ UINT8 Data;
+ UINT8 DevPos;
+
+ //
+ // Control of the floppy drive motors is a big pain. If motor is off, you have to turn it
+ // on first. But you can not leave the motor on all the time, since that would wear out the
+ // disk. On the other hand, if you turn the motor off after each operation, the system performance
+ // will be awful. The compromise used in this driver is to leave the motor on for 2 seconds after
+ // each operation. If a new operation is started in that interval(2s), the motor need not be
+ // turned on again. If no new operation is started, a timer goes off and the motor is turned off.
+ //
+ DevPos = Info->DevPos;
+
+ //
+ // If the Motor is already on, just return EFI_SUCCESS.
+ //
+ if (Info->MotorOn) {
+ return EFI_SUCCESS;
+ }
+ //
+ // The drive's motor is off, so need turn it on.
+ // First check if command and drive are busy or not.
+ //
+ if (FdcWaitForBSYClear (FdcBlkIoDev, DevPos, 1) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // for drive A: 1CH, drive B: 2DH
+ //
+ Data = 0x0C;
+ Data = (UINT8) (Data | (SELECT_DRV & DevPos));
+ if (DevPos == 0) {
+ Data |= DRVA_MOTOR_ON;
+ } else {
+ Data |= DRVB_MOTOR_ON;
+ }
+
+ Info->MotorOn = FALSE;
+
+ //
+ // Turn on the motor and wait for some time to ensure it takes effect.
+ //
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);
+ MicroSecondDelay (FDC_LONG_DELAY);
+
+ Info->MotorOn = TRUE;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Turn off the motor of floppy drive.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param Info Information of floppy device.
+
+**/
+VOID
+MotorOff (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info
+ )
+{
+ UINT8 Data;
+ UINT8 DevPos;
+
+ DevPos = Info->DevPos;
+
+ if (!Info->MotorOn) {
+ return;
+ }
+ //
+ // The motor is on, so need motor off
+ //
+ Data = 0x0C;
+ Data = (UINT8) (Data | (SELECT_DRV & DevPos));
+
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DOR), Data);
+ MicroSecondDelay (FDC_SHORT_DELAY);
+
+ Info->MotorOn = FALSE;
+}
+
+/**
+ Recalibrate the FDC device.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param Info Information of floppy device.
+
+ @retval EFI_SUCCESS FDC successfully recalibrated.
+ @retval EFI_DEVICE_ERROR Fail to send RECALIBRATE_CMD.
+ @retval EFI_DEVICE_ERROR Fail to get status value of Sts0 and Pcn.
+ @retval EFI_DEVICE_ERROR Fail to recalibrate FDC device.
+
+**/
+EFI_STATUS
+Recalibrate (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info
+ )
+{
+ FDC_COMMAND_PACKET2 Command;
+ UINTN Index;
+ UINT8 Sts0;
+ UINT8 Pcn;
+ UINT8 *Pointer;
+ UINT8 Count;
+ UINT8 DevPos;
+
+ DevPos = Info->DevPos;
+
+ //
+ // We would try twice.
+ //
+ Count = 2;
+ while (Count > 0) {
+ ZeroMem (&Command, sizeof (FDC_COMMAND_PACKET2));
+ Command.CommandCode = RECALIBRATE_CMD;
+ //
+ // drive select
+ //
+ if (DevPos == 0) {
+ Command.DiskHeadSel = 0;
+ } else {
+ Command.DiskHeadSel = 1;
+ }
+
+ Pointer = (UINT8 *) (&Command);
+ for (Index = 0; Index < sizeof (FDC_COMMAND_PACKET2); Index++) {
+ if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ MicroSecondDelay (FDC_RECALIBRATE_DELAY);
+
+ if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if ((Sts0 & 0xf0) == BIT5 && Pcn == 0) {
+ //
+ // Recalibration is successful.
+ //
+ Info->Pcn = 0;
+ Info->NeedRecalibrate = FALSE;
+
+ return EFI_SUCCESS;
+ } else {
+ //
+ // Recalibration is not successful. Try again.
+ // If trial is used out, return EFI_DEVICE_ERROR.
+ //
+ Count--;
+ if (Count == 0) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Seek for the cylinder according to given LBA.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param Info Information of floppy device.
+ @param Lba LBA for which to seek for cylinder.
+
+ @retval EFI_SUCCESS Successfully moved to the destination cylinder.
+ @retval EFI_SUCCESS Destination cylinder is just the present cylinder.
+ @retval EFI_DEVICE_ERROR Fail to move to the destination cylinder.
+
+**/
+EFI_STATUS
+Seek (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info,
+ IN EFI_PEI_LBA Lba
+ )
+{
+ FDC_SEEK_CMD Command;
+ DISKET_PARA_TABLE *Para;
+ UINT8 EndOfTrack;
+ UINT8 Head;
+ UINT8 Cylinder;
+ UINT8 Sts0;
+ UINT8 *Pointer;
+ UINT8 Pcn;
+ UINTN Index;
+ UINT8 Gap;
+ UINT8 DevPos;
+
+ DevPos = Info->DevPos;
+ if (Info->NeedRecalibrate) {
+ if (Recalibrate (FdcBlkIoDev, Info) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Recalibrate Success
+ //
+ Info->NeedRecalibrate = FALSE;
+ }
+
+ //
+ // Get the base of disk parameter information corresponding to its type.
+ //
+ Para = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);
+ EndOfTrack = Para->EndOfTrack;
+ //
+ // Calculate cylinder based on Lba and EOT
+ //
+ Cylinder = (UINT8) ((UINTN) Lba / EndOfTrack / 2);
+
+ //
+ // If the dest cylinder is the present cylinder, unnecessary to do the seek operation
+ //
+ if (Info->Pcn == Cylinder) {
+ return EFI_SUCCESS;
+ }
+
+ //
+ // Calculate the head : 0 or 1
+ //
+ Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
+
+ ZeroMem (&Command, sizeof (FDC_SEEK_CMD));
+ Command.CommandCode = SEEK_CMD;
+ if (DevPos == 0) {
+ Command.DiskHeadSel = 0;
+ } else {
+ Command.DiskHeadSel = 1;
+ }
+
+ //
+ // Send command to move to destination cylinder.
+ //
+ Command.DiskHeadSel = (UINT8) (Command.DiskHeadSel | (Head << 2));
+ Command.NewCylinder = Cylinder;
+
+ Pointer = (UINT8 *) (&Command);
+ for (Index = 0; Index < sizeof (FDC_SEEK_CMD); Index++) {
+ if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ MicroSecondDelay (FDC_SHORT_DELAY);
+
+ //
+ // Calculate waiting time, which is proportional to the gap between destination
+ // cylinder and present cylinder.
+ //
+ if (Info->Pcn > Cylinder) {
+ Gap = (UINT8) (Info->Pcn - Cylinder);
+ } else {
+ Gap = (UINT8) (Cylinder - Info->Pcn);
+ }
+
+ MicroSecondDelay ((Gap + 1) * FDC_LONG_DELAY);
+
+ //
+ // Confirm if the new cylinder is the destination and status is correct.
+ //
+ if (SenseIntStatus (FdcBlkIoDev, &Sts0, &Pcn) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if ((Sts0 & 0xf0) == BIT5) {
+ Info->Pcn = Command.NewCylinder;
+ Info->NeedRecalibrate = FALSE;
+ return EFI_SUCCESS;
+ } else {
+ Info->NeedRecalibrate = TRUE;
+ return EFI_DEVICE_ERROR;
+ }
+}
+
+/**
+ Check if diskette is changed.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV
+ @param Info Information of floppy device.
+
+ @retval EFI_SUCCESS Diskette is not changed.
+ @retval EFI_MEDIA_CHANGED Diskette is changed.
+ @retval EFI_NO_MEDIA No diskette.
+ @retval EFI_DEVICE_ERROR Fail to do the seek or recalibrate operation.
+
+**/
+EFI_STATUS
+DisketChanged (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info
+ )
+{
+ EFI_STATUS Status;
+ UINT8 Data;
+
+ //
+ // Check change line
+ //
+ Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DIR));
+
+ MicroSecondDelay (FDC_SHORT_DELAY);
+
+ if ((Data & DIR_DCL) == DIR_DCL) {
+ if (Info->Pcn != 0) {
+ Status = Recalibrate (FdcBlkIoDev, Info);
+ } else {
+ Status = Seek (FdcBlkIoDev, Info, 0x30);
+ }
+
+ if (Status != EFI_SUCCESS) {
+ //
+ // Fail to do the seek or recalibrate operation
+ //
+ return EFI_DEVICE_ERROR;
+ }
+
+ Data = IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_DIR));
+
+ MicroSecondDelay (FDC_SHORT_DELAY);
+
+ if ((Data & DIR_DCL) == DIR_DCL) {
+ return EFI_NO_MEDIA;
+ }
+
+ return EFI_MEDIA_CHANGED;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Detects if FDC device exists.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV
+ @param Info Information of floppy device.
+ @param MediaInfo Information of floppy media.
+
+ @retval TRUE FDC device exists and is working properly.
+ @retval FALSE FDC device does not exist or cannot work properly.
+
+**/
+BOOLEAN
+DiscoverFdcDevice (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info,
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ )
+{
+ EFI_STATUS Status;
+ DISKET_PARA_TABLE *Para;
+
+ Status = MotorOn (FdcBlkIoDev, Info);
+ if (Status != EFI_SUCCESS) {
+ return FALSE;
+ }
+
+ Status = Recalibrate (FdcBlkIoDev, Info);
+
+ if (Status != EFI_SUCCESS) {
+ MotorOff (FdcBlkIoDev, Info);
+ return FALSE;
+ }
+ //
+ // Set Media Parameter
+ //
+ MediaInfo->DeviceType = LegacyFloppy;
+ MediaInfo->MediaPresent = TRUE;
+
+ //
+ // Check Media
+ //
+ Status = DisketChanged (FdcBlkIoDev, Info);
+ switch (Status) {
+ case EFI_NO_MEDIA:
+ //
+ // No diskette in floppy.
+ //
+ MediaInfo->MediaPresent = FALSE;
+ break;
+
+ case EFI_MEDIA_CHANGED:
+ case EFI_SUCCESS:
+ //
+ // Diskette exists in floppy.
+ //
+ break;
+
+ default:
+ //
+ // EFI_DEVICE_ERROR
+ //
+ MotorOff (FdcBlkIoDev, Info);
+ return FALSE;
+ }
+
+ MotorOff (FdcBlkIoDev, Info);
+
+ //
+ // Get the base of disk parameter information corresponding to its type.
+ //
+ Para = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);
+
+ MediaInfo->BlockSize = BytePerSector[Para->Number];
+ MediaInfo->LastBlock = Para->EndOfTrack * 2 * (Para->MaxTrackNum + 1) - 1;
+
+ return TRUE;
+}
+
+/**
+ Enumerate floppy device
+
+ @param FdcBlkIoDev Instance of floppy device controller
+
+ @return Number of FDC devices.
+
+**/
+UINT8
+FdcEnumeration (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev
+ )
+{
+ UINT8 DevPos;
+ UINT8 DevNo;
+ EFI_PEI_BLOCK_IO_MEDIA MediaInfo;
+ EFI_STATUS Status;
+
+ DevNo = 0;
+
+ //
+ // DevPos=0 means Drive A, 1 means Drive B.
+ //
+ for (DevPos = 0; DevPos < 2; DevPos++) {
+ //
+ // Detecting device presence
+ //
+ REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_PC_PRESENCE_DETECT);
+
+ //
+ // Reset FDC
+ //
+ Status = FdcReset (FdcBlkIoDev, DevPos);
+
+ if (EFI_ERROR (Status)) {
+ continue;
+ }
+
+ FdcBlkIoDev->DeviceInfo[DevPos].DevPos = DevPos;
+ FdcBlkIoDev->DeviceInfo[DevPos].Pcn = 0;
+ FdcBlkIoDev->DeviceInfo[DevPos].MotorOn = FALSE;
+ FdcBlkIoDev->DeviceInfo[DevPos].NeedRecalibrate = TRUE;
+ FdcBlkIoDev->DeviceInfo[DevPos].Type = FdcType1440K1440K;
+
+ //
+ // Discover FDC device
+ //
+ if (DiscoverFdcDevice (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DevPos]), &MediaInfo)) {
+ FdcBlkIoDev->DeviceInfo[DevNo].DevPos = DevPos;
+
+ FdcBlkIoDev->DeviceInfo[DevNo].Pcn = FdcBlkIoDev->DeviceInfo[DevPos].Pcn;
+ FdcBlkIoDev->DeviceInfo[DevNo].MotorOn = FdcBlkIoDev->DeviceInfo[DevPos].MotorOn;
+ FdcBlkIoDev->DeviceInfo[DevNo].NeedRecalibrate = FdcBlkIoDev->DeviceInfo[DevPos].NeedRecalibrate;
+ FdcBlkIoDev->DeviceInfo[DevNo].Type = FdcBlkIoDev->DeviceInfo[DevPos].Type;
+
+ CopyMem (
+ &(FdcBlkIoDev->DeviceInfo[DevNo].MediaInfo),
+ &MediaInfo,
+ sizeof (EFI_PEI_BLOCK_IO_MEDIA)
+ );
+
+ DevNo++;
+ } else {
+ //
+ // Assume controller error
+ //
+ REPORT_STATUS_CODE (
+ EFI_ERROR_CODE | EFI_ERROR_MINOR,
+ EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_EC_CONTROLLER_ERROR
+ );
+ }
+ }
+
+ FdcBlkIoDev->DeviceCount = DevNo;
+ return DevNo;
+}
+
+/**
+ Checks result reflected by FDC_RESULT_PACKET.
+
+ @param Result FDC_RESULT_PACKET read from FDC after certain operation.
+ @param Info Information of floppy device.
+
+ @retval EFI_SUCCESS Result is healthy.
+ @retval EFI_DEVICE_ERROR Result is not healthy.
+
+**/
+EFI_STATUS
+CheckResult (
+ IN FDC_RESULT_PACKET *Result,
+ OUT PEI_FLOPPY_DEVICE_INFO *Info
+ )
+{
+ if ((Result->Status0 & STS0_IC) != IC_NT) {
+ if ((Result->Status0 & STS0_SE) == BIT5) {
+ //
+ // Seek error
+ //
+ Info->NeedRecalibrate = TRUE;
+ }
+
+ Info->NeedRecalibrate = TRUE;
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Check Status Register1
+ //
+ if ((Result->Status1 & (STS1_EN | STS1_DE | STS1_OR | STS1_ND | STS1_NW | STS1_MA)) != 0) {
+ Info->NeedRecalibrate = TRUE;
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Check Status Register2
+ //
+ if ((Result->Status2 & (STS2_CM | STS2_DD | STS2_WC | STS2_BC | STS2_MD)) != 0) {
+ Info->NeedRecalibrate = TRUE;
+ return EFI_DEVICE_ERROR;
+ }
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Fill parameters for command packet.
+
+ @param Info Information of floppy device.
+ @param Lba Logical block address.
+ @param Command Command for which for fill parameters.
+
+**/
+VOID
+FillPara (
+ IN PEI_FLOPPY_DEVICE_INFO *Info,
+ IN EFI_PEI_LBA Lba,
+ OUT FDC_COMMAND_PACKET1 *Command
+ )
+{
+ DISKET_PARA_TABLE *Para;
+ UINT8 EndOfTrack;
+ UINT8 DevPos;
+
+ DevPos = Info->DevPos;
+
+ //
+ // Get the base of disk parameter information corresponding to its type.
+ //
+ Para = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);
+
+ EndOfTrack = Para->EndOfTrack;
+
+ if (DevPos == 0) {
+ Command->DiskHeadSel = 0;
+ } else {
+ Command->DiskHeadSel = 1;
+ }
+
+ Command->Cylinder = (UINT8) ((UINTN) Lba / EndOfTrack / 2);
+ Command->Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
+ Command->Sector = (UINT8) ((UINT8) ((UINTN) Lba % EndOfTrack) + 1);
+ Command->DiskHeadSel = (UINT8) (Command->DiskHeadSel | (Command->Head << 2));
+ Command->Number = Para->Number;
+ Command->EndOfTrack = Para->EndOfTrack;
+ Command->GapLength = Para->GapLength;
+ Command->DataLength = Para->DataLength;
+}
+
+/**
+ Setup specifed FDC device.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param DevPos Index of FDC device.
+
+ @retval EFI_SUCCESS FDC device successfully set up.
+ @retval EFI_DEVICE_ERROR FDC device has errors.
+
+**/
+EFI_STATUS
+Setup (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN UINT8 DevPos
+ )
+{
+ EFI_STATUS Status;
+
+ IoWrite8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_CCR), 0x0);
+
+ MicroSecondDelay (FDC_MEDIUM_DELAY);
+
+ Status = Specify (FdcBlkIoDev);
+ return Status;
+}
+
+/**
+ Setup DMA channels to read data.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param Buffer Memory buffer for DMA transfer.
+ @param BlockSize the number of the bytes in one block.
+ @param NumberOfBlocks Number of blocks to read.
+
+**/
+VOID
+SetDMA (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN VOID *Buffer,
+ IN UINTN BlockSize,
+ IN UINTN NumberOfBlocks
+ )
+{
+ UINT8 Data;
+ UINTN Count;
+
+ //
+ // Mask DMA channel 2;
+ //
+ IoWrite8 (R_8237_DMA_WRSMSK_CH0_3, B_8237_DMA_WRSMSK_CMS | 2);
+
+ //
+ // Clear first/last flip flop
+ //
+ IoWrite8 (R_8237_DMA_CBPR_CH0_3, B_8237_DMA_WRSMSK_CMS | 2);
+
+ //
+ // Set mode
+ //
+ IoWrite8 (R_8237_DMA_CHMODE_CH0_3, V_8237_DMA_CHMODE_SINGLE | V_8237_DMA_CHMODE_IO2MEM | 2);
+
+ //
+ // Set base address and page register
+ //
+ Data = (UINT8) (UINTN) Buffer;
+ IoWrite8 (R_8237_DMA_BASE_CA_CH2, Data);
+ Data = (UINT8) ((UINTN) Buffer >> 8);
+ IoWrite8 (R_8237_DMA_BASE_CA_CH2, Data);
+
+ Data = (UINT8) ((UINTN) Buffer >> 16);
+ IoWrite8 (R_8237_DMA_MEM_LP_CH2, Data);
+
+ //
+ // Set count register
+ //
+ Count = BlockSize * NumberOfBlocks - 1;
+ Data = (UINT8) (Count & 0xff);
+ IoWrite8 (R_8237_DMA_BASE_CC_CH2, Data);
+ Data = (UINT8) (Count >> 8);
+ IoWrite8 (R_8237_DMA_BASE_CC_CH2, Data);
+
+ //
+ // Clear channel 2 mask
+ //
+ IoWrite8 (R_8237_DMA_WRSMSK_CH0_3, 0x02);
+}
+
+
+/**
+ According to the block range specified by Lba and NumberOfBlocks, calculate
+ the number of blocks in the same sector, which can be transferred in a batch.
+
+ @param Info Information of floppy device.
+ @param Lba Start address of block range.
+ @param NumberOfBlocks Number of blocks of the range.
+
+ @return Number of blocks in the same sector.
+
+**/
+UINTN
+GetTransferBlockCount (
+ IN PEI_FLOPPY_DEVICE_INFO *Info,
+ IN EFI_PEI_LBA Lba,
+ IN UINTN NumberOfBlocks
+ )
+{
+ DISKET_PARA_TABLE *Para;
+ UINT8 EndOfTrack;
+ UINT8 Head;
+ UINT8 SectorsInTrack;
+
+ //
+ // Get the base of disk parameter information corresponding to its type.
+ //
+ Para = (DISKET_PARA_TABLE *) ((UINT8 *) DiskPara + sizeof (DISKET_PARA_TABLE) * Info->Type);
+
+ EndOfTrack = Para->EndOfTrack;
+ Head = (UINT8) ((UINTN) Lba / EndOfTrack % 2);
+
+ SectorsInTrack = (UINT8) (EndOfTrack * (2 - Head) - (UINT8) ((UINTN) Lba % EndOfTrack));
+ if (SectorsInTrack < NumberOfBlocks) {
+ //
+ // Not all the block range locates in the same sector
+ //
+ return SectorsInTrack;
+ } else {
+ //
+ // All the block range is in the same sector.
+ //
+ return NumberOfBlocks;
+ }
+}
+
+/**
+ Read data sector from FDC device.
+
+ @param FdcBlkIoDev Instance of FDC_BLK_IO_DEV.
+ @param Info Information of floppy device.
+ @param Buffer Buffer to setup for DMA.
+ @param Lba The start address to read.
+ @param NumberOfBlocks Number of blocks to read.
+
+ @retval EFI_SUCCESS Data successfully read out.
+ @retval EFI_DEVICE_ERROR FDC device has errors.
+ @retval EFI_TIMEOUT Command does not take effect in time.
+
+**/
+EFI_STATUS
+ReadDataSector (
+ IN FDC_BLK_IO_DEV *FdcBlkIoDev,
+ IN OUT PEI_FLOPPY_DEVICE_INFO *Info,
+ IN VOID *Buffer,
+ IN EFI_PEI_LBA Lba,
+ IN UINTN NumberOfBlocks
+ )
+{
+ EFI_STATUS Status;
+ FDC_COMMAND_PACKET1 Command;
+ FDC_RESULT_PACKET Result;
+ UINTN Index;
+ UINTN Times;
+ UINT8 *Pointer;
+
+ Status = Seek (FdcBlkIoDev, Info, Lba);
+ if (Status != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ //
+ // Set up DMA
+ //
+ SetDMA (FdcBlkIoDev, Buffer, Info->MediaInfo.BlockSize, NumberOfBlocks);
+
+ //
+ // Allocate Read command packet
+ //
+ ZeroMem (&Command, sizeof (FDC_COMMAND_PACKET1));
+ Command.CommandCode = READ_DATA_CMD | CMD_MT | CMD_MFM | CMD_SK;
+
+ //
+ // Fill parameters for command.
+ //
+ FillPara (Info, Lba, &Command);
+
+ //
+ // Write command bytes to FDC
+ //
+ Pointer = (UINT8 *) (&Command);
+ for (Index = 0; Index < sizeof (FDC_COMMAND_PACKET1); Index++) {
+ if (DataOutByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ //
+ // Wait for some time until command takes effect.
+ //
+ Times = (STALL_1_SECOND / FDC_CHECK_INTERVAL) + 1;
+ do {
+ if ((IoRead8 ((UINT16) (PcdGet16 (PcdFdcBaseAddress) + FDC_REGISTER_MSR)) & 0xc0) == 0xc0) {
+ break;
+ }
+
+ MicroSecondDelay (FDC_SHORT_DELAY);
+ } while (--Times > 0);
+
+ if (Times == 0) {
+ //
+ // Command fails to take effect in time, return EFI_TIMEOUT.
+ //
+ return EFI_TIMEOUT;
+ }
+
+ //
+ // Read result bytes from FDC
+ //
+ Pointer = (UINT8 *) (&Result);
+ for (Index = 0; Index < sizeof (FDC_RESULT_PACKET); Index++) {
+ if (DataInByte (FdcBlkIoDev, Pointer++) != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+ }
+
+ return CheckResult (&Result, Info);
+}
+
+/**
+ Gets the count of block I/O devices that one specific block driver detects.
+
+ This function is used for getting the count of block I/O devices that one
+ specific block driver detects. To the PEI ATAPI driver, it returns the number
+ of all the detected ATAPI devices it detects during the enumeration process.
+ To the PEI legacy floppy driver, it returns the number of all the legacy
+ devices it finds during its enumeration process. If no device is detected,
+ then the function will return zero.
+
+ @param[in] PeiServices General-purpose services that are available
+ to every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI
+ instance.
+ @param[out] NumberBlockDevices The number of block I/O devices discovered.
+
+ @retval EFI_SUCCESS Operation performed successfully.
+
+**/
+EFI_STATUS
+EFIAPI
+FdcGetNumberOfBlockDevices (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ OUT UINTN *NumberBlockDevices
+ )
+{
+ FDC_BLK_IO_DEV *FdcBlkIoDev;
+
+ FdcBlkIoDev = NULL;
+
+ FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);
+
+ *NumberBlockDevices = FdcBlkIoDev->DeviceCount;
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Gets a block device's media information.
+
+ This function will provide the caller with the specified block device's media
+ information. If the media changes, calling this function will update the media
+ information accordingly.
+
+ @param[in] PeiServices General-purpose services that are available to every
+ PEIM
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, the PPIs that
+ want to talk to a single device must specify the
+ device index that was assigned during the enumeration
+ process. This index is a number from one to
+ NumberBlockDevices.
+ @param[out] MediaInfo The media information of the specified block media.
+ The caller is responsible for the ownership of this
+ data structure.
+
+ @retval EFI_SUCCESS Media information about the specified block device
+ was obtained successfully.
+ @retval EFI_DEVICE_ERROR Cannot get the media information due to a hardware
+ error.
+ @retval Others Other failure occurs.
+
+**/
+EFI_STATUS
+EFIAPI
+FdcGetBlockDeviceMediaInfo (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ OUT EFI_PEI_BLOCK_IO_MEDIA *MediaInfo
+ )
+{
+ UINTN DeviceCount;
+ FDC_BLK_IO_DEV *FdcBlkIoDev;
+ BOOLEAN Healthy;
+ UINTN Index;
+
+ FdcBlkIoDev = NULL;
+
+ if (This == NULL || MediaInfo == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);
+
+ DeviceCount = FdcBlkIoDev->DeviceCount;
+
+ //
+ // DeviceIndex is a value from 1 to NumberBlockDevices.
+ //
+ if ((DeviceIndex < 1) || (DeviceIndex > DeviceCount) || (DeviceIndex > 2)) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Index = DeviceIndex - 1;
+ //
+ // Probe media and retrieve latest media information
+ //
+ Healthy = DiscoverFdcDevice (
+ FdcBlkIoDev,
+ &FdcBlkIoDev->DeviceInfo[Index],
+ MediaInfo
+ );
+
+ if (!Healthy) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ CopyMem (
+ &(FdcBlkIoDev->DeviceInfo[Index].MediaInfo),
+ MediaInfo,
+ sizeof (EFI_PEI_BLOCK_IO_MEDIA)
+ );
+
+ return EFI_SUCCESS;
+}
+
+/**
+ Reads the requested number of blocks from the specified block device.
+
+ The function reads the requested number of blocks from the device. All the
+ blocks are read, or an error is returned. If there is no media in the device,
+ the function returns EFI_NO_MEDIA.
+
+ @param[in] PeiServices General-purpose services that are available to
+ every PEIM.
+ @param[in] This Indicates the EFI_PEI_RECOVERY_BLOCK_IO_PPI instance.
+ @param[in] DeviceIndex Specifies the block device to which the function wants
+ to talk. Because the driver that implements Block I/O
+ PPIs will manage multiple block devices, the PPIs that
+ want to talk to a single device must specify the device
+ index that was assigned during the enumeration process.
+ This index is a number from one to NumberBlockDevices.
+ @param[in] StartLBA The starting logical block address (LBA) to read from
+ on the device
+ @param[in] BufferSize The size of the Buffer in bytes. This number must be
+ a multiple of the intrinsic block size of the device.
+ @param[out] Buffer A pointer to the destination buffer for the data.
+ The caller is responsible for the ownership of the
+ buffer.
+
+ @retval EFI_SUCCESS The data was read correctly from the device.
+ @retval EFI_DEVICE_ERROR The device reported an error while attempting
+ to perform the read operation.
+ @retval EFI_INVALID_PARAMETER The read request contains LBAs that are not
+ valid, or the buffer is not properly aligned.
+ @retval EFI_NO_MEDIA There is no media in the device.
+ @retval EFI_BAD_BUFFER_SIZE The BufferSize parameter is not a multiple of
+ the intrinsic block size of the device.
+
+**/
+EFI_STATUS
+EFIAPI
+FdcReadBlocks (
+ IN EFI_PEI_SERVICES **PeiServices,
+ IN EFI_PEI_RECOVERY_BLOCK_IO_PPI *This,
+ IN UINTN DeviceIndex,
+ IN EFI_PEI_LBA StartLBA,
+ IN UINTN BufferSize,
+ OUT VOID *Buffer
+ )
+{
+ EFI_PEI_BLOCK_IO_MEDIA MediaInfo;
+ EFI_STATUS Status;
+ UINTN Count;
+ UINTN NumberOfBlocks;
+ UINTN BlockSize;
+ FDC_BLK_IO_DEV *FdcBlkIoDev;
+ VOID *MemPage;
+
+ FdcBlkIoDev = NULL;
+
+ if (This == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ FdcBlkIoDev = PEI_RECOVERY_FDC_FROM_BLKIO_THIS (This);
+
+ if (Buffer == NULL) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ Status = FdcGetBlockDeviceMediaInfo (PeiServices, This, DeviceIndex, &MediaInfo);
+ if (Status != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ if (!MediaInfo.MediaPresent) {
+ return EFI_NO_MEDIA;
+ }
+
+ BlockSize = MediaInfo.BlockSize;
+
+ //
+ // If BufferSize cannot be divided by block size of FDC device,
+ // return EFI_BAD_BUFFER_SIZE.
+ //
+ if (BufferSize % BlockSize != 0) {
+ return EFI_BAD_BUFFER_SIZE;
+ }
+
+ NumberOfBlocks = BufferSize / BlockSize;
+
+ if ((StartLBA + NumberOfBlocks - 1) > FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].MediaInfo.LastBlock) {
+ return EFI_INVALID_PARAMETER;
+ }
+
+ MemPage = AllocatePages (EFI_SIZE_TO_PAGES (BufferSize));
+ if ((MemPage == NULL) || ((UINTN) MemPage >= ISA_MAX_MEMORY_ADDRESS)) {
+ //
+ // If fail to allocate memory under ISA_MAX_MEMORY_ADDRESS, designate the address space for DMA
+ //
+ MemPage = (VOID *) ((UINTN) (UINT32) 0x0f00000);
+ }
+ Status = MotorOn (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));
+ if (Status != EFI_SUCCESS) {
+ return EFI_DEVICE_ERROR;
+ }
+
+ Status = Setup (FdcBlkIoDev, FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].DevPos);
+ if (Status != EFI_SUCCESS) {
+ MotorOff (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));
+ return EFI_DEVICE_ERROR;
+ }
+ //
+ // Read data in batches.
+ // Blocks in the same cylinder are read out in a batch.
+ //
+ while ((Count = GetTransferBlockCount (
+ &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]),
+ StartLBA,
+ NumberOfBlocks
+ )) != 0 && Status == EFI_SUCCESS) {
+ Status = ReadDataSector (
+ FdcBlkIoDev,
+ &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]),
+ MemPage,
+ StartLBA,
+ Count
+ );
+ CopyMem (Buffer, MemPage, BlockSize * Count);
+ StartLBA += Count;
+ NumberOfBlocks -= Count;
+ Buffer = (VOID *) ((UINTN) Buffer + Count * BlockSize);
+ }
+
+ MotorOff (FdcBlkIoDev, &(FdcBlkIoDev->DeviceInfo[DeviceIndex - 1]));
+
+ switch (Status) {
+ case EFI_SUCCESS:
+ return EFI_SUCCESS;
+
+ default:
+ FdcReset (FdcBlkIoDev, FdcBlkIoDev->DeviceInfo[DeviceIndex - 1].DevPos);
+ return EFI_DEVICE_ERROR;
+ }
+}
+
+/**
+ Initializes the floppy disk controller and installs FDC Block I/O PPI.
+
+ @param FileHandle Handle of the file being invoked.
+ @param PeiServices Describes the list of possible PEI Services.
+
+ @retval EFI_SUCCESS Successfully initialized FDC and installed PPI.
+ @retval EFI_NOT_FOUND Cannot find FDC device.
+ @retval EFI_OUT_OF_RESOURCES Have no enough memory to create instance or descriptors.
+ @retval Other Fail to install FDC Block I/O PPI.
+
+**/
+EFI_STATUS
+EFIAPI
+FdcPeimEntry (
+ IN EFI_PEI_FILE_HANDLE FileHandle,
+ IN CONST EFI_PEI_SERVICES **PeiServices
+ )
+{
+ EFI_STATUS Status;
+ FDC_BLK_IO_DEV *FdcBlkIoDev;
+ UINTN DeviceCount;
+ UINT32 Index;
+
+ Status = PeiServicesRegisterForShadow (FileHandle);
+ if (!EFI_ERROR (Status)) {
+ return Status;
+ }
+
+ //
+ // Allocate memory for instance of FDC_BLK_IO_DEV and copy initial value
+ // from template to it.
+ //
+ FdcBlkIoDev = AllocatePages (EFI_SIZE_TO_PAGES(sizeof (FDC_BLK_IO_DEV)));
+ if (FdcBlkIoDev == NULL) {
+ return EFI_OUT_OF_RESOURCES;
+ }
+ CopyMem (FdcBlkIoDev, &mBlockIoDevTemplate, sizeof (mBlockIoDevTemplate));
+
+ //
+ // Initialize DMA controller to enable all channels.
+ //
+ for (Index = 0; Index < sizeof (mRegisterTable) / sizeof (PEI_DMA_TABLE); Index++) {
+ IoWrite8 (mRegisterTable[Index].Register, mRegisterTable[Index].Value);
+ }
+ REPORT_STATUS_CODE (EFI_PROGRESS_CODE, EFI_PERIPHERAL_REMOVABLE_MEDIA + EFI_P_PC_INIT);
+
+ //
+ // Enumerate FDC devices.
+ //
+ DeviceCount = FdcEnumeration (FdcBlkIoDev);
+ if (DeviceCount == 0) {
+ return EFI_NOT_FOUND;
+ }
+
+ FdcBlkIoDev->PpiDescriptor.Ppi = &FdcBlkIoDev->FdcBlkIo;
+
+ return PeiServicesInstallPpi (&FdcBlkIoDev->PpiDescriptor);
+}
|