2011年11月18日 星期五

MTRR


MTRR (Memory type range registers) 是CPU內的一組MSR (Model-specific registers) ,它作用為告訴CPU要如何最佳的存取各個記憶體區段,簡單來說是CPU要以哪種快取模式來進行存取。

通常MTRR的初始化是BIOS的任務。

在UEFI中可以使用EfiReadMsr, EfiWriteMsr, MsrRead64, MsrWrite64...等Function來存取,只要拉相對應的Header與Library就好。


IA32_MTRRCAP, Index: 0xFE, Read-Only.
  • VCNT (variable range registers count) bits 0 ~ 7 - 指出這個CPU支援幾組動態的MTRR,MTRR也是有數量的,也有機會被塞爆。
  • FIX (fixed range registers supported) flag, bit 8 - 指出是否支援Fixed range MTRRs (IA32_MTRR_FIX64K_00000 through IA32_MTRR_FIX4K_0F8000)。
  • WC (write combining) flag, bit 10 - 是否支援write-combining (WC) memory。
  • SMRR (System-Management Range Register) flag, bit 11 - 是否支援SMRR。


IA32_MTRR_DEF_TYPE, Index: 0x2FF.
  • Type field, bits 0 ~ 7 - 指出未涵蓋在MTRR所定義的記憶體該以何種Cache為預設值。
  • FE (fixed MTRRs enabled) flag, bit 10 - Enable或Disable Fixed-range MTRR。
  • E (MTRRs enabled) flag, bit 11 - Enable或Disable動態的MTRR。


Variable - Range Register Pair
一組MTRR有兩個Registers,Base與Mask,起始索引可參考EDK的定義:
#define EFI_CACHE_VARIABLE_MTRR_BASE 0x200

  • Type field, bits 0 ~ 7 - 此MTRR所定義的範圍該以何種Cache方式存取。
  • Valid field, bits 11 - 指出此組MTRR是否有效。
  • PhysBase field, bits 12 ~ (MAXPHYADDR-1) - 取12 ~ (MAXPHYADDR-1) bits,補上3個0,就是組MTRR的Base Address。
  • PhysMask field, bits 12 ~ (MAXPHYADDR-1) - 一樣只取12 ~ (MAXPHYADDR-1) bits,補上3個0,再做1的補數運算,即可算出此MTRR的長度,值得注意的是長度需為2的次方。
MAXPHYADD代表是CPU的定址能力,長度依CPU而定。

下表為各種存取方式對應到MTRR的Type:
Memory Type and MnemonicEncoding in MTRR
Uncacheable (UC)00h
Write Combining (WC)01h
Reserved*02h
Reserved*03h
Write-through (WT)04h
Write-protected (WP)05h
Writeback (WB)06h
Reserved*07h ~ FFh


範例:
MTRRIndexValueType/SizeRange
Base20000000006WriteBack
Mask201F800008002048MB0 ~ 7FFFFFFF
Base20280000006WriteBack
Mask203FC00008001024MB80000000 ~ BFFFFFFF
略...
Base206D8000000Uncacheable
Mask207FF8000800128MBD8000000 ~ DFFFFFFF
Base208D7000000Uncacheable
Mask209FFF00080016MBD7000000 ~ D7FFFFFF
略...

對於MTRR所需要注意的Type有沒有設定為正確的Type,還有MTRR有沒有被塞爆,可能導致某些要使用Cache的Memory區域會因沒有宣告Cacheable,造成效能低落...等。

2011年11月17日 星期四

Memory Map

上圖擷取自Intel Spec.
以下以Intel Platfrom為例。

左圖是實體記憶體大於4 GB以上,有Remap機制。
右圖是記憶體小於4 GB,無Remap機制發生。

每張圖都有提供兩種觀點,System View與Memory Controller View。
System View為整個系統可以存取的記憶體範圍。
Memory Controller View為實際上Physical Memory的整體分布。

通常TOLUD Base ~ 4 GB這段範圍都被PCI Resource Mapping,所以這塊記憶體位置就被占據了。
因此後來發展了Remap機制,把Reclaim Base ~ Reclaim Limit這段範圍的記憶體將它Mapping至實體記憶體4 GB以下OS invisible Reclaim的位置,以便回收利用那些被PCI Resource所佔據的記憶體。
※TOLUD: Top of Lower Usable DRAM
※TOUUD: Top of Upper Usable DRAM

以下為Reclaim的Pseudo-code:
IF (ADDRESS_IN[38:20] ≥ REMAP_BASE[35:20]) AND
(ADDRESS_IN[38:20] ≤ REMAP_LIMIT[35:20]) THEN

ADDRESS_OUT[38:20] = (ADDRESS_IN[38:20] – REMAP_BASE[35:20]) +
0000000b & TOLUD[31:20]

ADDRESS_OUT[19:0] = ADDRESS_IN[19:0]

ME Base = TOM(Top of Memory,實體記憶體總量)- ME UMA Size。
Reclaim Base = MAX (4GB, ME Base) 看何者為大。
Reclaim Limit = Remap Base + (PCI Resource Total Size - 1);
TOUUD Base = Reclaim Limit + 1。
而TOLUD Base在哪裡則是要看PCI Resource整體分配了多少的空間而定,還有Physical Memory Size而定。

由於Intel Graphics本身沒有獨立的記憶體,所以它使用了Physical Memory當成自己的記憶體,所以這部分並沒有辦法Reclaim回來,可見GFX Stolen與GFX GTT Stolen這兩塊,而T SEG也佔據下面那一塊。

OS存取Reclaim Base ~ Reclaim Limit這段範圍時,它其實是將資料儲存在Physical Memory中OS invisible Reclaim這個地方。
所以支援4 GB以上Memory的OS就可以使用被PCI Resource占據的Memory。
這也是預設的情況下,Windows 32-bit的OS常常只能用到3 GB甚至以下的記憶體。

2011年10月31日 星期一

PCI Configuration Space


可以透過I/O方式或Memory方式來存取PCI Configuration Space,但在此之前要先算出該Device的PFA (PCI Function Address)。

I/O可透過I/O Port的0xCF8來指定所要存取的PCI Device的Address,然後再透過0xCFC將Data讀出。
其公式為:
Address = 0x80000000 + (Bus << 16) + (Device << 11) + (Function << 8) + Index
其中0x80000000稱之為Enable bit。

I/O Mapped I/O

Memory存取起來則比較方便,但缺點要先取得PCI的Base Address。
其公式為:
Address = Base Address + (Bus << 20) + (Device << 15) + (Function << 12) + Index
Memory Mapped I/O

各個參數的長度:
Bus: 0 ~ 255, Device: 0 ~ 31, Function: 0 ~ 7
可以注意到兩者的Index長度是不同的,透過I/O的話只能存取0 ~ 255的範圍,Memory則可到0 ~ 4095。

在這介紹一下PCI Configuration Registers。
PCI Device與PCI Bridge的前16個Bytes的格式是相同的。

PCI Device Register (Type 0):
0E: Header Type
可藉由bit 0 ~ 7這三個定義來判斷是甚麼Device:
#define HEADER_TYPE_DEVICE 0x00
#define HEADER_TYPE_PCI_TO_PCI_BRIDGE 0x01
#define HEADER_TYPE_CARDBUS_BRIDGE 0x02
且可由第8個Bit判斷該Device是不是Multi Function:
#define HEADER_TYPE_MULTI_FUNCTION 0x80
#define IS_PCI_MULTI_FUNC(_p) ((_p)->Hdr.HeaderType & HEADER_TYPE_MULTI_FUNCTION)

10 ~ 27: Base Address Register
上圖為BAR的Layout,長度為32 bits。
Bit 0: 判斷BAR為Memory還是I/O。

Memory:
Bits 0 ~ 3為ReadOnly。
Bits 1 ~ 2: Address Space的長度,00 - 32 bits、01, 11 - Reserved、10 - 64 bits。
Bit 3: 指出是否為Prefetchable,是的話該Device的Bridge Prefetchable Memory將會指定一段範圍。
Bits 4 ~ 31: Base Address。

I/O:
Bits 0: Reserved。
Bits 2 ~ 31: Base Address。

對BAR填入0xFFFFFFFF會得出一組數值,對此數值做1的補數即可得出這個BAR的長度。
BIOS在Enumerate PCI Device時候,便是以此來取得系統所要分配的PCI Resource。

2C ~ 2F: Subsystem Vendor ID and Subsystem ID

PCI Bridge Register (Type 1):
18 ~ 1A: Primary Bus, Secondary Bus and Subordinate Bus
Primary Bus: 此Bridge的前一個Bus Number是多少。
Secondary Bus: 此Bridge的下一個Bus Number是多少。
Subordinate Bus: 此Bridge底下的最後一個Bus Number是多少。

1C ~ 1D: I/O Base and Limit
指此Bridge所涵蓋的I/O Resource是多少,1C為Base、1D為Limit,一般每個Byte只要看前4個bits就好,I/O Limit會自動補上0xFFF,也就是說I/O Resource至少是以0x1000為一個單位。
如:0x4040的範圍為0x4000 ~ 0x4FFF、0x6050為0x5000 ~ 0x6FFF。

20 ~ 23: Memory Base and Limit
指此Bridge所涵蓋的Memory Resource是多少,20 ~ 21為Base、22 ~ 23為Limit,也只需要看前12個bits就好,Limit會自動補上0xFFFFF,也就是說Memory Resource至少是以0x100000(1MB)為一個單位。
如:Base: 0x5A00, Limit: 0x5AF0範圍是0x5A000000 ~ 0x5AFFFFFF

24 ~ 27: Prefetchable Memory Base and Limit
Prefetchable Memory的範圍。

PCI Device Enumeration:
for (Bus = 0; Bus <= PCI_MAX_BUS; ++Bus) {
for (Device = 0; Device <= PCI_MAX_DEVICE; ++Device) {
for (Func = 0; Func <= PCI_MAX_FUNC; ++Func) {

PciAddress = PCI_ACCRESS_ENABLE_BIT | PciCfgAddr (Bus, Device, Func);

//
// Get Vendor ID and Device ID
//
IoOutput32 (PCI_ADDRESS_INDEX, PciAddress);
VendorId = IoInput16 (PCI_DATA_INDEX);
DeviceId = IoInput16 (PCI_DATA_INDEX + PCI_DEVICE_ID_OFFSET);

if (VendorId == 0xFFFF || DeviceId == 0xFFFF) {
continue;
}

//
// Get HeaderType
//
IoOutput32 (PCI_ADDRESS_INDEX, PciAddress | 0x0C);
HeaderType = IoInput8 (PCI_DATA_INDEX + 0x02);

if (Func == 0 && !(HeaderType & HEADER_TYPE_MULTI_FUNCTION)) {
break;
}
}
}
}

2011年10月28日 星期五

UEFI Detect Utility - 2012


從踏入UEFI領域後,便陸續的開發這個Tool,目前先發布此版本。
部分功能只針對Phoenix SCT2與Intel HR、CR做開發。
目前提供的主要功能如下:
F1 - PCI
F2 - Variable
F3 - Handle
F4 - Memory, Memory Map
F5 - IO Space (預設的Address只針對Intel HR、CR)
F6 - IO Port
F7 - Flash (只支援Phoenix SCT2)
F8 - SPD (只支援Intel HR、CR)
F9 - MTRR (尚未在AMD Platform上測試...)

其他附屬功能:
  1. 儲存資料:Ctrl + S
  2. 比較數值:Ctrl + C
  3. 截取畫面:Ctrl + P
  4. 更新速度:+, -

About尚未加入...
如有Bug請歡迎提出!
原先以EDK1做開發,預計之後將移植到EDK2重新開發,真是個浩大工程...

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。