CH567 USB1 上面的 CH372 例子

这个例子是 Ch567 支持包里面带的demo, 将自己模拟为 Ch372 设备。

关键代码在 ch56x_usb1dev372.c 中如下:

/*
 ============================================================================
 Name        : ch56x_usb1dev372.c
 Author      : CC
 Version     :
 Copyright   : Your copyright notice
 Description : USB1 Device mode, simulate a device of CH372
 ============================================================================
 */

#include <stdio.h>
#include <nds32_isr.h>
#include <nds32_intrinsic.h>

#include "CH568SFR.H"
#include "MYdebug.H"
#include "ch56x_usb1dev372.h"


//------ 设备模式 USB1
// 设备描述符
const UINT8    MyDevDescr[] = { 0x12, 0x01, 0x00, 0x02, 0xFF, 0x80, 0x37, THIS_ENDP0_SIZE,
                                0x48, 0x43, 0x37, 0x55,              /* 厂商ID和产品ID */
                                0x00, 0x01, 0x01, 0x02, 0x00, 0x01
                              };
// 配置描述符
const UINT8    MyCfgDescr[] = { 0x09, 0x02, 0x4a, 0x00, 0x01, 0x01, 0x00, 0x80, 0x32,    //配置描述符
                                0x09, 0x04, 0x00, 0x00, 0x08, 0xFF, 0x80, 0x37, 0x00,    //接口描述符
                                0x07, 0x05, 0x84, 0x02, 0x00, 0x02, 0x00,                //端点描述符,
                                0x07, 0x05, 0x04, 0x02, 0x00, 0x02, 0x00,
                                0x07, 0x05, 0x83, 0x02, 0x00, 0x02, 0x00,
                                0x07, 0x05, 0x03, 0x02, 0x00, 0x02, 0x00,
                                0x07, 0x05, 0x82, 0x02, 0x00, 0x02, 0x00,
                                0x07, 0x05, 0x02, 0x02, 0x00, 0x02, 0x00,
                                0x07, 0x05, 0x81, 0x02, 0x00, 0x02, 0x0A,
                                0x07, 0x05, 0x01, 0x02, 0x00, 0x02, 0x0A,
                              };
// 语言描述符
const UINT8    MyLangDescr[] = { 0x04, 0x03, 0x09, 0x04 };
// 厂家信息
const UINT8    MyManuInfo[] = { 0x0E, 0x03, 'w', 0, 'c', 0, 'h', 0, '.', 0, 'c', 0, 'n', 0 };
// 产品信息
const UINT8    MyProdInfo[] = { 0x0C, 0x03, 'C', 0, 'H', 0, '5', 0, '6', 0, 'x', 0 };

__attribute__ ((aligned(4)))UINT8    Ep0Buffer[64 + 512*2] ;              // EP1+EP4 OUT&IN, must 4字节对齐
__attribute__ ((aligned(4)))UINT8   Ep1Buffer[512*2] ;                    // EP1 IN+OUT, must 4字节对齐
__attribute__ ((aligned(4)))UINT8   Ep2Buffer[512*2] ;                    // EP2 IN+OUT, must 4字节对齐
__attribute__ ((aligned(4)))UINT8   Ep3Buffer[512*2] ;                    // EP3 IN+OUT, must 4字节对齐
#define UsbSetupBuf         ((PUSB_SETUP_REQ)Ep0Buffer)
#define UsbEp1OUTBuf        ((PUINT8)&Ep1Buffer[0])
#define UsbEp1INBuf          ((PUINT8)&Ep1Buffer[512])
#define UsbEp2OUTBuf        ((PUINT8)&Ep2Buffer[0])
#define UsbEp2INBuf         ((PUINT8)&Ep2Buffer[512])
#define UsbEp3OUTBuf        ((PUINT8)&Ep3Buffer[0])
#define UsbEp3INBuf          ((PUINT8)&Ep3Buffer[512])
#define UsbEp4OUTBuf        ((PUINT8)&Ep0Buffer[64])
#define UsbEp4INBuf         ((PUINT8)&Ep0Buffer[64+512])

UINT16  SetupLen;                    /* USB控制传输传输长度 */
UINT8V  SetupReqCode;                /* USB控制传输命令码 */
UINT8V  UsbInterCfg;                 /* USB设备接口配置 */
PUINT8  pDescr;                      /* 上传指针 */
UINT8   DevAddr;                     /* 设备地址 */
UINT8   UsbConfig = 0;               // USB配置标志

/*******************************************************************************
* Function Name  : USB1DeviceInit
* Description    : USB1 设备模式初始化
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void    USB1DeviceInit( void )  // 初始化USB设备
{
        /* 全局寄存器 */
        R8_USB1_CTRL   = bUC_CLR_ALL;       //清空中断标志和FIFO
        R8_USB1_CTRL   = UCST_HS | bUC_INT_BUSY |bUC_DMA_EN;  //设备模式,高速,
        R8_USB1_SUSPEND = 0;                //不挂起
        R8_USB1_DEV_AD = 0x00;              //设备地址0
        R8_USB1_INT_FG = 0xff;              //清除中断标志
        R8_USB1_INT_EN = bUIE_FIFO_OV | bUIE_SUSPEND | bUIE_TRANSFER | bUIE_BUS_RST;        //中断使能
        R8_UEP4_1_MOD1 = 0;
        R8_UEP2_3_MOD1 = 0;

        /* 设备端点设置 */
//ENDP0
        R8_UEP0_TX_CTRL1 = UEP_T_RES_NAK;
        R8_UEP0_RX_CTRL1 = UEP_R_RES_ACK;
        R16_UEP0_MAX_LEN1 = THIS_ENDP0_SIZE;             //端点0接收包的最大长度为64字节
        R16_UEP0_DMA1 = (UINT32)Ep0Buffer;

//ENDP1
        R8_UEP1_TX_CTRL1 = UEP_T_RES_NAK | bUEP_AUTO_TOG;
        R8_UEP1_RX_CTRL1 = UEP_R_RES_ACK| bUEP_AUTO_TOG;
        R8_UEP4_1_MOD1 |= bUEP1_TX_EN | bUEP1_RX_EN;
        R16_UEP1_MAX_LEN1 = 512;
        R16_UEP1_DMA1 = (UINT32)Ep1Buffer;

//ENDP2
        R8_UEP2_TX_CTRL1 = UEP_T_RES_NAK| bUEP_AUTO_TOG;
        R8_UEP2_RX_CTRL1 = UEP_R_RES_ACK| bUEP_AUTO_TOG;
        R8_UEP2_3_MOD1 |= bUEP2_RX_EN | bUEP2_TX_EN;
        R16_UEP2_MAX_LEN1 = 512;
        R16_UEP2_DMA1 = (UINT32)Ep2Buffer;

//ENDP3
        R8_UEP3_TX_CTRL1 = UEP_T_RES_NAK | bUEP_AUTO_TOG;
        R8_UEP3_RX_CTRL1 = UEP_R_RES_ACK| bUEP_AUTO_TOG;
        R8_UEP2_3_MOD1 |= bUEP3_RX_EN | bUEP3_TX_EN;
        R16_UEP3_MAX_LEN1 = 512;
        R16_UEP3_DMA1 = (UINT32)Ep3Buffer;

//ENDP4
        R8_UEP4_TX_CTRL1 = UEP_T_RES_NAK;        //不支持自动翻转
        R8_UEP4_RX_CTRL1 = UEP_R_RES_ACK;
        R8_UEP4_1_MOD1 |= bUEP4_RX_EN | bUEP4_TX_EN;
        R16_UEP4_MAX_LEN1 = 512;

        R8_USB1_CTRL |= bUC_DEV_PU_EN;        //设备连接
}

/*******************************************************************************
* Function Name  : USB1DevIntDeal
* Description    : USB1 设备中断处理
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void USB1DevIntDeal(void)
{
        UINT8   intstatus;
        UINT16  i, l;

        if ( R8_USB1_INT_FG & UIF_TRANSFER )    // USB传输完成
        {
                if ( R8_USB1_INT_ST & bUIS_IS_NAK )    //接收到主机IN包
                {
                        printf("NAK\n");
                }
                else
                {
                        intstatus = R8_USB1_INT_ST & (MASK_UIS_TOKEN | MASK_UIS_ENDP);

                        if(intstatus == (UIS_TOKEN_OUT|4))             /* endpoint 4  下传 */
                        {
                                if(R8_USB1_INT_ST&bUIS_TOG_OK)
                                {
                                        R8_UEP4_RX_CTRL1 = R8_UEP4_RX_CTRL1 ^ 0x08;    //手动翻转
                                        l = R16_USB1_RX_LEN;
                                        for(i=0; i<l; i++)
                                                UsbEp4INBuf[i] = ~UsbEp4OUTBuf[i];        //按位取反上传
                                        R16_UEP4_T_LEN1 = l;
                                        R8_UEP4_TX_CTRL1 = (R8_UEP4_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_ACK;
                                }
                        }
                        if(intstatus == (UIS_TOKEN_OUT|3))             /* endpoint 3 下传 */
                        {
                                if(R8_USB1_INT_ST&bUIS_TOG_OK)
                                {
                                        //R8_UEP3_RX_CTRL1 = R8_UEP3_RX_CTRL1 ^ 0x08;    //翻转
                                        l = R16_USB1_RX_LEN;
                                        for(i=0; i<l; i++)
                                                UsbEp3INBuf[i] = ~UsbEp3OUTBuf[i];        //按位取反上传
                                        R16_UEP3_T_LEN1 = l;
                                        R8_UEP3_TX_CTRL1 = (R8_UEP3_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_ACK;
                                }
                        }
                        if(intstatus == (UIS_TOKEN_OUT|2))             /* endpoint 2 下传 */
                        {
                                if(R8_USB1_INT_ST&bUIS_TOG_OK)
                                {
                                        //R8_UEP2_RX_CTRL1 = R8_UEP2_RX_CTRL1 ^ 0x08;    //翻转
                                        l = R16_USB1_RX_LEN;
                                        for(i=0; i<l; i++)
                                                UsbEp2INBuf[i] = ~UsbEp2OUTBuf[i];    //按位取反上传
                                        R16_UEP2_T_LEN1 = l;
                                        R8_UEP2_TX_CTRL1 = (R8_UEP2_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_ACK;
                                }
                        }
                        if(intstatus == (UIS_TOKEN_OUT|1))             /* endpoint 1 下传 */
                        {
                                if(R8_USB1_INT_ST&bUIS_TOG_OK)
                                {
                                        //R8_UEP1_RX_CTRL1 = R8_UEP1_RX_CTRL1 ^ 0x08;    //翻转
                                        l = R16_USB1_RX_LEN;
                                        for(i=0; i<l; i++)
                                                UsbEp1INBuf[i] = ~UsbEp1OUTBuf[i];        //按位取反上传
                                        R16_UEP1_T_LEN1 = l;
                                        R8_UEP1_TX_CTRL1 = (R8_UEP1_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_ACK;
                                }
                        }
                        if(intstatus == (UIS_TOKEN_IN|4))             /* endpoint 4 上传 */
                        {
                                R8_UEP4_TX_CTRL1 = R8_UEP4_TX_CTRL1 ^ 0x08;     //手动翻转
                                R8_UEP4_TX_CTRL1 = (R8_UEP4_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_NAK;
                        }
                        if(intstatus == (UIS_TOKEN_IN|3))             /* endpoint 3 上传 */
                        {
                                R8_UEP3_TX_CTRL1 = (R8_UEP3_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_NAK;
                        }
                        if(intstatus == (UIS_TOKEN_IN|2))             /* endpoint 2 上传 */
                        {
                                R8_UEP2_TX_CTRL1 = (R8_UEP2_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_NAK;
                        }
                        if(intstatus == (UIS_TOKEN_IN|1))             /* endpoint 1 上传 */
                        {
                                R8_UEP1_TX_CTRL1 = (R8_UEP1_TX_CTRL1 & ~ MASK_UEP_T_RES) | UEP_T_RES_NAK;
                        }
                        if(intstatus == (UIS_TOKEN_SETUP))             /* endpoint 0# SETUP */
                        {
                                USB1Dev_EDP0_Setup_Deal();
                        }
                        if(intstatus == (UIS_TOKEN_IN))             /* endpoint 0# IN */
                        {
                                USB1Dev_EDP0_IN_Deal();
                        }
                        if(intstatus == (UIS_TOKEN_OUT))             /* endpoint 0# OUT */
                        {
                                USB1Dev_EDP0_OUT_Deal();
                        }
                }
                R8_USB1_INT_FG = UIF_TRANSFER;
        }
        else if ( R8_USB1_INT_FG & UIF_SUSPEND )   // USB总线挂起/唤醒完成
        {
                R8_USB1_INT_FG = UIF_SUSPEND;
                printf("Suspend\n");
        }
        else if( R8_USB1_INT_FG & UIF_BUS_RST )     //USB设备复位
        {
                R8_UEP0_TX_CTRL1 = UEP_T_RES_NAK;
                R8_UEP0_RX_CTRL1 = UEP_R_RES_ACK;
                R8_UEP1_TX_CTRL1 = UEP_T_RES_NAK | bUEP_AUTO_TOG;
                R8_UEP1_RX_CTRL1 = UEP_R_RES_ACK | bUEP_AUTO_TOG;
                R8_UEP2_TX_CTRL1 = UEP_T_RES_NAK | bUEP_AUTO_TOG;
                R8_UEP2_RX_CTRL1 = UEP_R_RES_ACK | bUEP_AUTO_TOG;
                R8_UEP3_TX_CTRL1 = UEP_T_RES_NAK | bUEP_AUTO_TOG;
                R8_UEP3_RX_CTRL1 = UEP_R_RES_ACK | bUEP_AUTO_TOG;
                R8_UEP4_TX_CTRL1 = UEP_T_RES_NAK;
                R8_UEP4_RX_CTRL1 = UEP_R_RES_ACK;
                R8_USB1_DEV_AD = 0;
                DevAddr = 0;
                R8_USB1_INT_FG = UIF_BUS_RST;
                printf("Reset!\n");
        }
        else if( R8_USB1_INT_FG & UIF_FIFO_OV )     //FIFO溢出中断
        {
                R8_USB1_INT_FG = UIF_FIFO_OV;
                printf("FIFOOV!\n");
        }
}

/*******************************************************************************
* Function Name  : USB1Dev_EDP0_Setup_Deal
* Description    : USB1 设备模式端点0 setup处理
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void USB1Dev_EDP0_Setup_Deal(void)
{
        UINT16 len;
        UINT8  status, i;
        UINT8  buf[8];

        /* 获取SETUP包 */
        len = R16_USB1_RX_LEN;
        if ( len == sizeof( USB_SETUP_REQ ) )
        {
//        for(i=0; i<8; i++)    printf("%02x ", Ep0Buffer[i]);    printf("\n");

                SetupLen = (UINT16)(UsbSetupBuf->wLengthH)<<8|(UsbSetupBuf->wLengthL);        //请求数据长度
                SetupReqCode = UsbSetupBuf->bRequest;                                        //命令码

                /* 分析并处理当前的SETUP包 */
                len = 0;                                                                      // 默认为成功并且上传0长度
                status = 0;
                if (( UsbSetupBuf->bRequestType & USB_REQ_TYP_MASK ) != USB_REQ_TYP_STANDARD )  /* 非标准请求 */
                {
                        status = 0xFF;  // 操作失败
                }
                else                                                                                /* 标准请求 */
                {
                        switch( SetupReqCode )            // 请求码
                        {
                        case USB_GET_DESCRIPTOR:
                                switch( UsbSetupBuf->wValueH )
                                {
                                case 0x01:                                                                  // 设备描述符
                                        pDescr = (PUINT8)( &MyDevDescr[0] );
                                        len = sizeof( MyDevDescr );
                                        break;
                                case 0x02:                                                                  // 配置描述符
                                        pDescr = (PUINT8)( &MyCfgDescr[0] );
                                        len = sizeof( MyCfgDescr );
                                        break;
                                case 0x03:                                                                  // 字符串描述符
                                        switch( UsbSetupBuf->wValueL )
                                        {
                                        case 1:                                                                //厂商字符串描述符
                                                pDescr = (PUINT8)( &MyManuInfo[0] );
                                                len = sizeof( MyManuInfo );
                                                break;
                                        case 2:                                                                //产品字符串描述符
                                                pDescr = (PUINT8)( &MyProdInfo[0] );
                                                len = sizeof( MyProdInfo );
                                                break;
                                        case 0:                                                                //语言描述符
                                                pDescr = (PUINT8)( &MyLangDescr[0] );
                                                len = sizeof( MyLangDescr );
                                                break;
                                        default:
                                                status = 0xFF;  // 不支持的字符串描述符
                                                break;
                                        }
                                        break;
                                default:
                                        status = 0xFF;  // 不支持的描述符类型
                                        break;
                                }
                                break;
                        case USB_SET_ADDRESS:
                                DevAddr = UsbSetupBuf->wValueL;  // 暂存USB设备地址
                                break;
                        case USB_GET_CONFIGURATION:
                                buf[0] = UsbConfig;
                                pDescr = buf;
                                if ( SetupLen >= 1 ) len = 1;
                                break;
                        case USB_SET_CONFIGURATION:
                                UsbConfig = UsbSetupBuf->wValueL;
                                break;
                        case USB_CLEAR_FEATURE:
                                if ( ( UsbSetupBuf->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_ENDP )    // 清除端点
                                {
                                        switch( UsbSetupBuf->wIndexL )          //复位同步触发位,清除STALL
                                        {
                                        case 0x82:
                                                R8_UEP2_TX_CTRL1 = R8_UEP2_TX_CTRL1 & ~ ( MASK_UEP_T_RES ) | UEP_T_RES_NAK;
                                                break;
                                        case 0x02:
                                                R8_UEP2_RX_CTRL1 = R8_UEP2_RX_CTRL1 & ~ ( MASK_UEP_R_RES ) | UEP_R_RES_ACK;
                                                break;
                                        case 0x81:
                                                R8_UEP1_TX_CTRL1 = R8_UEP1_TX_CTRL1 & ~ ( MASK_UEP_T_RES ) | UEP_T_RES_NAK;
                                                break;
                                        case 0x01:
                                                R8_UEP1_RX_CTRL1 = R8_UEP1_RX_CTRL1 & ~ ( MASK_UEP_R_RES ) | UEP_R_RES_ACK;
                                                break;
                                        default:
                                                status = 0xFF;  // 不支持的端点
                                                break;
                                        }
                                }
                                else if (( UsbSetupBuf->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_DEVICE)         //清除设备
                                {
                                        /* 设备休眠 */
                                }
                                else status = 0xFF;  // 不是端点不支持
                                break;
                        case USB_SET_FEATURE:
                                if ( ( UsbSetupBuf->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_ENDP )    // 设置端点
                                {
                                        switch((UINT16)(UsbSetupBuf->wIndexH)<<8|(UsbSetupBuf->wIndexL))          //复位同步触发位,清除STALL
                                        {
                                        case 0x82:
                                                R8_UEP2_TX_CTRL1 |= UEP_T_RES_STALL;
                                                break;
                                        case 0x02:
                                                R8_UEP2_RX_CTRL1 |= UEP_T_RES_STALL;
                                                break;
                                        case 0x81:
                                                R8_UEP1_TX_CTRL1 |= UEP_T_RES_STALL;
                                                break;
                                        case 0x01:
                                                R8_UEP1_RX_CTRL1 |= UEP_T_RES_STALL;
                                                break;
                                        default:
                                                status = 0xFF;  // 不支持的端点
                                                break;
                                        }
                                }
                                else if (( UsbSetupBuf->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_DEVICE)         //设置设备
                                {
                                        /* 设备唤醒 */
                                }
                                else status = 0xFF;  // 不是端点不支持
                                break;
                        case USB_SET_INTERFACE:
                                UsbInterCfg = UsbSetupBuf->wIndexL;
                                break;
                        case USB_GET_INTERFACE:
                                buf[0] = UsbInterCfg;
                                pDescr = buf;
                                if ( SetupLen >= 1 ) len = 1;
                                break;
                        case USB_GET_STATUS:
                                if (( UsbSetupBuf->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_ENDP )    // 获取端点状态
                                {
                                        switch((UINT16)(UsbSetupBuf->wIndexH)<<8|(UsbSetupBuf->wIndexL))
                                        {
                                        case 0x82:
                                                buf[0] = ((R8_UEP2_TX_CTRL1&0x11) == UEP_T_RES_STALL) ? 0x01 : 0x00 ;
                                                break;
                                        case 0x02:
                                                buf[0] = ((R8_UEP2_RX_CTRL1&0x11) == UEP_T_RES_STALL) ? 0x01 : 0x00 ;
                                                break;
                                        case 0x81:
                                                buf[0] = ((R8_UEP1_TX_CTRL1&0x11) == UEP_T_RES_STALL) ? 0x01 : 0x00 ;
                                                break;
                                        case 0x01:
                                                buf[0] = ((R8_UEP1_RX_CTRL1&0x11) == UEP_T_RES_STALL) ? 0x01 : 0x00 ;
                                                break;
                                        default:
                                                status = 0xFF;  // 不支持的端点
                                                break;
                                        }
                                        buf[1] = 0x00;
                                        pDescr = buf;
                                        len = 2;
                                }
                                else if (( UsbSetupBuf->bRequestType & USB_REQ_RECIP_MASK ) == USB_REQ_RECIP_DEVICE )      // 获取设备状态
                                {
                                        buf[0] = 0x00;        //填充当前设备状态
                                        buf[1] = 0x00;
                                        pDescr = buf;
                                        len = 2;
                                }
                                break;
                        default:
                                status = 0xFF;  // 操作失败
                                break;
                        }
                }
        }
        else
        {
                status = 0xFF;      // SETUP包长度错误
        }
        if ( status == 0xFF )    // 操作失败
        {
                SetupReqCode = 0xFF;
                R8_UEP0_RX_CTRL1 = UEP_DATA1 | UEP_R_RES_STALL;
                R8_UEP0_TX_CTRL1 = UEP_DATA1 | UEP_T_RES_STALL;
        }
        else
        {
                SetupLen = (SetupLen<len) ? SetupLen : len;                                            //这次传输上传数据长度
                if(SetupLen)
                {
                        if(UsbSetupBuf->bRequestType & 0x80)                                                                     //上传数据
                        {
                                len = (SetupLen<THIS_ENDP0_SIZE) ? SetupLen : THIS_ENDP0_SIZE;        //本次数据上传长度
                                memcpy( Ep0Buffer, pDescr, len );  /* 加载上传数据 */
                                R16_UEP0_T_LEN1 = len;
                                R8_UEP0_TX_CTRL1 = UEP_DATA1 | UEP_T_RES_ACK;                    // 默认数据包是DATA1
                                SetupLen -= len;
                                pDescr += len;
                        }
                        else                                                                                                     //下传数据
                        {
                                R16_UEP0_T_LEN1 = 0;  // 虽然尚未到状态阶段,但是提前预置上传0长度数据包以防主机提前进入状态阶段
                                R8_UEP0_RX_CTRL1 = UEP_DATA1 | UEP_R_RES_ACK;                    // 默认数据包是DATA1
                        }
                }
                else
                {
                        R16_UEP0_T_LEN1 = 0;
                        R8_UEP0_TX_CTRL1 = UEP_DATA1 | UEP_T_RES_ACK;        //状态阶段
                }
        }
}

/*******************************************************************************
* Function Name  : USB1Dev_EDP0_IN_Deal
* Description    : USB1 设备模式端点0 IN处理
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
void USB1Dev_EDP0_IN_Deal(void)
{
        UINT16 len;

        switch(SetupReqCode)
        {
        case USB_GET_DESCRIPTOR:
                len = (SetupLen<THIS_ENDP0_SIZE) ? SetupLen : THIS_ENDP0_SIZE;        //本次数据上传长度
                memcpy( Ep0Buffer, pDescr, len );  /* 加载上传数据 */
                R16_UEP0_T_LEN1 = len;
                R8_UEP0_TX_CTRL1 ^= UEP_DATA1;  // 翻转
                SetupLen -= len;
                pDescr += len;
                break;
        case USB_SET_ADDRESS:
                R16_UEP0_T_LEN1 = 0;
                R8_UEP0_TX_CTRL1 = UEP_DATA0 | UEP_T_RES_NAK;
                R8_USB1_DEV_AD = DevAddr;
                break;
        default:
                R16_UEP0_T_LEN1 = 0;
                R8_UEP0_TX_CTRL1 = UEP_DATA0 | UEP_T_RES_NAK;
                R8_UEP0_RX_CTRL1 = UEP_DATA0 | UEP_R_RES_ACK;
                break;
        }
}

/*******************************************************************************
* Function Name  : USB1Dev_EDP0_OUT_Deal
* Description    : USB1 设备模式端点0 OUT处理
* Input          : None
* Output         : None
* Return         : None
*******************************************************************************/
UINT16 USB1Dev_EDP0_OUT_Deal(void)
{
        UINT16 len;
        len = R16_USB1_RX_LEN;
        return (len);
}

和之前的相比,修改2个位置:

1.项目名称,位于 .project 文件中:

&lt;name>USB1_DevCH372&lt;/name>

2. 对代码进行了格式化,使用4个空格代替代码中的 Tab

TIPS: ACPIVIEW

之前krishna 在“UEFI Shell 下的 ACPI Dump工具”评论中提到过,EDK2 提供了内置的 ACPI COMMAND:ACPIVIEW,可以直接在 Shell 下查看 ACPI。最近在研究 ACPI 方面的内容,于是研究了一下这个 command 的使用。

Hi Z.t,现在的EDK2已经有现成的ACPI command可用哦,需要手动添加一下,方法如下:

1,编辑ShellPkg\ShellPkg.dsc,在这个地方添加一行:
ShellPkg/Application/Shell/Shell.inf {
# follow line is added by krishna
NULL|ShellPkg/Library/UefiShellAcpiViewCommandLib/UefiShellAcpiViewCommandLib.inf
}
2,Save and "build -p ShellPkg\ShellPkg.dsc -m ShellPkg\Application\Shell\Shell.inf -a X64"
3,Boot this shell and run command "acpiview"

在 EDK2 202108 的版本中, Shell 提供了2种访问这个工具的方法:

1.独立的 EFI 程序 ,源代码在 \ShellPkg\Application\AcpiViewApp

2.内置于 Shell 中的 acpiview 命令。现在无需手工添加,使用下面的命令编译 ShellPkg

 build -a X64 -p ShellPkg\ShellPkg.dsc

之后可以看到2个Shell 的 EFI 文件,其中较大的就包含了acpiview 命令:

运行Shell后,可以使用 help acpiview 查看,支持的命令行参数如下:

ACPIVIEW [[-?] | [[[[-l] | [-s AcpiTable [-d]]] [-q] [-h]] [-r Spec]]]

  -l - Display list of installed ACPI Tables.
  -s - Display only the specified AcpiTable type and only support single invocation option.
         AcpiTable    : The required ACPI Table type.
  -d - Generate a binary file dump of the specified AcpiTable.
  -q - Quiet. Suppress errors and warnings. Disables consistency checks.
  -h - Enable colour highlighting.
  -r - Validate that all required ACPI tables are installed
         Spec  : Specification to validate against.
                 For Arm, the possible values are:
                   0 - Server Base Boot Requirements v1.0
                   1 - Server Base Boot Requirements v1.1
                   2 - Server Base Boot Requirements v1.2
  -? - Show help.

-l 参数显示当前系统中 ACPI Table 的名称

-s 显示指定的 ACPI Table

-d 将指定的 ACPI Table 保存到文件中

特别注意:不要直接运行 acpiview,它会在屏幕输出所有的 ACPI Table 内容非常慢。

Step to UEFI (242)写一个输出当前 IP 的函数

前面的文章中,我们写过输出当前IP 的代码,这次,我们要尝试将这个代码编写为C的函数方便我们调用。

因为 X64 无法内嵌汇编,我们值得参考其他的函数,用如下三步可以实现:

1. \MdePkg\Include\Library\BaseLib.h 中加入函数头定义

/**
Output current RIP to debug port (0x402 for QEMU)

**/
VOID
EFIAPI
AsmCurrentRIP(
	VOID
);

2. \MdePkg\Library\BaseLib\X64\CurrentRIP.nasm 加入我们的函数汇编实现

;------------------------------------------------------------------------------
;
; Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.&lt;BR>
; SPDX-License-Identifier: BSD-2-Clause-Patent
;
; Module Name:
;
;   CurrentRIP.Asm
;
; Abstract:
;
;  Output current RIP to debug port (0x402 for QEMU)
;  
;
; Notes:
;
;------------------------------------------------------------------------------

    DEFAULT REL
    SECTION .text

;------------------------------------------------------------------------------
; VOID
; EFIAPI
; AsmCurrentRIP (
;   VOID
;   );
;------------------------------------------------------------------------------
global ASM_PFX(AsmCurrentRIP)
ASM_PFX(AsmCurrentRIP):
    lea     rax,[$]
    mov     dx,0x402
	
	; 7-0 Bits
    out     dx,al       

    ; 15-8 Bits
	shr     rax,8
    out     dx,al       
	
	; 23-16 Bits
	shr     rax,8
    out     dx,al       

	; 31-24 Bits
	shr     rax,8
    out     dx,al       

	; 39-32 Bits
	shr     rax,8
    out     dx,al       

	; 47-40 Bits
	shr     rax,8
    out     dx,al       

	; 55-48 Bits
	shr     rax,8
    out     dx,al       

	; 63-56 Bits
	shr     rax,8
    out     dx,al     

ret

3. \MdePkg\Library\BaseLib\BaseLib.inf 加入上面的文件

[Sources.X64]
  X64/Thunk16.nasm
  X64/CpuIdEx.nasm
  X64/CpuId.nasm
  X64/LongJump.nasm
  X64/SetJump.nasm
  X64/SwitchStack.nasm
  X64/EnableCache.nasm
  X64/DisableCache.nasm
  X64/WriteTr.nasm
  X64/Lfence.nasm
  X64/CurrentRIP.nasm
  X64/CpuBreakpoint.c | MSFT
  X64/WriteMsr64.c | MSFT
  X64/ReadMsr64.c | MSFT
  X64/CpuPause.nasm| MSFT
  X64/DisableInterrupts.nasm| MSFT

接下来就可以使用这个函数了,比如,我们在\OvmfPkg\Sec\SecMain.c中使用这个函数。

  if (!SevEsIsEnabled ()) {
    //
    // For non SEV-ES guests, just load the IDTR.
    //
    AsmWriteIdtr (&IdtDescriptor);
  } else {
    //
    // Under SEV-ES, the hypervisor can't modify CR0 and so can't enable
    // caching in order to speed up the boot. Enable caching early for
    // an SEV-ES guest.
    //
    AsmEnableCache ();
  }
AsmCurrentRIP();
  DEBUG ((DEBUG_INFO,
    "SecCoreStartupWithStack(0x%x, 0x%x)\n",
    (UINT32)(UINTN)BootFv,
    (UINT32)(UINTN)TopOfCurrentStack
));

之后的运行中,我们也可以在串口 Log 中刚看到输出的 IP。

实际上这样的作法是错误的,有兴趣的朋友可以思考一下:我想输出当前代码运行的IP,上面的方法错在哪里?

接下来,我们在 SecMain.inf 中加入如下命令,生成 SecMain.cod 进行查看:

[BuildOptions]
  MSFT:*_*_X64_CC_FLAGS  = /FAsc /Od

在 SecMain.cod 中我们看到的结果如下:
; 908  :     //
; 909  :     // Under SEV-ES, the hypervisor can't modify CR0 and so can't enable
; 910  :     // caching in order to speed up the boot. Enable caching early for
; 911  :     // an SEV-ES guest.
; 912  :     //
; 913  :     AsmEnableCache ();

  00131	e8 00 00 00 00	 call	 AsmEnableCache
$LN25@SecCoreSta:

; 914  :   }
; 915  : AsmCurrentRIP();

  00136	e8 00 00 00 00	 call	 AsmCurrentRIP
$LN13@SecCoreSta:

; 916  :   DEBUG ((DEBUG_INFO,

  0013b	e8 00 00 00 00	 call	 DebugPrintEnabled
  00140	0f b6 c0	 movzx	 eax, al
  00143	85 c0		 test	 eax, eax
  00145	74 38		 je	 SHORT $LN26@SecCoreSta
$LN16@SecCoreSta:

; 917  :     "SecCoreStartupWithStack(0x%x, 0x%x)\n",
; 918  :     (UINT32)(UINTN)BootFv,
; 919  :     (UINT32)(UINTN)TopOfCurrentStack
; 920  :     ));

那么问题来了,我们是在 AsmCurrentRIP() 中输出的当前 IP。作为一个函数,它的地址是确定的。因此,你在不同的地方调用这个函数,实际上会得到同样的结果。因此,上面的方法是错误的。继续思考,当我们 Call 到这个函数中之后,堆栈中存着调用处的信息(更准确的说是返回位置的信息)。因此,稍微修改我们之前的代码,输出堆栈中的信息即可。

首先,修改后的代码如下:

;------------------------------------------------------------------------------
;
; Copyright (c) 2006 - 2008, Intel Corporation. All rights reserved.<BR>
; SPDX-License-Identifier: BSD-2-Clause-Patent
;
; Module Name:
;
;   CurrentRIP.Asm
;
; Abstract:
;
;  Output current RIP to debug port (0x402 for QEMU)
;  
;
; Notes:
;
;------------------------------------------------------------------------------

    DEFAULT REL
    SECTION .text

;------------------------------------------------------------------------------
; VOID
; EFIAPI
; AsmCurrentRIP (
;   VOID
;   );
;------------------------------------------------------------------------------
global ASM_PFX(AsmCurrentRIP)
ASM_PFX(AsmCurrentRIP):
    mov     rax,[rsp]
    mov     dx,0x402
	
	; 7-0 Bits
    out     dx,al       

    ; 15-8 Bits
	shr     rax,8
    out     dx,al       
	
	; 23-16 Bits
	shr     rax,8
    out     dx,al       

	; 31-24 Bits
	shr     rax,8
    out     dx,al       

	; 39-32 Bits
	shr     rax,8
    out     dx,al       

	; 47-40 Bits
	shr     rax,8
    out     dx,al       

	; 55-48 Bits
	shr     rax,8
    out     dx,al       

	; 63-56 Bits
	shr     rax,8
    out     dx,al     

ret

在 SecMain.c 中调用如下:

//
  // Find PEI Core entry point. It will report SEC and Pei Core debug information if remote debug
  // is enabled.
  //
  BootFv = (EFI_FIRMWARE_VOLUME_HEADER *)SecCoreData->BootFirmwareVolumeBase;
  FindAndReportEntryPoints (&BootFv, &PeiCoreEntryPoint);
  SecCoreData->BootFirmwareVolumeBase = BootFv;
  SecCoreData->BootFirmwareVolumeSize = (UINTN) BootFv->FvLength;
  AsmCurrentRIP(); //ZivDebug
  DEBUG ((DEBUG_INFO,
    "Check [BootFirmwareVolumeBase]-0x%X [PeiCoreEntryPoint]=0x%X BootFirmwareVolumeBase=0x%X \n",
	(UINT64)BootFv,
    (UINT64)(PeiCoreEntryPoint),
	(UINT64)(*PeiCoreEntryPoint)
    ));
  AsmCurrentRIP(); //ZivDebug	
  //
  // Transfer the control to the PEI core
  //
  (*PeiCoreEntryPoint) (SecCoreData, (EFI_PEI_PPI_DESCRIPTOR *)&mPrivateDispatchTable);

在对应的 Cod 文件中查看到的结果如下:

; 1016 :   AsmCurrentRIP(); //ZivDebug

  00050	e8 00 00 00 00	 call	 AsmCurrentRIP
$LN4@SecStartup:

; 1017 :   DEBUG ((DEBUG_INFO,

  00055	e8 00 00 00 00	 call	 DebugPrintEnabled
  0005a	0f b6 c0	 movzx	 eax, al
  0005d	85 c0		 test	 eax, eax
  0005f	74 3c		 je	 SHORT $LN11@SecStartup
$LN7@SecStartup:

; 1018 :     "Check [BootFirmwareVolumeBase]-0x%X [PeiCoreEntryPoint]=0x%X BootFirmwareVolumeBase=0x%X \n",
; 1019 : 	(UINT64)BootFv,
; 1020 :     (UINT64)(PeiCoreEntryPoint),
; 1021 : 	(UINT64)(*PeiCoreEntryPoint)
; 1022 :     ));

  00061	b9 40 00 00 00	 mov	 ecx, 64			; 00000040H
  00066	e8 00 00 00 00	 call	 DebugPrintLevelEnabled
  0006b	0f b6 c0	 movzx	 eax, al
  0006e	85 c0		 test	 eax, eax
  00070	74 25		 je	 SHORT $LN12@SecStartup
  00072	48 8b 44 24 40	 mov	 rax, QWORD PTR PeiCoreEntryPoint$[rsp]
  00077	48 89 44 24 20	 mov	 QWORD PTR [rsp+32], rax
  0007c	4c 8b 4c 24 40	 mov	 r9, QWORD PTR PeiCoreEntryPoint$[rsp]
  00081	4c 8b 44 24 30	 mov	 r8, QWORD PTR BootFv$[rsp]
  00086	48 8d 15 00 00
	00 00		 lea	 rdx, OFFSET FLAT:??_C@_0FL@FMODEEHL@Check?5?$FLBootFirmwareVolumeBase?$FN?90@
  0008d	b9 40 00 00 00	 mov	 ecx, 64			; 00000040H
  00092	e8 00 00 00 00	 call	 DebugPrint
$LN12@SecStartup:
  00097	33 c0		 xor	 eax, eax
  00099	85 c0		 test	 eax, eax
  0009b	75 c4		 jne	 SHORT $LN7@SecStartup
$LN11@SecStartup:

; 1017 :   DEBUG ((DEBUG_INFO,

  0009d	33 c0		 xor	 eax, eax
  0009f	85 c0		 test	 eax, eax
  000a1	75 b2		 jne	 SHORT $LN4@SecStartup

; 1023 :   AsmCurrentRIP(); //ZivDebug	

  000a3	e8 00 00 00 00	 call	 AsmCurrentRIP

第一次call 是在 AsmCurrentRIP 00050, 第二次是call 是在 000a3的位置,二者相差 (a3-50=53)。接下来查看 Log 中的输出结果:

红色位置就是2次 Call 输出结果

前面是 FFFC E309 后一个是 FFFC E35C二者相差也是 53,因此,可以确定函数输出没问题。

晚上上述实验之后,我开始翻阅 《Step To UEFI》系列文章,找到了《Step to UEFI (201)直接取得函数返回地址》【参考1】就是说可以直接通过 VC 内置函数取得调用处下一条指令的地址。

参考:

1.http://www.lab-z.com/stu201aor/

Micron 内存颗粒命名规则

在阅读Micron 内存颗粒的 DataSheet过程中经常碰到 -125e,-062e 这样的代码,起初我以为是JEDEC规定的代号,但是多次搜索无果,最近偶然间搞清楚了,这个是 Micron 家自己的命名。

以MT40A2G8SA-062E:F【参考1】为例,Datasheet中可以看到如下定义:

Timing -Cycle Time

  • 0.625ns@CL=22(DDR4-3200)  -062E
  • 0.682ns@CL=21(DDR4-2933)  -068

简单的说 -062 就是代表了 0.625ns,而0.6225=1秒/(1600Mhz),这里3200Mhz 的内存内部主频是 1600。同样的  0.6818=1秒/(2933Mhz/2)。对于-062E 最后的 E 则是表示不同的时序。在【参考2】有如下的解释:

代号对应的基本参数

所以,这里只是Micron 自定义的编号。此外,Micron内存模组有时候还会使用 nGm 这种方式进行编号。例如,内存条产品 MTA36ASF4G72PZ – 32GB 【参考1】:

– 0.62ns @ CL = 22 (DDR4-3200) -3G2

– 0.682ns @ CL = 21 (DDR4-2933) -2G9

– 0.75ns @ CL = 19 (DDR4-2666) -2G6

– 0.83ns @ CL = 17 (DDR4-2400) -2G3

表明最高速度是 n.mG 。

参考:

  1. https://media-www.micron.com/-/media/client/global/documents/products/data-sheet/dram/ddr4/16gb_ddr4_sdram.pdf?rev=40fa0d2bdf344b0fb7a36356d8b6b8f7
  2. https://www.micron.com/-/media/client/global/documents/products/part-numbering-guide/numdram.pdf
  3. https://www.micron.com/-/media/client/global/documents/products/data-sheet/modules/rdimm/ddr4/asf36c4gx72pz.pdf

Ampere Computing 职位(2022 Jan)

Req IdTitleLocationLink for Job Description
NS870Bigdata System and Software EngineerSHhttps://amperecomputing.com/apply/?p=job%2FoeMyffwx
NS1213System Control Firmware EngineerSHhttps://amperecomputing.com/apply/?p=job/o7gWhfwk&nl=1
NS935Platform Applications EngineerSH/BJhttps://jobs.jobvite.com/amperecomputing/job/oTd6ffwb
NS1173Cloud Solution ArchitectSHhttps://amperecomputing.com/apply/?p=job%2FopnChfwp
NS1153Account ManagerBJhttps://amperecomputing.com/apply/?p=job%2FopHxhfwE
NS1167Field Applications EngineerSH/BJhttps://amperecomputing.com/apply/?p=job%2FoxpBhfwy
NS1187Sales Development ManagerSH/BJ/SZhttps://amperecomputing.com/apply/?p=job/oVyLhfwf&nl=1
NS1146IT Service Desk / System AdministratorSHhttps://amperecomputing.com/apply/?p=job/o4zuhfw8&nl=1

有兴趣的朋友可以直接联系 jun.chen@amperecomputing.com

Step to UEFI (241)SecMain 中的 CONSTRUCTOR

在 SecCoreStartupWithStack() 函数中,有下面这一条调用:

ProcessLibraryConstructorList (NULL, NULL);

具体调用的代码在\Build\OvmfX64\DEBUG_VS2015x86\X64\OvmfPkg\Sec\SecMain\DEBUG\AutoGen.c 中:

VOID
EFIAPI
ProcessLibraryConstructorList (
  VOID
  )
{
  RETURN_STATUS  Status;

  Status = PlatformRomDebugLibIoPortConstructor ();
  ASSERT_RETURN_ERROR (Status);

  Status = AcpiTimerLibConstructor ();
  ASSERT_RETURN_ERROR (Status);

  Status = LzmaDecompressLibConstructor ();
  ASSERT_RETURN_ERROR (Status);
}
  1. PlatformRomDebugLibIoPortConstructor() 在 DebugLibDetectRom.c 中,函数是空的直接 return 了事;
  2. AcpiTimerLibConstructor() 在 BaseRomAcpiTimerLib.c 中,可以看到是取得当前一些 IO Base 的信息
/**
  The constructor function enables ACPI IO space.

  If ACPI I/O space not enabled, this function will enable it.
  It will always return RETURN_SUCCESS.

  @retval EFI_SUCCESS   The constructor always returns RETURN_SUCCESS.

**/
RETURN_STATUS
EFIAPI
AcpiTimerLibConstructor (
  VOID
  )
{
  UINT16 HostBridgeDevId;
  UINTN Pmba;
  UINT32 PmbaAndVal;
  UINT32 PmbaOrVal;
  UINTN AcpiCtlReg;
  UINT8 AcpiEnBit;

  //
  // Query Host Bridge DID to determine platform type
  //
  HostBridgeDevId = PciRead16 (OVMF_HOSTBRIDGE_DID);
  switch (HostBridgeDevId) {
    case INTEL_82441_DEVICE_ID:
      Pmba       = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMBA);
      PmbaAndVal = ~(UINT32)PIIX4_PMBA_MASK;
      PmbaOrVal  = PIIX4_PMBA_VALUE;
      AcpiCtlReg = POWER_MGMT_REGISTER_PIIX4 (PIIX4_PMREGMISC);
      AcpiEnBit  = PIIX4_PMREGMISC_PMIOSE;
      break;
    case INTEL_Q35_MCH_DEVICE_ID:
      Pmba       = POWER_MGMT_REGISTER_Q35 (ICH9_PMBASE);
      PmbaAndVal = ~(UINT32)ICH9_PMBASE_MASK;
      PmbaOrVal  = ICH9_PMBASE_VALUE;
      AcpiCtlReg = POWER_MGMT_REGISTER_Q35 (ICH9_ACPI_CNTL);
      AcpiEnBit  = ICH9_ACPI_CNTL_ACPI_EN;
      break;
    default:
      DEBUG ((DEBUG_ERROR, "%a: Unknown Host Bridge Device ID: 0x%04x\n",
        __FUNCTION__, HostBridgeDevId));
      ASSERT (FALSE);
      return RETURN_UNSUPPORTED;
  }

  //
  // Check to see if the Power Management Base Address is already enabled
  //
  if ((PciRead8 (AcpiCtlReg) & AcpiEnBit) == 0) {
    //
    // If the Power Management Base Address is not programmed,
    // then program it now.
    //
    PciAndThenOr32 (Pmba, PmbaAndVal, PmbaOrVal);

    //
    // Enable PMBA I/O port decodes
    //
    PciOr8 (AcpiCtlReg, AcpiEnBit);
  }

  return RETURN_SUCCESS;
}

3.LzmaDecompressLibConstructor 函数定义在\MdeModulePkg\Library\LzmaCustomDecompressLib\GuidedSectionExtraction.c 中:

/**
  Register LzmaDecompress and LzmaDecompressGetInfo handlers with LzmaCustomerDecompressGuid.

  @retval  RETURN_SUCCESS            Register successfully.
  @retval  RETURN_OUT_OF_RESOURCES   No enough memory to store this handler.
**/
EFI_STATUS
EFIAPI
LzmaDecompressLibConstructor (
  VOID
  )
{
  return ExtractGuidedSectionRegisterHandlers (
          &gLzmaCustomDecompressGuid,
          LzmaGuidedSectionGetInfo,
          LzmaGuidedSectionExtraction
          );
}

在 OvmfPkgX64.dsc 中[LibraryClasses.common.SEC] 这个Section可以看到如下三个 Lib:

  1. DebugLib|OvmfPkg/Library/PlatformDebugLibIoPort/PlatformRomDebugLibIoPort.inf
  2. TimerLib|OvmfPkg/Library/AcpiTimerLib/BaseRomAcpiTimerLib.inf
  3. <LibraryClasses> NULL|MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaCustomDecompressLib.inf

上述的INF 文件中都有定义   CONSTRUCTOR   ,例如: 

CONSTRUCTOR                    = LzmaDecompressLibConstructor

因此,就是说SecMain.inf给出了引用的 LIB,然后在编译过程中如果发现对应的 LIB 有指定CONSTRUCTOR   , 那么就会构造 ProcessLibraryConstructorList() 。这样做的目的是:保证在调用一些LIB之前,已经完成了对应的初始化。类似的还有ProcessLibraryDestructorList(),可以用来完成一些LIB的收尾动作。

接下来的一个问题是:谁做了这个自动生成的动作?

答案是: build.py

在\BaseTools\Source\Python\AutoGen\GenC.py 有下面的函数:

## Create code for library constructor
#
#   @param      Info        The ModuleAutoGen object
#   @param      AutoGenC    The TemplateString object for C code
#   @param      AutoGenH    The TemplateString object for header file
#
def CreateLibraryConstructorCode(Info, AutoGenC, AutoGenH):

在其中加入如下 代码用于验证:

    for Lib in DependentLibraryList:
        if len(Lib.ConstructorList) <= 0:
            continue
        Dict = {'Function':Lib.ConstructorList}
        if Lib.ModuleType in [SUP_MODULE_BASE, SUP_MODULE_SEC]:
            ConstructorPrototypeString.Append("//www.lab-z.com testing");
            ConstructorPrototypeString.Append(gLibraryStructorPrototype[SUP_MODULE_BASE].Replace(Dict))
            ConstructorCallingString.Append(gLibraryStructorCall[SUP_MODULE_BASE].Replace(Dict))

重新编译代码,再次查看\Build\OvmfX64\DEBUG_VS2015x86\X64\OvmfPkg\Sec\SecMain\DEBUG\AutoGen.c可以看到我们测试的注释代码已经写入 AutoGen.c 了:

GLOBAL_REMOVE_IF_UNREFERENCED const BOOLEAN _gPcd_FixedAtBuild_PcdCpuSmmStackGuard = _PCD_VALUE_PcdCpuSmmStackGuard;
extern const  BOOLEAN  _gPcd_FixedAtBuild_PcdCpuSmmStackGuard;
#define _PCD_GET_MODE_BOOL_PcdCpuSmmStackGuard  _gPcd_FixedAtBuild_PcdCpuSmmStackGuard
//#define _PCD_SET_MODE_BOOL_PcdCpuSmmStackGuard  ASSERT(FALSE)  // It is not allowed to set value for a FIXED_AT_BUILD PCD

//www.lab-z.com testing
RETURN_STATUS
EFIAPI
PlatformRomDebugLibIoPortConstructor (
  VOID
  );
//www.lab-z.com testing
RETURN_STATUS
EFIAPI
AcpiTimerLibConstructor (
  VOID
  );
//www.lab-z.com testing
RETURN_STATUS
EFIAPI
LzmaDecompressLibConstructor (
  VOID
  );


VOID
EFIAPI
ProcessLibraryConstructorList (
  VOID
  )
{
  RETURN_STATUS  Status;

  Status = PlatformRomDebugLibIoPortConstructor ();
  ASSERT_RETURN_ERROR (Status);

  Status = AcpiTimerLibConstructor ();
  ASSERT_RETURN_ERROR (Status);

  Status = LzmaDecompressLibConstructor ();
  ASSERT_RETURN_ERROR (Status);

}

上述实验是基于 edk202108, 如果你在其他版本实验,有可能调用的是 build.exe,实验无法成功。

能在 ADL 上使用的 RU

今天忽然发现之前的RU 无法在 Intel 最新的 ADL 平台上工作(现象是运行之后黑屏)。我忽然也意识到在后台留言“密码“二字的朋友,他们需要 RU 压缩包的解压密码。

特此声明: RU 系列的作者是: AMI 的 James。

正式的下载网站是https://github.com/JamesAmiTw/ru-uefi , 其中的压缩包密码可以在

他个人网站 http://ruexe.blogspot.com/ 看到(该网站需要科学上网)。。

为了方便大家,这里放上一个最新的无密码 RU (截至2021年12月27日)。有需要的朋友可以下载。

=============================================================

2024年1月5日

更新的版本,解压密码 cc92d99a-ab87-43c3-99df-a5957ea53be9

=============================================================

最新版本更新在 https://www.lab-z.com/intru/ 页面

Step to UEFI (240)分析第一条 Debug Log 的输出方法

前面提到,Debug Log 中的第一条信息位于 SecMain.c 中的 SecCoreStartupWithStack() 函数中:

  DEBUG ((DEBUG_INFO,
    "SecCoreStartupWithStack(0x%x, 0x%x)\n",
    (UINT32)(UINTN)BootFv,
    (UINT32)(UINTN)TopOfCurrentStack
));

这次就分析一下 OVMF 这段代码的具体实现方式。DEBUG 这个宏,之前我们有分析过,有兴趣的读者可以在【参考1】看到之前的文章。

1.这里的 DEBUG 宏可以在  \MdePkg\Include\Library/DebugLib.h 看到,这个和我们之前遇到的是完全相同的定义:

/**
  Macro that calls DebugPrint().

  If MDEPKG_NDEBUG is not defined and the DEBUG_PROPERTY_DEBUG_PRINT_ENABLED
  bit of PcdDebugProperyMask is set, then this macro passes Expression to
  DebugPrint().

  @param  Expression  Expression containing an error level, a format string,
                      and a variable argument list based on the format string.


**/
#if !defined(MDEPKG_NDEBUG)
  #define DEBUG(Expression)        \
    do {                           \
      if (DebugPrintEnabled ()) {  \
        _DEBUG (Expression);       \
      }                            \
    } while (FALSE)
#else
  #define DEBUG(Expression)
#endif

1.1 其中的 DebugPrintEnabled () 定义在 \OvmfPkg\Library\PlatformDebugLibIoPort\DebugLib.c 文件中,OVMF 这个函数是检查编译期定义

/**
  Returns TRUE if DEBUG() macros are enabled.

  This function returns TRUE if the DEBUG_PROPERTY_DEBUG_PRINT_ENABLED bit of
  PcdDebugProperyMask is set.  Otherwise FALSE is returned.

  @retval  TRUE    The DEBUG_PROPERTY_DEBUG_PRINT_ENABLED bit of PcdDebugProperyMask is set.
  @retval  FALSE   The DEBUG_PROPERTY_DEBUG_PRINT_ENABLED bit of PcdDebugProperyMask is clear.

**/
BOOLEAN
EFIAPI
DebugPrintEnabled (
  VOID
  )
{
  return (BOOLEAN) ((PcdGet8(PcdDebugPropertyMask) &amp; DEBUG_PROPERTY_DEBUG_PRINT_ENABLED) != 0);
}

1.2 _DEBUG 的定义在 DebugLib.h 文件中,其中 DebugPrint() 是真正实现功能的函数

/**
  Internal worker macro that calls DebugPrint().

  This macro calls DebugPrint() passing in the debug error level, a format
  string, and a variable argument list.
  __VA_ARGS__ is not supported by EBC compiler, Microsoft Visual Studio .NET 2003
  and Microsoft Windows Server 2003 Driver Development Kit (Microsoft WINDDK) version 3790.1830.

  @param  Expression  Expression containing an error level, a format string,
                      and a variable argument list based on the format string.

**/

#if !defined(MDE_CPU_EBC) &amp;&amp; (!defined (_MSC_VER) || _MSC_VER > 1400)
  #define _DEBUG_PRINT(PrintLevel, ...)              \
    do {                                             \
      if (DebugPrintLevelEnabled (PrintLevel)) {     \
        DebugPrint (PrintLevel, ##__VA_ARGS__);      \
      }                                              \
    } while (FALSE)
  #define _DEBUG(Expression)   _DEBUG_PRINT Expression
#else
#define _DEBUG(Expression)   DebugPrint Expression
#endif

1.2.1 \OvmfPkg\Library\PlatformDebugLibIoPort\DebugLib.c 中定义了 DebugPrint() 函数:

/**
  Prints a debug message to the debug output device if the specified error level is enabled.

  If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function
  GetDebugPrintErrorLevel (), then print the message specified by Format and the
  associated variable argument list to the debug output device.

  If Format is NULL, then ASSERT().

  @param  ErrorLevel  The error level of the debug message.
  @param  Format      Format string for the debug message to print.
  @param  ...         Variable argument list whose contents are accessed
                      based on the format string specified by Format.

**/
VOID
EFIAPI
DebugPrint (
  IN  UINTN        ErrorLevel,
  IN  CONST CHAR8  *Format,
  ...
  )
{
  VA_LIST         Marker;

  VA_START (Marker, Format);
  DebugVPrint (ErrorLevel, Format, Marker);
  VA_END (Marker);
}

1.2.2 同一个文件中,定义了函数DebugVPrint()

/**
  Prints a debug message to the debug output device if the specified
  error level is enabled.

  If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function
  GetDebugPrintErrorLevel (), then print the message specified by Format and
  the associated variable argument list to the debug output device.

  If Format is NULL, then ASSERT().

  @param  ErrorLevel    The error level of the debug message.
  @param  Format        Format string for the debug message to print.
  @param  VaListMarker  VA_LIST marker for the variable argument list.

**/
VOID
EFIAPI
DebugVPrint (
  IN  UINTN         ErrorLevel,
  IN  CONST CHAR8   *Format,
  IN  VA_LIST       VaListMarker
  )
{
  DebugPrintMarker (ErrorLevel, Format, VaListMarker, NULL);
}

1.2.3 同一个文件中 DebugPrintMarker() 代码如下:

/**
  Prints a debug message to the debug output device if the specified
  error level is enabled base on Null-terminated format string and a
  VA_LIST argument list or a BASE_LIST argument list.

  If any bit in ErrorLevel is also set in DebugPrintErrorLevelLib function
  GetDebugPrintErrorLevel (), then print the message specified by Format and
  the associated variable argument list to the debug output device.

  If Format is NULL, then ASSERT().

  @param  ErrorLevel      The error level of the debug message.
  @param  Format          Format string for the debug message to print.
  @param  VaListMarker    VA_LIST marker for the variable argument list.
  @param  BaseListMarker  BASE_LIST marker for the variable argument list.

**/
VOID
DebugPrintMarker (
  IN  UINTN         ErrorLevel,
  IN  CONST CHAR8   *Format,
  IN  VA_LIST       VaListMarker,
  IN  BASE_LIST     BaseListMarker
  )
{
  CHAR8    Buffer[MAX_DEBUG_MESSAGE_LENGTH];
  UINTN    Length;

  //
  // If Format is NULL, then ASSERT().
  //
  ASSERT (Format != NULL);

  //
  // Check if the global mask disables this message or the device is inactive
  //
  if ((ErrorLevel &amp; GetDebugPrintErrorLevel ()) == 0 ||
      !PlatformDebugLibIoPortFound ()) {
    return;
  }

  //
  // Convert the DEBUG() message to an ASCII String
  //
  if (BaseListMarker == NULL) {
    Length = AsciiVSPrint (Buffer, sizeof (Buffer), Format, VaListMarker);
  } else {
    Length = AsciiBSPrint (Buffer, sizeof (Buffer), Format, BaseListMarker);
  }

  //
  // Send the print string to the debug I/O port
  //
  IoWriteFifo8 (PcdGet16 (PcdDebugIoPort), Length, Buffer);
}

其中PcdDebugIoPort定义在 \OvmfPkg\OvmfPkg.dec文件中:

  ## This flag is used to control the destination port for PlatformDebugLibIoPort
  gUefiOvmfPkgTokenSpaceGuid.PcdDebugIoPort|0x402|UINT16|4

因此,就是说这里的DEBUG 是通过向 0x402 直接写入 ASCII 来实现的。

参考:

1. https://www.lab-z.com/stu170dbg/

注册表关闭 ModernStandby 的方法

在注册表 HKLM \ System \ CurrentControlSet \ Control \ Power 创建 PlatformAoAcOverride 属性为 DWORD32 值为0.

上述方法来自:

1.https://zhuanlan.zhihu.com/p/339761713 在Win10 v2004以上版本启用S3睡眠并禁用Modern Standby待机的终极解决方案

2.http://blog.sinovale.com/3867.html 戴尔dell笔记本无法进入睡眠的解决办法

3.https://www.reddit.com/r/Dell/comments/h0r56s/getting_back_s3_sleep_and_disabling_modern/