2011年2月20日 星期日

Real Mode


在此先補點Hardware的基本觀念好了,以前都只做Windows上的Application,有許多底層的硬體基礎要研究...

早期Intel在1978年所推出的16位元8086 CPU有20條位址線,所以可控制的記憶體最大為1048576Bytes = 1MB(220)。

Intel CPU list:
CPUData BusAddress BusMax Addressable Memory
8086/8816201MB
80286162416MB
80386SX162416MB
80386DX32324GB
80486SX/DX32324GB
Pentium64324GB
Pentium Pro643664GB

以下為1MB以下各區段的記憶體配置表。
Memory Map:
0xE0000 - 0xFFFFFBIOS
0xC8000 - 0xDFFFFI/O BIOS
0xC0000 - 0xC7FFFVideo BIOS
0xB8000 - 0xBFFFFVGA Color Mode Text RAM
0xB0000 - 0xB7FFFVGA Monochrome Mode Text RAM
0xA0000 - 0xAFFFFVGA Graphics Mode RAM
0x00500 - 0x9FFFFUser Program Space
0x00400 - 0x004FFBIOS Data Area
0x00000 - 0x003FFInterrupt Vector Table
※其中0xA0000至0xBFFFF為Video RAM

傳統記憶體(Conventional Memory)為640KB以下
延伸記憶體(Extended Memory)為1MB以上
UMB(Upper Memory Block)的範圍為0xA0000至0xFFFFF的位址,大小為384KB

早期CPU的Register只有16bits,所以使用兩個Register來定址,表達方式CS:IP(Code Segment:Instruction Pointer),而一個Segment的長度為64KB。
其算法為Address = Segment * 16 + Offset
舉例C800:1234的實際位址為C8000 + 1234 = C9234,只要將CS往左偏移4個bits再加上IP即可。

但由於Address Bus只有20bits,CS:IP最高卻可表達到FFFF:FFFF(10FFEF = 1114095Bytes),超過了1MB界線的話,將會進行為Memory Wrap,如0x10AAAA的位址為0xAAAA。
然而之後的CPU Address Bus都已超過20bits(如80286可定址到16MB的記憶體),若要以用額外的記憶體空間,就必須要將A20 Enable。
有些利用Memory Wrap特性的程式可能最後發生問題了,所以遇到該類型程式還是必須將A20 Disable掉。
FFFF:0010(1MB)至FFFF:FFFF這段空間就稱為HMA(High Memory Area)。

關於A20的開關,這又是一門學問了...

2011年1月14日 星期五

ME沒起來與SPI Flash無法寫入的問題

這陣子使用了某家給的ME,但卻發生ME沒有起來,且Variable無法寫入,包含用tool刷BIOS...
原來是ME設定中他們只用了某家的Flash型號,而我們是用別家的,自然而然就發生了這樣的狀況。
可以用FITC去修改ME的設定與增加所支援的SPI Flash型號。

於左邊樹狀目錄上的VSCC Table按下右鍵,選擇Add Table Entry...
輸入完Entry Name後,就可在右邊填入Flash的VID、DID與VSCC。
如不知道手邊的Flash資料可於FITC目錄底下的VSCCommn_bin Content.pdf去做查詢的動作。

2011年1月4日 星期二

ASF(Alert Standard Format)

最近剛好有碰到這東西,記一下找的相關資料,以防這記性越來越差的腦袋忘掉...

ASF(Alert Standard Format)是由DMTF所訂定的標準規範,可使具有ASF功能的電腦能夠遠端監控(Remote monitoring)、遠端管理(Remote management)及遠端控制(Remote control),可在遠端電腦不具有OS的情況下作控管的功能。
ASF需有內建網路晶片(LOM)或網路卡(NIC)的支援,還有BIOS的啟用等搭配才能夠使用其功能。

其功能有錯誤警報、監控(溫度、系統狀態等)與控制(開、關機)...等。


※DMTF(Distributed Management Task Force)為訂定、開發與維護電腦管理相關標準規範的組織。

請做好EFI的指標動態記憶體配置...

新年第一帖:
最近使用了某家的Application,但每次執行到EFI_LEGACY_BIOS_PROTOCOL內的FarCall86時必當...
結果原來是該Application中的指標記憶體配置沒做好,以致它的起始位置在1MB以下,覆蓋掉原先的資料造成錯誤。

佔版面時間:
#define EFI_LEGACY_BIOS_PROTOCOL_GUID \
{ 0xdb9a1e3d, 0x45cb, 0x4abb, 0x85, 0x3b, 0xe5, 0x38, 0x7f, 0xdb, 0x2e, 0x2d}

typedef struct _EFI_LEGACY_BIOS_PROTOCOL {
 EFI_LEGACY_BIOS_INT86 Int86;
 EFI_LEGACY_BIOS_FARCALL86 FarCall86;
 EFI_LEGACY_BIOS_CHECK_ROM CheckPciRom;
 EFI_LEGACY_BIOS_INSTALL_ROM InstallPciRom;
 EFI_LEGACY_BIOS_BOOT LegacyBoot;
 EFI_LEGACY_BIOS_UPDATE_KEYBOARD_LED_STATUS UpdateKeyboardLedStatus;
 EFI_LEGACY_BIOS_GET_BBS_INFO GetBbsInfo;
 EFI_LEGACY_BIOS_SHADOW_ALL_LEGACY_OPROMS ShadowAllLegacyOproms;
 EFI_LEGACY_BIOS_PREPARE_TO_BOOT_EFI PrepareToBootEFI;
 EFI_LEGACY_BIOS_GET_LEGACY_REGION GetLegacyRegion;
 EFI_LEGACY_BIOS_COPY_LEGACY_REGION CopyLegacyRegion;
 EFI_LEGACY_BIOS_BOOT_UNCONVENTIONAL_DEVICE BootUnconventionalDevice;
} EFI_LEGACY_BIOS_PROTOCOL;

可用此Protocol執行傳統的Legacy Code或OS。

2010年10月5日 星期二

EFI中的CR (CONTAINING RECORD)

在EFI中常常可以見到CR這個define,而這個define的作用是,當擁有某個結構成員的位址時,但卻不知整個結構的頭在哪裡的時候,就可利用這個方法來取得整個結構所在的位址,此為CR的定義:
#define _CR(Record, TYPE, Field) ((TYPE *) ((CHAR8 *) (Record) - (CHAR8 *) &(((TYPE *) 0)->Field)))
Record:為該成員的位址
TYPE:為該結構的型態
Field:為在結構中指向的該成員

這個方法可以分開兩段來看:
(CHAR8 *) (Record):該成員的實際位址
(CHAR8 *) &(((TYPE *) 0)->Field):該成員在結構中的偏移量

兩個做相減之後便可取得結構頭的位址,最後再來個(TYPE *)作強制轉換。

2010年10月4日 星期一

PCI I/O Protocols

若要對PCI Device進行讀寫的動作時,可以使用EFI_PCI_IO_PROTOCOL所提供的Function來簡化以前所要做的步驟,以下是PCI IO Protocol的結構:
typedef struct _EFI_PCI_IO_PROTOCOL {
 EFI_PCI_IO_PROTOCOL_POLL_IO_MEM PollMem;
 EFI_PCI_IO_PROTOCOL_POLL_IO_MEM PollIo;
 EFI_PCI_IO_PROTOCOL_ACCESS Mem;
 EFI_PCI_IO_PROTOCOL_ACCESS Io;
 EFI_PCI_IO_PROTOCOL_CONFIG_ACCESS Pci;
 EFI_PCI_IO_PROTOCOL_COPY_MEM CopyMem;
 EFI_PCI_IO_PROTOCOL_MAP Map;
 EFI_PCI_IO_PROTOCOL_UNMAP Unmap;
 EFI_PCI_IO_PROTOCOL_ALLOCATE_BUFFER AllocateBuffer;
 EFI_PCI_IO_PROTOCOL_FREE_BUFFER FreeBuffer;
 EFI_PCI_IO_PROTOCOL_FLUSH Flush;
 EFI_PCI_IO_PROTOCOL_GET_LOCATION GetLocation;
 EFI_PCI_IO_PROTOCOL_ATTRIBUTES Attributes;
 EFI_PCI_IO_PROTOCOL_GET_BAR_ATTRIBUTES GetBarAttributes;
 EFI_PCI_IO_PROTOCOL_SET_BAR_ATTRIBUTES SetBarAttributes;
 UINT64 RomSize;
 VOID *RomImage;
} EFI_PCI_IO_PROTOCOL;

// ++++++++++PCI Configuration Space++++++++++
首先來講如何取得PCI Configuration Space:

以上為PCI Configuration Space配置表Type 0。

先使用LocateHandleBuffer,將所有PCI Device的Handle全都找出來:
gBS->LocateHandleBuffer (ByProtocol, &gEfiPciIoProtocolGuid, NULL, &PciHandleCount, &PciHandleBuffer);

隨後在指定的Handle中將PCI IO Protocol找出來:
gBS->OpenProtocol (PciHandle, &gEfiPciIoProtocolGuid, &PciIo, ImageHandle, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);

以下可取得PCI Device的四個參數:
UINTN Seg;
UINTN Bus;
UINTN Device;
UINTN Function;
PciIo->GetLocation (PciIo, &Seg, &Bus, &Device, &Function);

若要取得整張配置表可以宣告此union型別變數:
typedef union {
 PCI_TYPE00 Device;
 PCI_TYPE01 Bridge;
} PCI_TYPE_GENERIC;
可用其Header Type來判斷它是Device或Bridge,00h與01h...等。

typedef struct {
 PCI_DEVICE_INDEPENDENT_REGION Hdr;
 PCI_DEVICE_HEADER_TYPE_REGION Device;
} PCI_TYPE00;

typedef struct {
 PCI_DEVICE_INDEPENDENT_REGION Hdr;
 PCI_BRIDGE_CONTROL_REGISTER Bridge;
} PCI_TYPE01;

PCI_DEVICE_INDEPENDENT_REGION、PCI_DEVICE_HEADER_TYPE_REGION、PCI_BRIDGE_CONTROL_REGISTER
這三個結構定義可看Spec。

宣告一個PCI_TYPE_GENERIC變數
PCI_TYPE_GENERIC Pci;
PciIo->Pci.Read (PciIo, EfiPciWidthUint32, 0, sizeof (PCI_TYPE_GENERIC) / sizeof (UINT32), &Pci);
第二個參數可以設定每次所要取得的大小,詳情可看其定義。
第三個參數為起始位址,由於要抓整張表所以便填入0。
第四個參數為總共要抓取的次數。
成功後可一一取值,如:
Pci.Device.Hdr.VendorId;
Pci.Device.Device.SubsystemVendorID;

若要取得配置表的其中一個值,可先算出所在位址,如Status位址在06h而大小是16bits,先宣告一個UINT16的變數Status,代入Pci.Read()。
PciIo->Pci.Read (PciIo, EfiPciWidthUint16, 0, 1, &Status);

如果要取得ClassCode的話,而它是3個8bits整數,所以宣告一個UINT8 ClassCode[3]陣列,再呼叫Pci.Read()。
PciIo->Pci.Read (PciIo, EfiPciWidthUint8, 0, 3, ClassCode);

以下可印出整張表的內容:
UINT8 Content;
for (i = 0x00; i <= 0x0F; ++i) {  for (j = 0x00; j <= 0x0F; ++j) {   Status = PciIo->Pci.Read (PciIo, EfiPciIoWidthUint8, ((i * 0x10) + j), sizeof (Content), &Content);
  PrintAt (j * 3, i, L"%02x", Content);
 }
}

// ++++++++++PCI Base Address Register++++++++++
接下來是Base Address Register的部分,一般PCI Device共有六個BAR,而PCI/PCI Bridge只有兩個BAR,可看Spec。
而BAR又分了IO Space與Memory Space兩種方式,可看其第一個bit決定,0是Memory Space而1是IO Space,也就是基偶之分,EFI也提供了一個結構儲存該BAR的屬性。
typedef struct {
 UINT8 Desc;
 UINT16 Len;
 UINT8 ResType;
 UINT8 GenFlag;
 UINT8 SpecificFlag;
 UINT64 AddrSpaceGranularity;
 UINT64 AddrRangeMin;
 UINT64 AddrRangeMax;
 UINT64 AddrTranslationOffset;
 UINT64 AddrLen;
} EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR;

此時宣告其指標:
EFI_ACPI_ADDRESS_SPACE_DESCRIPTOR *Resources;
PciIo->GetBarAttributes (PciIo, PciBarIndex, NULL, &Resources);
注意用完Resources後要釋放掉。

Resources->ResType也可判斷BAR的屬性(IO/Memory)。
if (Resources->ResType == 0) {
 PciIo->Mem.Read(PciIo, EfiPciIoWidthUint8, PciBarIndex,0, sizeof (Content), &Content);
} else {
 PciIo->Io.Read(PciIo, EfiPciIoWidthUint8, PciBarIndex, 0, sizeof (Content), &Content);
}
BarIndex按自己所需的BAR代入0~5(P2P Bridge: 0~1)的整數。
至於寫入也是相同方式。

2010年10月2日 星期六

Variable Service

EFI BIOS使用Variable來儲存各個配置設定,通常存放於NVRAM或CMOS中,這部分就看IBV如何訂定,在DXE階段後可使用Runtime Services所提供的三個服務來存取Variable,基本上一個Variable是由Name、GUID與Data所組成,但其在記憶體中並沒有一定的順序性,所以要找到一個Data就得靠Name和GUID所提供的Index來取得,先介紹這三個Services。

GetNextVariableName(
IN OUT UINTN *VariableNameSize,
IN OUT CHAR16 *VariableName,
IN OUT EFI_GUID *VendorGuid
);
可以透過傳進去的Name與GUID來得到它下一個Name與GUID,並傳出Name的實際Size,如果您不知道任何的Name與GUID的話,此時可填入空的Name與GUID,便會將第一組傳回。

GetVariable(
 IN CHAR16 *VariableName,
 IN EFI_GUID *VendorGuid,
 OUT UINT32 *Attributes OPTIONAL,
 IN OUT UINTN *DataSize,
 OUT VOID *Data
);
可傳回對應Name與GUID的Data,要傳出的Data要先為它配置好一個空間(動態或靜態皆可),如果不知道Data的Size,此時可先傳入一個非0的Size,它會將實際的Size傳出,並傳回EFI_BUFFER_TOO_SMALL的錯誤訊息,接下來再動態配置空間即可。

SetVariable(
 IN CHAR16 *VariableName,
 IN EFI_GUID *VendorGuid,
 IN UINT32 Attributes,
 IN UINTN DataSize,
 IN VOID *Data
);
將傳入的Data寫入至對應的Name與GUID。

Attributes:
#define EFI_VARIABLE_NON_VOLATILE 0x00000001
#define EFI_VARIABLE_BOOTSERVICE_ACCESS 0x00000002
#define EFI_VARIABLE_RUNTIME_ACCESS 0x00000004
#define EFI_VARIABLE_HARDWARE_ERROR_RECORD 0x00000008
#define EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS 0x00000010