一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Essential Linux Device Driver附錄A . Linux匯編

 orion360doc 2013-07-10

Essential Linux Device Driver附錄A . Linux匯編

分類: Linux Kernel開發(fā) 621人閱讀 評論(0) 收藏 舉報

By 宋寶華 / 本系列文章交流與討論:@宋寶華Barry

設(shè)備驅(qū)動程序有時需要用匯編實現(xiàn)一些代碼片斷,因此讓我們看看Linux上匯編編程的不同特性。

圖A.1顯示了Linux在PC兼容系統(tǒng)上的引導(dǎo)順序,是第2章“內(nèi)核一瞥”中圖2.1的縮減版。圖中的固件組件是用不同的匯編語法實現(xiàn)的:

· BIOS通常全部用匯編編寫。一些流行的PC BIOS使用像Microsoft Macro Assembler (MASM)這樣的匯編來編碼。

· Linux 引導(dǎo)程序,像LILO和GRUB用C與匯編混合編寫。SYSLINUX引導(dǎo)程序整個用Netwide Assembler(NASM)匯編編寫。

· 實模式的Linux啟動代碼使用GNU匯編器(GAS)編碼。

· 保護模式的BIOS調(diào)用用內(nèi)聯(lián)匯編編寫。內(nèi)聯(lián)匯編是GCC支持的結(jié)構(gòu),在C語句之間插入?yún)R編。

圖 A.1. 固件組件與匯編語法

clip_image002

在圖A.1中,上面的兩個組件通常遵守基于Intel的匯編語法,而下面的兩個用AR&T(或GAS)語法來編碼。也有一些例外,GRUB的匯編部分就使用GAS。

為了演示這兩種語法之間的差異,考慮如下輸出一個字節(jié)到并口的代碼。在BIOS或引導(dǎo)程序所使用的Intel格式中,你將會編寫代碼:

  1. mov dx, 03BCh ;0x3BC is the I/O address of the parallel port  
  2.   
  3. mov al, 0ABh ;0xAB is the data to be output  
  4.   
  5. out dx, al ;Send data to the parallel port  

然而,如果你想從Linux實模式啟動代碼中完成同樣的工作,你將需要編寫如下代碼:

  1. movw $0x3BC, %dx  
  2.   
  3. movb $0xAB, %al  
  4.   
  5. outb %al, %dx  
你會發(fā)現(xiàn),不像Intel格式,在AT&T語法中,首先出現(xiàn)的是源操作數(shù),目的操作數(shù)在其后。AT&T格式中的寄存器名字由%開始,立即數(shù)用$開始。AT&T的操作碼為了指定內(nèi)存操作數(shù)的寬度,都帶有后綴如b(針對字節(jié))和w(針對字);而Intel語法中通過查看操作數(shù)而不是操作碼來實現(xiàn)此目的。在Intel語法中,為了移動指針引用,你需要為操作數(shù)指定前綴,如byte ptr。

學(xué)習(xí)AT&T語法的益處是它被GAS和內(nèi)聯(lián)GCC所支持,而GAS和GCC不僅運行于基于Intel的系統(tǒng)上,也運行于各種處理器架構(gòu)。

下面,讓我們使用GCC內(nèi)聯(lián)匯編重寫前面的代碼片斷,它是你在保護模式的內(nèi)核將要用到的:

  1. unsigned short port = 0x3BC;  
  2.   
  3. unsigned char data = 0xAB;  
  4.   
  5. asm("outb %%al, %%dx\n\t"  
  6.   
  7. :  
  8.   
  9. : "a" (data), "d" (port)  
  10.   
  11. );  

GCC支持的匯編格式通常如下:

  1. asm(assembly : output operand constraints : input operand constraints : clobbered operand specifier );  
在操作數(shù)項,a,b,c,d,S和D分別代表EAX,EBX,ECX,EDX,ESI和EDI寄存器。輸入操作數(shù)constraint用于在執(zhí)行匯編指令之前,將數(shù)據(jù)從提供的變量里拷貝至寄存器。關(guān)于GCC內(nèi)聯(lián)匯編語法的細節(jié)請查看GCC 內(nèi)聯(lián)匯編指南(www./gferg/ldp/GCC-Inline-Assembly-HOWTO.html)。

在我們的例子中,唯一用到的constraint是針對輸入操作數(shù)的。此約束有效地拷貝data的值至AL寄存器,以及port的值至DX寄存器。在內(nèi)聯(lián)匯編中,寄存器名由%%開始,因為%被用于指定提供的操作數(shù)。%i代表第i個操作數(shù),因此,在前面的例子內(nèi)聯(lián)匯編代碼片斷中,如果你想指定data和port,可以分別使用%0和%1。

為了對內(nèi)聯(lián)匯編轉(zhuǎn)換有更清晰的了解,讓我們看看對應(yīng)于前面的內(nèi)聯(lián)匯編片斷、通過提供-s命令行參數(shù)給GCC,由編譯器產(chǎn)生的匯編代碼。為了理解,請閱讀針對產(chǎn)生的每行代碼的注釋:

  1. movw $956, -2(%ebp) # Value of data in stack set to 0x3BC  
  2.   
  3. movb $-85, -3(%ebp) # Value of port in stack set to 0xAB  
  4.   
  5. movb -3(%ebp), %al # movb 0xAB, %al  
  6.   
  7. movw -2(%ebp), %dx # movw 0x3BC, %dx  
  8.   
  9. #APP # Marker to note start of inline assembly  
  10.   
  11. outb %al, %dx # Write to parallel port  
  12.   
  13. #NO_APP # Marker to note end of inline assembly  

你也可以在用戶模式的程序中使用內(nèi)聯(lián)匯編。下面是用內(nèi)聯(lián)匯編編寫的一個應(yīng)用程序,調(diào)用syslog()系統(tǒng)調(diào)用以從內(nèi)核的printk()的環(huán)形緩沖區(qū)中讀取最后的128字節(jié):

  1. #define READ_COMMAND 3 /* First argument to  
  2.   
  3. syslog() system call */  
  4.   
  5. #define MSG_LENGTH 128 /* Third argument to syslog() */  
  6.   
  7. int  
  8.   
  9. main(int argc, char *argv[])  
  10.   
  11. {  
  12.   
  13. int syslog_command = READ_COMMAND;  
  14.   
  15. int bytes_to_read = MSG_LENGTH;  
  16.   
  17. int retval;  
  18.   
  19. char buffer[MSG_LENGTH]; /* Second argument to syslog() */  
  20.   
  21. asm volatile(  
  22.   
  23. "movl %1, %%ebx\n" /* READ_COMMAND */  
  24.   
  25. "movl %2, %%ecx\n" /* buffer */  
  26.   
  27. "movl %3, %%edx\n" /* bytes_to_read */  
  28.   
  29. "movl $103, %%eax\n" /* __NR_syslog */  
  30.   
  31. "int $128\n" /* Generate System Call */  
  32.   
  33. "movl %%eax, %0" /* retval */  
  34.   
  35. :"=r" (retval)  
  36.   
  37. :"m"(syslog_command),"r"(buffer),"m"(bytes_to_read)  
  38.   
  39. :"%eax","%ebx","%ecx","%edx");  
  40.   
  41. if (retval > 0) printf("%s\n", buffer);  
  42.   
  43. }  
正如在第4章“打下基礎(chǔ)”中所學(xué)到的,int $128(或者int 0x80)指令產(chǎn)生一個軟中斷,陷入系統(tǒng)調(diào)用。由于系統(tǒng)調(diào)用導(dǎo)致從用戶模式至內(nèi)核模式的轉(zhuǎn)換,故函數(shù)參數(shù)未傳入用戶或內(nèi)核堆棧中,而是在CPU寄存器中。此系統(tǒng)調(diào)用號(在include/asm-your-arch/unistd.h中有完整列表)存儲在EAX寄存器中。對于syslog()系統(tǒng)調(diào)用,調(diào)用號是103。如果查看syslog()的參考頁,將會發(fā)現(xiàn)它需要三個參數(shù):命令,存放返回數(shù)據(jù)的緩沖區(qū)的地址,以及緩沖區(qū)的長度。這些分別通過EBX、ECX和EAX來傳遞。返回值被從EAX傳遞至retval。此內(nèi)聯(lián)匯編調(diào)用被轉(zhuǎn)換為如下語句:

retval = syslog(syslog_command, buffer, bytes_to_read);

如果你編譯并運行此代碼,將會看到如下從內(nèi)核的環(huán)形緩沖區(qū)中獲取的輸出:

  1. 0:0:0:0: Attached scsi removable disk sda  
  2.   
  3. <5>sd 0:0:0:0: Attached scsi generic sg0 type 0  
  4.   
  5. <7>usb-storage: device scan complete  
  6.   
  7. ...  
arch/x86/kernel/entry_32.S中的所有內(nèi)核系統(tǒng)調(diào)用trap會保存所有的寄存器內(nèi)容至堆棧,因此 ,即使用戶空間的代碼使用CPU寄存器來傳遞參數(shù),實際上系統(tǒng)調(diào)用處理函數(shù)還是從堆棧中取其參數(shù),。為了確保系統(tǒng)調(diào)用例程預(yù)期的參數(shù)在堆棧中,都用GCC屬性asmlinkage進行標記。需要注意的是asmlinkage與asm(或__asm__)沒有任何關(guān)系,后者用于聲明內(nèi)聯(lián)匯編。

讓我們通過演示一個內(nèi)聯(lián)匯編的例子來結(jié)束本節(jié),此例子修改自基于PowerPC的電路板的Linux引導(dǎo)程序。假設(shè)此電路板上的flash存儲器不支持背景操作(BackGround Operation,BGO)。這意味此引導(dǎo)程序代碼從flash執(zhí)行時,不能寫入flash;但有時這是必須的,例如如果引導(dǎo)程序需要更新內(nèi)核映象,而此映象存放于flash的另一部分。一個解決方案是修改引導(dǎo)程序,以便用于寫入和擦除flash的引導(dǎo)代碼完全從指令cache(I-cache)中執(zhí)行,而數(shù)據(jù)段放入數(shù)據(jù)cache(D-cache)中。示例用的GCC內(nèi)聯(lián)匯編編寫的宏用于完成將必要的引導(dǎo)程序指令搬入I-cache的工作。為了理解此代碼片斷,你需要有一定的PowerPC匯編知識:

  1. /* instr_length is the number of instructions to touch  
  2.   
  3. into I-cache. _load_i$_copy and _end_i$_copy are  
  4.   
  5. program labels */  
  6.   
  7. #define load_into_icache_copy(instr_length) \  
  8.   
  9. asm volatile("lis %%r3, 0x1@h\n \  
  10.   
  11. ori %%r3, %%r3, 0x1@l\n \  
  12.   
  13. mticcr %%r3\n \  
  14.   
  15. isync\n \  
  16.   
  17. \n \  
  18.   
  19. lis %%r6, _end_i$_copy@h\n \  
  20.   
  21. ori %%r6, %%r6, _end_i$_copy@l\n \  
  22.   
  23. icbt %%r0, %%r6\n \  
  24.   
  25. lis %%r4, %0@h\n \  
  26.   
  27. ori %%r4, %%r4, %0@l\n \  
  28.   
  29. mtctr %%r4\n \  
  30.   
  31. _load_i$_copy: \  
  32.   
  33. addis %%r6, %%r6, 32@ha\n \  
  34.   
  35. addi %%r6, %%r6, 32@l\n \  
  36.   
  37. icbt %%r0, %%r6\n \  
  38.   
  39. bdnz _load_i$_copy\n \  
  40.   
  41. _end_i$_copy: \  
  42.   
  43. nop\n" \  
  44.   
  45. : \  
  46.   
  47. : "i"(instr_length) \  
  48.   
  49. :"%r6","%r4","%r0","r8","r9");  

調(diào)試

為了調(diào)試實模式內(nèi)核,不能使用我們在第21章“調(diào)試設(shè)備驅(qū)動”中所討論、使用過的調(diào)試器,如kdb或kgdb。調(diào)試內(nèi)核匯編片斷的便捷方式是將代碼轉(zhuǎn)換為Intel類型的語法后,使用DOS調(diào)試工具。但調(diào)試器是在16位時代編寫的,因此,不能調(diào)試32位的代碼,例如不能調(diào)試初始化EAX寄存器的代碼。從Internet上可以下載一些32位的免費調(diào)試器。第21章所討論的JTAG調(diào)試器是萬金油,因為這一工具可用于調(diào)試BIOS,引導(dǎo)程序,Linux實模式代碼,以及內(nèi)核與BIOS之間的交互。


    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多

    免费观看潮喷到高潮大叫| 出差被公高潮久久中文字幕| 少妇视频一区二区三区| 日韩日韩日韩日韩在线| 经典欧美熟女激情综合网| 日本男人女人干逼视频| 99久久国产精品成人观看| 高中女厕偷拍一区二区三区| 久热这里只有精品九九| 暴力性生活在线免费视频| 亚洲欧美一二区日韩高清在线| 大尺度剧情国产在线视频| 日本精品最新字幕视频播放| 国语对白刺激高潮在线视频| 国产精品久久精品国产| 精品久久av一二三区| 国产一区欧美一区二区| 日本欧美一区二区三区在线播| 国产精品尹人香蕉综合网 | 婷婷色香五月综合激激情| 午夜国产成人福利视频| 五月天丁香婷婷一区二区| 蜜臀人妻一区二区三区| 国产精品欧美一级免费| 黄片三级免费在线观看| 亚洲天堂精品在线视频| 日韩欧美一区二区不卡看片| 国产免费成人激情视频| 伊人欧美一区二区三区| 国产精品午夜小视频观看| 欧美日韩国产自拍亚洲| 国产传媒一区二区三区| 国产亚洲不卡一区二区| 国产99久久精品果冻传媒| 久久永久免费一区二区| 日本午夜福利视频免费观看| 国产美女精品午夜福利视频| 久久热在线免费视频精品| 国产成人精品视频一区二区三区| 国产福利在线播放麻豆| 国产精品不卡免费视频|