VirtualBox

Changeset 99152 in vbox for trunk/src/VBox


Ignore:
Timestamp:
Mar 23, 2023 6:15:43 PM (22 months ago)
Author:
vboxsync
Message:

Devices: Updates to the ARM PL011 UART emulation, bugref:10403

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Devices/Serial/DevPL011.cpp

    r99126 r99152  
    5454/** PL011 MMIO region size in bytes. */
    5555#define PL011_MMIO_SIZE                         _4K
     56/** Maximum size of a FIFO. */
     57#define PL011_FIFO_LENGTH_MAX                   32
    5658
    5759/** The offset of the UARTDR register from the beginning of the region. */
     
    136138/** The offset of the UARTCR register from the beginning of the region. */
    137139#define PL011_REG_UARTCR_INDEX                  0x30
     140/** UART enable. */
     141# define PL011_REG_UARTCR_UARTEN                RT_BIT(0)
     142/** SIR enable. */
     143# define PL011_REG_UARTCR_SIREN                 RT_BIT(1)
     144/** SIR low-power IrDA mode. */
     145# define PL011_REG_UARTCR_SIRLP                 RT_BIT(2)
     146/** Loopback enable. */
     147# define PL011_REG_UARTCR_LBE                   RT_BIT(7)
    138148/** UART transmit enable flag. */
    139149# define PL011_REG_UARTCR_TXE                   RT_BIT(8)
    140150/** UART receive enable flag. */
    141151# define PL011_REG_UARTCR_RXE                   RT_BIT(9)
     152/** Data transmit ready. */
     153# define PL011_REG_UARTCR_DTR                   RT_BIT(10)
     154/** Request to send. */
     155# define PL011_REG_UARTCR_RTS                   RT_BIT(11)
     156/** UART Out1 modem status output (DCD). */
     157# define PL011_REG_UARTCR_OUT1_DCD              RT_BIT(12)
     158/** UART Out2 modem status output (RI). */
     159# define PL011_REG_UARTCR_OUT2_RI               RT_BIT(13)
     160/** RTS hardware flow control enable. */
     161# define PL011_REG_UARTCR_OUT1_RTSEn            RT_BIT(14)
     162/** CTS hardware flow control enable. */
     163# define PL011_REG_UARTCR_OUT1_CTSEn            RT_BIT(15)
    142164
    143165/** The offset of the UARTIFLS register from the beginning of the region. */
    144166#define PL011_REG_UARTIFLS_INDEX                0x34
     167/** Returns the Transmit Interrupt FIFO level. */
     168# define PL011_REG_UARTIFLS_TXFIFO_GET(a_Ifls)  ((a_Ifls) & 0x7)
     169/** Returns the Receive Interrupt FIFO level. */
     170# define PL011_REG_UARTIFLS_RXFIFO_GET(a_Ifls)  (((a_Ifls) >> 3) & 0x7)
     171/** 1/8 Fifo level. */
     172# define PL011_REG_UARTIFLS_LVL_1_8             0x0
     173/** 1/4 Fifo level. */
     174# define PL011_REG_UARTIFLS_LVL_1_4             0x1
     175/** 1/2 Fifo level. */
     176# define PL011_REG_UARTIFLS_LVL_1_2             0x2
     177/** 3/4 Fifo level. */
     178# define PL011_REG_UARTIFLS_LVL_3_4             0x3
     179/** 7/8 Fifo level. */
     180# define PL011_REG_UARTIFLS_LVL_7_8             0x4
    145181
    146182/** The offset of the UARTIMSC register from the beginning of the region. */
     
    176212#define PL011_REG_UART_PCELL_ID3_INDEX          0xffc
    177213
     214/** Set the specified bits in the given register. */
     215#define PL011_REG_SET(a_Reg, a_Set)             ((a_Reg) |= (a_Set))
     216/** Clear the specified bits in the given register. */
     217#define PL011_REG_CLR(a_Reg, a_Clr)             ((a_Reg) &= ~(a_Clr))
     218
    178219
    179220/*********************************************************************************************************************************
    180221*   Structures and Typedefs                                                                                                      *
    181222*********************************************************************************************************************************/
     223
     224/**
     225 * UART FIFO.
     226 */
     227typedef struct PL011FIFO
     228{
     229    /** Fifo size configured. */
     230    uint8_t                         cbMax;
     231    /** Current amount of bytes used. */
     232    uint8_t                         cbUsed;
     233    /** Next index to write to. */
     234    uint8_t                         offWrite;
     235    /** Next index to read from. */
     236    uint8_t                         offRead;
     237    /** The interrupt trigger level (only used for the receive FIFO). */
     238    uint8_t                         cbItl;
     239    /** The data in the FIFO. */
     240    uint8_t                         abBuf[PL011_FIFO_LENGTH_MAX];
     241    /** Alignment to a 4 byte boundary. */
     242    uint8_t                         au8Alignment0[3];
     243} PL011FIFO;
     244/** Pointer to a FIFO. */
     245typedef PL011FIFO *PPL011FIFO;
     246
    182247
    183248/**
     
    195260    /** @name Registers.
    196261     * @{ */
     262    uint8_t                         uRegDr;
    197263    /** UART control register. */
    198     uint32_t                        uRegCr;
     264    uint16_t                        uRegCr;
    199265    /** UART flag register. */
    200     uint32_t                        uRegFr;
     266    uint16_t                        uRegFr;
     267    /** UART integer baud rate register. */
     268    uint16_t                        uRegIbrd;
     269    /** UART fractional baud rate register. */
     270    uint16_t                        uRegFbrd;
     271    /** UART line control register. */
     272    uint16_t                        uRegLcrH;
    201273    /** @} */
     274
     275    /** Time it takes to transmit/receive a single symbol in timer ticks. */
     276    uint64_t                        cSymbolXferTicks;
     277    /** Number of bytes available for reading from the layer below. */
     278    volatile uint32_t               cbAvailRdr;
     279
     280    /** The transmit FIFO. */
     281    PL011FIFO                       FifoXmit;
     282    /** The receive FIFO. */
     283    PL011FIFO                       FifoRecv;
    202284} DEVPL011;
    203285/** Pointer to the shared serial device state. */
     
    255337
    256338
     339/*********************************************************************************************************************************
     340*   Global Variables                                                                                                             *
     341*********************************************************************************************************************************/
     342
     343#ifdef IN_RING3
     344/**
     345 * String versions of the parity enum.
     346 */
     347static const char *s_aszParity[] =
     348{
     349    "INVALID",
     350    "NONE",
     351    "EVEN",
     352    "ODD",
     353    "MARK",
     354    "SPACE",
     355    "INVALID"
     356};
     357
     358
     359/**
     360 * String versions of the stop bits enum.
     361 */
     362static const char *s_aszStopBits[] =
     363{
     364    "INVALID",
     365    "1",
     366    "INVALID",
     367    "2",
     368    "INVALID"
     369};
     370#endif
     371
     372
     373/*********************************************************************************************************************************
     374*   Internal Functions                                                                                                           *
     375*********************************************************************************************************************************/
     376
    257377#ifndef VBOX_DEVICE_STRUCT_TESTCASE
    258378
     379/**
     380 * Updates the IRQ state based on the current device state.
     381 *
     382 * @returns nothing.
     383 * @param   pDevIns             The device instance.
     384 * @param   pThis               The shared serial port instance data.
     385 * @param   pThisCC             The serial port instance data for the current context.
     386 */
     387static void pl011IrqUpdate(PPDMDEVINS pDevIns, PDEVPL011 pThis, PDEVPL011CC pThisCC)
     388{
     389    LogFlowFunc(("pThis=%#p\n", pThis));
     390    RT_NOREF(pDevIns, pThis, pThisCC);
     391}
     392
     393
     394/**
     395 * Transmits the given byte.
     396 *
     397 * @returns Strict VBox status code.
     398 * @param   pDevIns             The device instance.
     399 * @param   pThis               The shared serial port instance data.
     400 * @param   pThisCC             The serial port instance data for the current context.
     401 * @param   bVal                Byte to transmit.
     402 */
     403static VBOXSTRICTRC pl011Xmit(PPDMDEVINS pDevIns, PDEVPL011CC pThisCC, PDEVPL011 pThis, uint8_t bVal)
     404{
     405    int rc = VINF_SUCCESS;
     406#ifdef IN_RING3
     407    bool fNotifyDrv = false;
     408#endif
     409
     410    if (pThis->uRegLcrH & PL011_REG_UARTLCR_H_FEN)
     411    {
     412        AssertReleaseFailed(); /** @todo */
     413    }
     414    else
     415    {
     416        /* Notify the lower driver about available data only if the register was empty before. */
     417        if (!(pThis->uRegFr & PL011_REG_UARTFR_BUSY))
     418        {
     419#ifndef IN_RING3
     420            rc = VINF_IOM_R3_IOPORT_WRITE;
     421#else
     422            pThis->uRegDr = bVal;
     423            pThis->uRegFr |= PL011_REG_UARTFR_BUSY;
     424            pl011IrqUpdate(pDevIns, pThis, pThisCC);
     425            fNotifyDrv = true;
     426#endif
     427        }
     428        else
     429            pThis->uRegDr = bVal;
     430    }
     431
     432#ifdef IN_RING3
     433    if (fNotifyDrv)
     434    {
     435        /* Leave the device critical section before calling into the lower driver. */
     436        PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
     437
     438        if (   pThisCC->pDrvSerial
     439            && !(pThis->uRegCr & PL011_REG_UARTCR_LBE))
     440        {
     441            int rc2 = pThisCC->pDrvSerial->pfnDataAvailWrNotify(pThisCC->pDrvSerial);
     442            if (RT_FAILURE(rc2))
     443                LogRelMax(10, ("PL011#%d: Failed to send data with %Rrc\n", pDevIns->iInstance, rc2));
     444        }
     445        else
     446        {
     447            AssertReleaseFailed(); /** @todo */
     448            //PDMDevHlpTimerSetRelative(pDevIns, pThis->hTimerTxUnconnected, pThis->cSymbolXferTicks, NULL);
     449        }
     450
     451        rc = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VINF_SUCCESS);
     452    }
     453#endif
     454
     455    return rc;
     456}
     457
     458
     459#ifdef IN_RING3
     460/**
     461 * Updates the serial port parameters of the attached driver with the current configuration.
     462 *
     463 * @returns nothing.
     464 * @param   pDevIns             The device instance.
     465 * @param   pThis               The shared serial port instance data.
     466 * @param   pThisCC             The serial port instance data for the current context.
     467 */
     468static void pl011R3ParamsUpdate(PPDMDEVINS pDevIns, PDEVPL011 pThis, PDEVPL011CC pThisCC)
     469{
     470    if (   pThis->uRegIbrd != 0
     471        && pThisCC->pDrvSerial)
     472    {
     473        uint32_t uBps = 460800 / pThis->uRegIbrd; /** @todo This is for a 7.3728MHz clock. */
     474        unsigned cDataBits = PL011_REG_UARTLCR_H_WLEN_GET(pThis->uRegLcrH) + 5;
     475        uint32_t cFrameBits = cDataBits;
     476        PDMSERIALSTOPBITS enmStopBits = PDMSERIALSTOPBITS_ONE;
     477        PDMSERIALPARITY enmParity = PDMSERIALPARITY_NONE;
     478
     479        if (pThis->uRegLcrH & PL011_REG_UARTLCR_H_STP2)
     480        {
     481            enmStopBits = PDMSERIALSTOPBITS_TWO;
     482            cFrameBits += 2;
     483        }
     484        else
     485            cFrameBits++;
     486
     487        if (pThis->uRegLcrH & PL011_REG_UARTLCR_H_PEN)
     488        {
     489            /* Select the correct parity mode based on the even and stick parity bits. */
     490            switch (pThis->uRegLcrH & (PL011_REG_UARTLCR_H_EPS | PL011_REG_UARTLCR_H_SPS))
     491            {
     492                case 0:
     493                    enmParity = PDMSERIALPARITY_ODD;
     494                    break;
     495                case PL011_REG_UARTLCR_H_EPS:
     496                    enmParity = PDMSERIALPARITY_EVEN;
     497                    break;
     498                case PL011_REG_UARTLCR_H_EPS | PL011_REG_UARTLCR_H_SPS:
     499                    enmParity = PDMSERIALPARITY_SPACE;
     500                    break;
     501                case PL011_REG_UARTLCR_H_SPS:
     502                    enmParity = PDMSERIALPARITY_MARK;
     503                    break;
     504                default:
     505                    /* We should never get here as all cases where caught earlier. */
     506                    AssertMsgFailed(("This shouldn't happen at all: %#x\n",
     507                                     pThis->uRegLcrH & (PL011_REG_UARTLCR_H_EPS | PL011_REG_UARTLCR_H_SPS)));
     508            }
     509
     510            cFrameBits++;
     511        }
     512
     513        //uint64_t uTimerFreq = PDMDevHlpTimerGetFreq(pDevIns, pThis->hTimerRcvFifoTimeout);
     514        //pThis->cSymbolXferTicks = (uTimerFreq / uBps) * cFrameBits;
     515
     516        LogFlowFunc(("Changing parameters to: %u,%s,%u,%s\n",
     517                     uBps, s_aszParity[enmParity], cDataBits, s_aszStopBits[enmStopBits]));
     518
     519        int rc = pThisCC->pDrvSerial->pfnChgParams(pThisCC->pDrvSerial, uBps, enmParity, cDataBits, enmStopBits);
     520        if (RT_FAILURE(rc))
     521            LogRelMax(10, ("Serial#%d: Failed to change parameters to %u,%s,%u,%s -> %Rrc\n",
     522                           pDevIns->iInstance, uBps, s_aszParity[enmParity], cDataBits, s_aszStopBits[enmStopBits], rc));
     523
     524        /* Changed parameters will flush all receive queues, so there won't be any data to read even if indicated. */
     525        pThisCC->pDrvSerial->pfnQueuesFlush(pThisCC->pDrvSerial, true /*fQueueRecv*/, false /*fQueueXmit*/);
     526        ASMAtomicWriteU32(&pThis->cbAvailRdr, 0);
     527        PL011_REG_CLR(pThis->uRegFr, PL011_REG_UARTFR_BUSY);
     528    }
     529}
     530
     531
     532/**
     533 * Reset the transmit/receive related bits to the standard values
     534 * (after a detach/attach/reset event).
     535 *
     536 * @returns nothing.
     537 * @param   pDevIns             The device instance.
     538 * @param   pThis               The shared serial port instance data.
     539 * @param   pThisCC             The serial port instance data for the current context.
     540 */
     541static void pl011R3XferReset(PPDMDEVINS pDevIns, PDEVPL011 pThis, PDEVPL011CC pThisCC)
     542{
     543    //PDMDevHlpTimerStop(pDevIns, pThis->hTimerRcvFifoTimeout);
     544    //PDMDevHlpTimerStop(pDevIns, pThis->hTimerTxUnconnected);
     545    //pThis->uRegLsr = UART_REG_LSR_THRE | UART_REG_LSR_TEMT;
     546
     547    //uartFifoClear(&pThis->FifoXmit);
     548    //uartFifoClear(&pThis->FifoRecv);
     549    pl011R3ParamsUpdate(pDevIns, pThis, pThisCC);
     550    pl011IrqUpdate(pDevIns, pThis, pThisCC);
     551
     552    if (pThisCC->pDrvSerial)
     553    {
     554        /* Set the modem lines to reflect the current state. */
     555        int rc = pThisCC->pDrvSerial->pfnChgModemLines(pThisCC->pDrvSerial, false /*fRts*/, false /*fDtr*/);
     556        if (RT_FAILURE(rc))
     557            LogRel(("PL011#%d: Failed to set modem lines with %Rrc during reset\n",
     558                    pDevIns->iInstance, rc));
     559
     560        uint32_t fStsLines = 0;
     561        rc = pThisCC->pDrvSerial->pfnQueryStsLines(pThisCC->pDrvSerial, &fStsLines);
     562        if (RT_SUCCESS(rc))
     563        {} //uartR3StsLinesUpdate(pDevIns, pThis, pThisCC, fStsLines);
     564        else
     565            LogRel(("PL011#%d: Failed to query status line status with %Rrc during reset\n",
     566                    pDevIns->iInstance, rc));
     567    }
     568
     569}
     570
     571
     572/**
     573 * Tries to copy the specified amount of data from the active TX queue (register or FIFO).
     574 *
     575 * @returns nothing.
     576 * @param   pDevIns             The device instance.
     577 * @param   pThis               The shared serial port instance data.
     578 * @param   pThisCC             The serial port instance data for the current context.
     579 * @param   pvBuf               Where to store the data.
     580 * @param   cbRead              How much to read from the TX queue.
     581 * @param   pcbRead             Where to store the amount of data read.
     582 */
     583static void pl011R3TxQueueCopyFrom(PPDMDEVINS pDevIns, PDEVPL011 pThis, PDEVPL011CC pThisCC,
     584                                   void *pvBuf, size_t cbRead, size_t *pcbRead)
     585{
     586    if (pThis->uRegLcrH & PL011_REG_UARTLCR_H_FEN)
     587    {
     588        RT_NOREF(cbRead);
     589        AssertReleaseFailed();
     590    }
     591    else if (pThis->uRegFr & PL011_REG_UARTFR_BUSY)
     592    {
     593        *(uint8_t *)pvBuf = pThis->uRegDr;
     594        *pcbRead = 1;
     595        PL011_REG_CLR(pThis->uRegFr, PL011_REG_UARTFR_BUSY);
     596        pl011IrqUpdate(pDevIns, pThis, pThisCC);
     597    }
     598    else
     599    {
     600        /*
     601         * This can happen if there was data in the FIFO when the connection was closed,
     602         * indicate this condition to the lower driver by returning 0 bytes.
     603         */
     604        *pcbRead = 0;
     605    }
     606}
     607#endif
    259608
    260609
     
    324673static DECLCALLBACK(VBOXSTRICTRC) pl011MmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
    325674{
    326     PDEVPL011 pThis = PDMDEVINS_2_DATA(pDevIns, PDEVPL011);
     675    PDEVPL011   pThis   = PDMDEVINS_2_DATA(pDevIns, PDEVPL011);
     676    PDEVPL011CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVPL011CC);
    327677    LogFlowFunc(("cb=%u reg=%RGp val=%llx\n", cb, off, cb == 4 ? *(uint32_t *)pv : cb == 8 ? *(uint64_t *)pv : 0xdeadbeef));
    328678    RT_NOREF(pvUser);
     
    330680    Assert(!(off & (cb - 1)));
    331681
    332     int rc = VINF_SUCCESS;
    333     return rc;
     682    VBOXSTRICTRC rcStrict = VINF_SUCCESS;
     683    uint32_t u32Val = *(uint32_t *)pv;
     684    switch (off)
     685    {
     686        case PL011_REG_UARTDR_INDEX:
     687            if (   (pThis->uRegCr & PL011_REG_UARTCR_UARTEN)
     688                && (pThis->uRegCr & PL011_REG_UARTCR_TXE))
     689                rcStrict = pl011Xmit(pDevIns, pThisCC, pThis, (uint8_t)u32Val);
     690            break;
     691        default:
     692            break;
     693
     694    }
     695    return rcStrict;
    334696}
    335697
     
    400762    PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
    401763
    402     /** @todo */
    403     RT_NOREF(pThis);
     764    pl011R3TxQueueCopyFrom(pDevIns, pThis, pThisCC, pvBuf, cbRead, pcbRead);
    404765
    405766    PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
     
    563924    PDEVPL011CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVPL011CC);
    564925
    565     pThis->uRegCr = PL011_REG_UARTCR_TXE | PL011_REG_UARTCR_RXE;
    566     pThis->uRegFr = PL011_REG_UARTFR_TXFE | PL011_REG_UARTFR_RXFE;
     926    pThis->uRegDr   = 0;
     927    /* UARTEN is normally 0 on reset but UEFI doesn't set it causing the serial port to not log anything. */
     928    pThis->uRegCr   = PL011_REG_UARTCR_TXE | PL011_REG_UARTCR_RXE | PL011_REG_UARTCR_UARTEN;
     929    pThis->uRegFr   = PL011_REG_UARTFR_TXFE | PL011_REG_UARTFR_RXFE;
     930    pThis->uRegIbrd = 0;
     931    pThis->uRegFbrd = 0;
     932    pThis->uRegLcrH = 0;
    567933    /** @todo */
    568     RT_NOREF(pThisCC);
     934
     935    pl011R3XferReset(pDevIns, pThis, pThisCC);
    569936}
    570937
     
    575942static DECLCALLBACK(int) pl011R3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
    576943{
     944    PDEVPL011   pThis   = PDMDEVINS_2_DATA(pDevIns, PDEVPL011);
    577945    PDEVPL011CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVPL011CC);
    578946    RT_NOREF(fFlags);
     
    588956            return VERR_PDM_MISSING_INTERFACE;
    589957        }
     958        rc = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
     959        if (RT_SUCCESS(rc))
     960        {
     961            pl011R3XferReset(pDevIns, pThis, pThisCC);
     962            PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
     963        }
    590964    }
    591965    else if (rc == VERR_PDM_NO_ATTACHED_DRIVER)
     
    593967        pThisCC->pDrvBase = NULL;
    594968        pThisCC->pDrvSerial = NULL;
     969        rc = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
     970        if (RT_SUCCESS(rc))
     971        {
     972            pl011R3XferReset(pDevIns, pThis, pThisCC);
     973            PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
     974        }
    595975        LogRel(("PL011#%d: no unit\n", pDevIns->iInstance));
    596976    }
     
    607987static DECLCALLBACK(void) pl011R3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
    608988{
     989    PDEVPL011   pThis   = PDMDEVINS_2_DATA(pDevIns, PDEVPL011);
    609990    PDEVPL011CC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDEVPL011CC);
    610991    RT_NOREF(fFlags);
     
    614995    pThisCC->pDrvBase   = NULL;
    615996    pThisCC->pDrvSerial = NULL;
     997
     998    int const rcLock = PDMDevHlpCritSectEnter(pDevIns, pDevIns->pCritSectRoR3, VERR_IGNORED);
     999    PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, pDevIns->pCritSectRoR3, rcLock);
     1000
     1001    pl011R3XferReset(pDevIns, pThis, pThisCC);
     1002
     1003    PDMDevHlpCritSectLeave(pDevIns, pDevIns->pCritSectRoR3);
    6161004}
    6171005
Note: See TracChangeset for help on using the changeset viewer.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette