bloggerads

2014年10月25日 星期六

C/C++一些經驗談

一些雜談,提供個人Coding 的經驗

1. 無窮迴圈的例子: 沒有注意宣告的變數範圍上限或邏輯錯誤而溢位產生無窮迴圈
  1. for (unsigned char x=0; x<=0xff; x++) ;
  2. for (unsigned char x=0xff; x>=0; x--) ;
2. 宣告雙指標的時機
用指標來管理記憶體,而雙指標(指標的指標)就是用指標管理指標
實務上在函式間傳遞指標的位址時, 就必需要傳遞指標的指標。舉個記憶體管理的例子,假如我有一串link list如下p1~p6,就可以透過雙指標pp start/pp tail指定鏈結串鏈其中一段的起始節點位址(&p2)和尾部節點位址的(&p5), 而PP start/ PP tail就是雙指標的型態

2014年9月26日 星期五

C 結構(structure) 的一些注意事項 (C89 / C99 / 對齊,Alignment)

1. 結構變數初值給定

首先,先宣告一個結構型態, 接下來再介紹兩種初值給定方法(C99/C89):
+  struct test
+  {
+    const char* cap;
+    unsigned char data_0;
+    int data_1;  
+  };

support C99 mode (現在新版本的GCC都有support C99 mode 的結構變數初值給定方法。 這種方法的優點是未來如果結構調整變數的位置,宣告變數初值不用跟著調整):

+  struct test node = 
+  {
+      .cap="first", 
+      .data_0=10,
+      .data_1=5
+  };

以下是傳統(C89 mode)的寫法 (很可惜的是目前Watcom C版本(1.9)尚未支援C99 mode結構變數的初值給定方法),所以還是用舊有的寫法:

+  struct test node = 
+ {
+      "first",   // cap
+      10,       // data_0
+      5          // data_1
+  };

2. 結構內變數對齊(alignment)的問題

2014年7月25日 星期五

在DOS下寫程式常會用到的中斷 (to DOS愛好者)

現在還在用Dos的人其實不少只是比較不顯眼 :P ,就我知道幾乎所有的x86系統在開發過程中,還是以Dos tool來驗證chipset問題。(畢竟誰會這麼閒重新在UEFI shell上開發以前在Dos下沒問題的程式XD)

我也是Dos的愛用者,開發過的Dos測試程式比在其他作業系統上的測試程式多得多,寫程式的功力也是從這邊開始練起。

基本上使用Watcom若是default使用protect mode, 那麼寫Dos程式幾乎跟在windows上感覺差不多,但是Watcom可以透過inline assembly神不知鬼不覺地使用原始 real mode 的中斷。

由於中斷很多,這篇僅列出我常使用的中斷給大家參考:

1. Access PCI configuration: INT 1A

提供範例函數來讀取PCI cfg data:
+
+  //Author: Martin Lee
+   const unsigned char PCI_FUNCTION_ID= 0xB1;
+   const unsigned char READ_CONFIG_DWORD 0x0A
+   
+   DWORD READ_PCI_CONFIG_DWORD(WORD BDF, WORD index)
+   {
+   
+      DWORD data;
+       __asm
+       {
+       // 1AB109 INT 1A - PCI BIOS v2.0c+ - READ CONFIGURATION WORD
+       // BH = bus number
+       // BL = device/function number (bits 7-3 device, bits 2-0 function)
+       // ECX = dword read
+       mov     BX,  BDF
+       mov     di,  index
+       mov     ah,  PCI_FUNCTION_ID
+       mov     al,  READ_CONFIG_DWORD
+       int     1ah
+       mov     data, ecx
+       }
+       return data;
+   }

2. 對碟機做讀寫: INT 13

3. 設定螢幕的顏色游標什麼的: INT 10 (Watcom有很多內建的函數可以使用,使用上方便又美觀), 像我這個介面就完全是呼叫Watcom 的library做的

(待續)

2014年6月27日 星期五

ATA overview

想要寫一支Driver來控制硬碟發送ATA command,必須了解三份Spec
  1. ATA (AT Attachment): 這是一份軟體的spec,定義了command的種類
  2. AHCI or IDE : 這是controller的spec, 說明如何控制controller發出ATA command給碟機
  3. SATA :定義HW(phy)的行為,如power management及一些encoding/decoding, Link規則...等等 
這邊列舉一些常見的ATA command, 並以我開發的tool, 下圖,來介紹一些常見的command。



  • 25h: Read DMA ext: 傳統的Read
  • 35h: Write DMA ext: 傳統的Write
  • 60h: Read FDMA Queued :這就是 NCQ read
  • 61h: Write FDMA Queued :NCQ write
  • ECh: Identify : 這個command帶有512Byte的table,用來提供host查詢碟機的能力
  • E0h: Standby Immediately: 用於斷碟機電前, host發此命令告訴碟機
  • EFh: Set feature: host可以透過這個command來開啟碟機一些能力,如Partial/Slumber...
  • 06h: Data Set Management :這就是Trim command,用來告訴碟機哪些LBA是無效的資料請碟機刪掉。這個command對SSD尤其重要,若沒有這個command, SSD firmware 裡的GC(Garbage Collection)將不會回收一些無效的LBA而導致硬碟用久後效能低落
  • 92h: Download Microcode: 碟機廠家用此command來update 碟機內部的firmware


2014年6月12日 星期四

UEFI : Boot Service: HandleProtocol () / OpenProtocol (), LocateHandle() / LocateHandleBuffer()


     在寫UEFI code的時候,常常需要找到某個特定的handle, 然後打開這個handle底下特定的Protocol。如找到某個Pci Controller Handle, 然後打開他底下的gEfiPciIoProtocolGuid, 之後才能順利access此controller的Pci Space/Mem Space, 使用的方法不外乎是呼叫Boot Service所提供的HandleProtocol () / OpenProtocol (), LocateHandle() / LocateHandleBuffer()。

這幾個function說明如下:

■ HandleProtocol():

Queries a handle to determine if it supports a specified protocol.
The HandleProtocol() function is still available for use by old EFI applications and drivers. However, all new applications and drivers should use OpenProtocol() in place of HandleProtocol().

注意spec有說明HandleProtocol ()這個function應該要由OpenProtocol()來取代。因為它其實是長這樣的

EFI_STATUS
HandleProtocol (
IN EFI_HANDLE Handle,
IN EFI_GUID *Protocol,
OUT VOID **Interface
)
{
return OpenProtocol (
          Handle,
          Protocol,
          Interface,
          EfiCoreImageHandle,
          NULL,
          EFI_OPEN_PROTOCOL_BY_HANDLE_PROTOCOL
          );
}

■ OpenProtocol()

Queries a handle to determine if it supports a specified protocol. If the protocol is supported by the handle, it opens the protocol on behalf of the calling agent. This is an extended version of the EFI boot service HandleProtocol().

■ LocateHandle() / ■ LocateHandleBuffer() <== 這個比較常用!!

先分別看兩個的原型

typedef
EFI_STATUS
(EFIAPI *EFI_LOCATE_HANDLE_BUFFER) (
          IN EFI_LOCATE_SEARCH_TYPE SearchType,
          IN EFI_GUID * Protocol OPTIONAL,
          IN VOID *SearchKey OPTIONAL,
          IN OUT UINTN *NumberHandles,
          OUT EFI_HANDLE **Buffer
);
typedef
EFI_STATUS
(EFIAPI *EFI_LOCATE_HANDLE) (
          IN EFI_LOCATE_SEARCH_TYPE SearchType,
          IN EFI_GUID * Protocol OPTIONAL,
          IN VOID *SearchKey OPTIONAL,
          IN OUT UINTN *BufferSize, // On input, the size in bytes of Buffer. On output, the size in bytes of the array returned in Buffer (if the buffer was large enough) or the size, in bytes, of the buffer needed to obtain the array (if the buffer was not large enough).
          OUT EFI_HANDLE * Buffer // The buffer in which the array is returned
);

Description

The LocateHandle() function returns an array of handles that match the SearchType request. If the input value of BufferSize is too small, the function returns EFI_BUFFER_TOO_SMALL and updates BufferSize to the size of the buffer needed to obtain the array.

兩者差別在LocateHandle()需要自己”分配allocate”跟釋放Buffer。而LocateHandleBuffer() 使用者僅需釋放Buffer,allocate buffer 由此函式做掉

2014年6月9日 星期一

Demonstrate how macro CR works

在UEFI的code裡看到很多這樣的用法, 所以追根究底把它弄懂。用法是: 如果傳進一個函數裡面是某個結構成員的位址,可以用這個巨集回推出主結構的起始位址。 

+    /*    
+    原始定義: 
+    #define _CR(Record, TYPE, Field) 
+          ((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field)))
+      
+    用途: 由結構成員的位址求得整個結構的起始位址
+    
+    Record:該成員的位址
+    TYPE:該成員所屬結構的型態
+    Field:該成員
+    (CHAR8 *) (Record):該TYPE結構成員的實際位址
+    (CHAR8 *) &( ( (TYPE *) 0)->Field):該成員在結構中的相對偏移量
+    
+    兩個做相減之後便可取得結構頭的位址,最後以(TYPE *)作強制轉換。
+    
+    */
+
+    #define _CR(Record, TYPE, Field)   
+      ((TYPE *) ((char *) (Record) - (char *) &( ((TYPE *)0)->Field)) )
+    
+    typedef struct _MyStru {
+            char a;
+            char b;
+            char c;
+    } MY_TYPE;
+    
+    void main()
+    {
+      MY_TYPE *pd;
+        
+      printf("Find pd address by CR: %X \n", _CR(&(pd->c), MY_TYPE, c) );
+      printf("pd address: %X \n", pd );  
+    }

2014年5月29日 星期四

makefile example in windows

path
|
+--scr
|   +--a.txt
|   +--b.txt
|
+--dest


@IF EXIST "src" (
@ECHO src exist
)

REM set current path to variable
@set current=%cd%
@cd %current%/src

REM Don't show command in screen by add @
@

REM Show something
@ECHO Show Something..

REM Copy a a.txt from src folder to dest folder
cp src/a.txt dest

REM Copy all files from src folder to dest
cp -r src/* dest

REM Jum To Tag
@GOTO EXIT
:EXIT

REM create a new folder
mkdir NewFolder