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一開始的返回值也沒有了,那麼函數也無法跳轉回去了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章