| /** @file | |
| Floppy Peim to support Recovery function from Floppy device. | |
| Copyright (c) 2006 - 2013, 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); | |
| if (Status == EFI_NO_MEDIA) { | |
| // | |
| // No diskette in floppy. | |
| // | |
| MediaInfo->MediaPresent = FALSE; | |
| } else if (Status != EFI_MEDIA_CHANGED && Status != EFI_SUCCESS) { | |
| // | |
| // 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); | |
| } |