UEFI下如何在C语言嵌入汇编

UEFI下如何在C语言内嵌入汇编

有两种方法:分别是以内嵌汇编的形式和直接就是汇编的形式存在,内嵌汇编不够灵活,而且代码的可读性和维护性都比较差,强烈推荐第二种方式。

内嵌汇编的形式

 __asm__ __volatile__(                                                                        
    ".set_noat______\n"                                                                        
    ".set_mips64______\n"                                                                      
    "move_$t0, %0_____\n"                                                                      
    "move_$t1, %1_____\n"                                                                      
    "dli__$t2, 0x00000000ffffffff_\n"                                                          
    "and__$t1,$t2_____\n"                                                                      
    "dsll_$t0,32______\n"                                                                      
    "or_$sp, $t0,$t1____\n"                                                                    
    "jr_%2______\n"                                                                            
    "nop________\n"                                                                            
    ".set_at______\n"                                                                          
    : /* No outputs */                                                                         
    :"r"(Sphigh), "r"(Splow),"r"(StrRa)                                                        
    );

使用__asm__ __volatile()修饰一下,每一行汇编都需要用“”包起来。其中%0代表Sphigh,%1,代表Splow,%2代表StrRa。注意“r”后面跟的参数就是上面使用的%i的值。

直接包含汇编代码

首先新建一个汇编文件,假如是MIPS.S

这里面我们就可以直接写汇编代码,但是代码需要特定的指令修饰:
下面看详细的代码写法:

.global ConfigOnePll
    .end    ConfigOnePll
    //.set    noreorder
    .set    mips3
ConfigOnePll:
//input parameters:
//a0: pll address
//a1: pll value
//a2: div_refc
//output value:
//v0: 0: success; 1: fail.

    move t9,ra  /*save ra*/
    //switch to backup clk
    lw      t1, 0x4(a0)
    li      t2, (0x7 << LS7A_PLL_SEL0_OFFSET)
    not     t2, t2
    and     t1, t1, t2
    sw      t1, 0x4(a0)

    //power down pll
    lw      t1, 0x4(a0)
    li      t2, (1 << LS7A_PLL_PD_OFFSET)
    or      t1, t1, t2
    sw      t1, 0x4(a0)
//configure pll parameters
    sw      a1, 0x0(a0)
    //set div_refc
    lw      t1, 0x4(a0)
    li      t2, (0x3f << LS7A_PLL_DIV_REFC_OFFSET)
    not     t2, t2
    and     t1, t1, t2
    or      t1, t1, a2
    sw      t1, 0x4(a0)

    //enable pll configure
    lw      t1, 0x4(a0)
    li      t2, (1 << LS7A_PLL_SET_OFFSET)
    or      t1, t1, t2
    sw      t1, 0x4(a0)

    //not bypass pll
    lw      t1, 0x4(a0)
    li      t2, (0x1 << LS7A_PLL_BYPASS_OFFSET)
    not     t2, t2
    and     t1, t1, t2
    sw      t1, 0x4(a0)

    //power up pll
    lw      t1, 0x4(a0)
    li      t2, (0x1 << LS7A_PLL_PD_OFFSET)
    not     t2, t2
    and     t1, t1, t2
    sw      t1, 0x4(a0)
    //poll lock signal
    li      v1, 0x1000
    move    v0, $0
    li      t2, (0x1 << LS7A_PLL_LOCK_OFFSET)
1:
    lw      t1, 0x4(a0)
    and     t1, t1, t2
    subu    v1, v1, 1
    beqz    v1, 1f
    nop
    beqz    t1, 1b
    nop

    //select pll out
    lw      t1, 0x4(a0)
    li      t2, (0x7 << LS7A_PLL_SEL0_OFFSET)
    or      t1, t1, t2
    sw      t1, 0x4(a0)
    b       2f
    nop
1:  //PLL lock fail
    ori     v0, v0, 1 

2:
    move ra,t9
    jr      ra
    nop
    .end    ConfigOnePll

上面的代码和下面的C实现的函数是完全等价的:

EFI_STATUS
ConfigOnePll (
  IN UINT64 PllBase,
  IN UINT32 PllVal,
  IN UINT32 DivRefc
  )
{
  UINT32 i,Val32;
  Val32 = Readl(PllBase + 0x4);
  Val32 &= ~(0x7 << LS7A_PLL_SEL0_OFFSET);//switch to backup clk
  Readl(PllBase + 0x4) = Val32;

  Val32 = Readl(PllBase + 0x4);
  Val32 |= (1 << LS7A_PLL_PD_OFFSET);//power down pll
  Readl(PllBase + 0x4) = Val32;

  Val32 = Readl(PllBase + 0x4);
  Val32 &= ~(1 << LS7A_PLL_SET_OFFSET);//disable pll configure
  Readl(PllBase + 0x4) = Val32;

//configure pll parameters
  Readl(PllBase) = PllVal;

  Val32 = Readl(PllBase + 0x4);
  Val32 = (Val32 & ~(0x3f << LS7A_PLL_DIV_REFC_OFFSET)) | DivRefc;
  Readl(PllBase + 0x4) = Val32;

  Val32 = Readl(PllBase + 0x4);
  Val32 |= (1 << LS7A_PLL_SET_OFFSET);//enable pll configure
  Readl(PllBase + 0x4) = Val32;

  Val32 = Readl(PllBase + 0x4);
  Val32 &= ~(0x1 << LS7A_PLL_BYPASS_OFFSET);//not bypass pll
  Readl(PllBase + 0x4) = Val32;

  Val32 = Readl(PllBase + 0x4);                                                                                                                                                               
  Val32 &= ~(0x1 << LS7A_PLL_PD_OFFSET);//power up pll
  Readl(PllBase + 0x4) = Val32;
  //poll lock signal
  i = 0x1000;
  do{
    Val32 = Readl(PllBase + 0x4);
    Val32 &= (0x1 << LS7A_PLL_LOCK_OFFSET);
    i--;
    if(i== 0)return EFI_INVALID_PARAMETER;
  }while(Val32 == 0);

  Val32 = Readl(PllBase + 0x4);
  Val32 |= (0x7 << LS7A_PLL_SEL0_OFFSET);
  Readl(PllBase + 0x4) = Val32;

  return EFI_SUCCESS;
}

根据上面的代码可知,函数的格式开头需要使用
.global ConfigOnePll
.end ConfigOnePll
.set noreorder
.set mips3
ConfigOnePll:
修饰,ConfigOnePll:这里才是函数的主体,函数需要使用 .end ConfigOnePll修饰,表示函数的结尾。
这里需要注意一下几点:
(1)MIPS使用a0-a3来作为传参使用,也就是C函数的前4个参数都是用这4个寄存器来传递的,如果传递的参数多余4个,那么就使用堆栈来传递。这里不考虑多余4个参数的汇编代码。所以在调用ConfigOnePll时,传递的参数规则和代码内的实现必须一致。下面是这个函数的调用。


//PIX1, default 38.2MHz for x800x600                                                           
  if (ConfigOnePll(CONF_PLL4_OFFSET + LS7A_CONFBUS_BASE_ADDR, LS7A_PLL_VALUE(104, 68, 68, 68), 0x4)) {                                                                                        
    DbgPrint(EFI_D_INFO, "!!!LS7A PLL4 soft configure fail.\r\n");                             
    ASSERT(0);                                                                                 
    return EFI_INVALID_PARAMETER;                                                              
  }

函数声明就是在调用的C文件中extern这个函数,如下:

extern UINT64
ConfigOnePll (
  IN UINT64 PllBase,
  IN UINT32 PllVal,
  IN UINT32 DivRefc
  );

(2)汇编代码中进来的第一条指令就是将ra的值保存在了t9寄存器中,然后在函数快结尾的时候,又将ra的值从t9中读出来给ra,然后跳转到ra的地址去执行函数。保存ra的值,就是为了防止这个函数再调用其他的函数时,就会将ra破坏掉,导致函数无法跳回。这里需要注意,t9在这个函数执行结束之前,是不能再被使用的,如果使用了,ra一开始的返回值也没有了,那么函数也无法跳转回去了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值