mips 存儲管理

一.虛擬地址空間

地址空間的最大長度與實際可用的物理內存數量無關,因此被稱爲虛擬地址空間(Virtual Address Space)。這個虛擬地址空間的大小是由計算機的硬件平臺決定的,具體來說是由CPU的位數決定的。硬件決定了地址空間的最大理論上限,即硬件的尋址空間大小,比如主流的32位處理器(IA32,MIPS,ARM)等能尋址2^32B,即4GB的大小的地址空間(0~0xFFFF FFFF)。

Linux將虛擬地址空間劃分爲兩個部分:內核空間用戶空間。系統中每個用戶進程都有自己的虛擬地址範圍,從0到TASK_SIZE。通常取TASK_SIZE=0xC000 0000,即4GB的底部3GB(0x0000 0000 ~ 0xBFFF FFFF)留給用戶空間的進程使用的,頂部1GB(0xC000 0000 ~ 0xFFFF FFFF)留給內核使用。對於Windows操作系統來說,它的進程虛擬地址空間劃分是操作系統佔用2GB,用戶進程只剩下2GB。
二.MMU

那麼虛擬內存的底層內存管理機制是怎樣的呢?當我們自底向上思考時,關注的主要硬件爲TLB(Translation Lookaside Buffer,翻譯後備緩衝)。TLB爲虛擬地址(VA)到物理地址(PA) 轉換的硬件機構,它是虛擬存儲的硬件基礎。當我們自頂向下思考時,該硬件通常稱爲MMUMemory Management Unit,內存管理單元)。

虛擬存儲的實現需要依靠硬件的支持,對於不同的CPU來說是不同的。但是幾乎所有的硬件都採用一個叫MMU的部件來進行頁映射,如圖1-7所示。


在頁映射模式下,CPU發出的是Virtual Address,即我們的程序看到的是虛擬地址。經過MMU轉換以後就變成了Physical Address。具體來說,TLB表中的每個表項有一個頁的虛擬地址(VPN代表虛擬頁號)和一個物理地址(PFN代表物理幀號)。當一個程序發出一個虛擬地址後,它與TLB中的每個VPN進行比較,如果它匹配了一個表項,對應的PFN就發出。

一般MMU都集成到了CPU內部了,不會以獨立的部件存在。

三.MIPS基本地址空間

在MIPS CPU裏,你的程序中使用的地址絕對不會和芯片裏的物理地址一樣(有可能會很接近,但不會相同)。我們分別稱爲:程序地址物理地址(Physical Address)。這裏所講的程序地址的含義同虛擬地址(Virtual Address)完全相同,不過不會牽扯到操作系統內存管理(進程)語境下的複雜性。

MIPS CPU可以運行在兩種特權級別上:用戶態核心態(R4000之後的MIPS CPU有第三種監管者模式)。我們經常提到用戶模式核心模式,需要明確的是用戶程序的運行處於操作系統的監管之下,進程的虛擬空間都在操作系統的掌握之中。從核心態切換到用戶態,MIPS CPU做的工作並沒有不同,只是有時是非法的。在用戶態,任何一個程序地址的首位是非法的,就會引起陷阱異常。另外,在用戶態下,一些指令也會引起陷阱。

如果2.1所示,在32位視圖中,程序地址空間劃分爲4個大區域,分別用一個傳統的名字(完全沒有具體含義)命名。地址處在不同的區域,會有不同的屬性,如下所示:

1.kuseg: 0x000 0000 - 0x7FFF FFFF (低端2G)

這些是用戶模式下可用的地址,即MIPS規範約定用戶空間爲2G。在帶有MMU的機器裏,這些地址都將由MMU轉換。除非已經設置好MMU,否則不要使用這2G 地址。

對於沒有MMU 的機器,對這2G 地址的操作由具體實現所決定。CPU使用手冊會告訴你關於這方面的信息。如果你希望編寫的代碼具有兼容性,可以在缺少MMU 的MIPS處理器之間移植,儘量避免使用這塊區域。

2.kseg0: 0x8000 0000 - 0x9FFF FFFF(512M)

只需要把最高位清零(&0x7fffffff),這些地址就被轉換爲物理地址,然後把它們連續地映射到物理內存的低端512M(0x0000 0000 - 0x1FFF FFFF)空間。因爲這種映射是很簡單的,不需要MMU轉換,通常把這些地址稱爲“非翻譯無需轉換的”(Unmapped)地址區域。

對這段地址的存取都會通過高速緩存(cached)。因此在緩存(cache)未進行初始化之前,不要使用這段地址。通常在沒有MMU的系統中,這段空間用於存放大多數程序和數據。對於有MMU 的系統,操作系統的內核會存放在這個區域。

3.kseg1: 0xA000 0000 - 0xBFFF FFFF(512M)

通過將最高3位清零(&0x1fffffff)的方法來把這些地址映射爲相應的物理地址,與kseg0 映射的物理地址一樣,都映射到物理內存的低端512M(0x0000 0000 - 0x1FFF FFFF)空間,也是“非翻譯無需轉換的”(Unmapped)地址區域。但要注意,kseg1不使用緩存(Uncached)。

kseg1是唯一的在系統重啓時能正常工作的內存映射地址空間,這也是爲什麼重新啓動時的入口向量是(0xBFC0 0000)會在這個區域。這個向量對應的物理地址是0x1FC0 0000。

因此你可以使用這段地址空間來訪問你的初始化程序的ROM。還有大多數人把它用來訪問I/O 寄存器。如果你的硬件工程師要把這段地址映射到非低端512M 空間,你應該試圖說服他們。

4.kseg2: 0xC000 0000 - 0xFFFF FFFF (1G)

這段地址空間只能在核心態下使用並且要經過MMU轉換。在MMU 設置好之前,不能存取這段區域。除非你在寫一個真正的操作系統,一般來說你不需要使用這段地址空間。

有時,你會看到這段地址空間被分成兩等分,並稱之爲kseg2和kseg3,要着重指出的是其中的低半部分(kseg2)對於運行在監管者模式可用

由於kseg0和kseg1用於操作系統分配外設I/O地址,加上kseg2的1G空間,故MIPS規範約定內核空間爲2G。

四.簡單系統尋址

MIPS的程序地址很少與真正的物理地址一致。但對於簡單的嵌入式軟件而言,在使用kseg0和kseg1這兩段區域內的地址時,其程序地址和物理地址之間的關係是很密切的。

大多數的簡單系統會對512MB以下的空間完全映射,對於MIPS 平臺上的物理地址空間(>=32位),工業界長期形成的慣例是將0x0000 0000 ~ 0x1000 0000給內存(512M的前256M映射到kseg0),而將0x1000 0000 ~ 0x1FFF FFFF留給I/O(512M的後256M映射到kseg1,用作I/O)。可參考AR9331 Datasheet中的2.3 AR9331 Address MAP實例。從物理地址0x2000 0000(512MB)以上的地址空間不能被映射到任何地址,但是在需要時,可以通過在MMU內設置轉換入口(TLB)的方式,或者使用64位CPU中的一些額外空間對較高的地址進行訪問。

在覈心特權下(CPU啓動時),CPU可以做任何事情。在用戶模式下,程序地址超過2GB(首位設置)之上是非法的,將會產生一個陷阱。如果一個CPU具有MMU,這意味着所有的用戶地址在到物理內存之前必須經過MMU的轉換,從而使得OS可以防止用戶程序隨便亂用空間。當然,這也意味着,對於一個MIPS CPU,其運行的OS如果沒有使用內存映射,用戶態優先級其實是多餘

當系統內存大於256MB 時,kseg0的空間不夠用,則需要啓用High Memory機制,使用kseg2的空間,通過TLB建立映射去訪問。對64 位情形,則不存在這個問題,因爲64位Linux使用xkphys區域訪問物理內存,cached和uncached 區域與整個物理地址空間一樣大,足夠用矣。

五. MIPS地址空間若干問題

(1)ROM/Flash啓動地址

MIPS 下TLB、Cache都要OS參與管理,在其啓動時OS尚未接管系統,這個時候不採用TLB、Cache機制是很重要的。在系統加電後,MIPS CPU將首先執行Boot Loader程序,第一條指令就映射在地址空間的0xBFC0 0000處。MIPS24K 起始地址改到了0xBF000000,從而使ROM/Flash空間從4MB提高到16MB。這個入口向量即ROM/Flash起始(物理)地址0x1FC00000在kseg1地址段的映射。

AR9331 Datasheet(The AR9331 integrates an embedded MIPS 24Kc processor)中的2.2 Configuration中提到:

==============================================================

Upon reset, the CPU puts out an address of 0xBFC00000 which is mapped to the flash address space or internal ROM code,using an external pull up/down register to choose if the AR9331 will boot from the Flash or internal ROM.

==============================================================

(2)I/O設備寄存器

MIPS處理器一般把外設映射到虛擬地址0xA0000000-0xBFFFFFFF之間非緩存kseg1所對應的512MB地址空間中。

I/O設備寄存器:似乎很顯然,但還會值得再指出:MIPS沒有專門的輸入輸出指令,所有設備寄存器都映射到某個地址空間,如果你將該空間設置爲緩存的,會發生奇怪的事情。

==============================================================

I/O device registers: Perhaps obvious, but worth pointing out. MIPS has no dedicated input/output instructions, so all device registers must be mapped somewhere in the address space, and very strange things will happen if you accidentally let them be cached.

==============================================================

AR9331 Datasheet中的General Purpose I/O Output Enable(GPIO_OE)寄存器物理地址爲0x18040000,則程序在讀寫GPIO_OE寄存器時,需要先調用宏KSEG1ADDR將物理地址轉換到非緩存的kseg1地址段。

由於設備寄存器都映射到某個地址空間(ROM、RAM、I/O統一編址), 對於完全運行在覈心模式下(未開啓MMU)的簡單嵌入式系統(例如實時VxWorks操作系統), 我們甚至可以增加串口命令mm(memory modify),然後通過mm命令修改某些I/O寄存器的值來實現即時調試。

(3)DMA描述符數組(環)

DMA描述符數組:·複雜的DMA控制器使用內存中小的數據描述符和CPU共享設備的控制/狀態信息。通常CPU創建一系列待傳輸的信息,並告訴DMA控制器開始工作。如果你的系統使用描述符,需要將它們存放在非緩存kseg1區域。

==============================================================

DMA descriptor arrays: Sophisticated DMA controllers share control/status information with the CPU using small descriptor data structures held in memory. Typically, the CPU uses these to create a long list of information to be transferred, and only then tells the DMA controller to begin its work. If your system uses descriptors,you’ll want to access the memory region that contains them through an uncached address region.

==============================================================

DMA描述符數組(DMA Descriptor Array)本身是一個指針數組(形如int32* hw_desc[DESC_NUM]),hw_desc[i]指向由硬件定義的描述符(h/w descriptor)。hw_desc[]地址需要配置到SoC芯片相應的寄存器中,例如AR9331 Datasheet中的DMARXDESCR(Pointer to RxDescrpitor)和DMATXDESCR_Q0(Descriptor Address for Queue 0 Tx)。很顯然,這個數組需要分配或轉換到非緩存kseg1區域

(4)主內存和外設都只能映射在512MB的地址空間中?

“主內存和外設都只能映射在512MB的地址空間中”的解釋:主內存一般映射到0x80000000-0x9FFFFFFF的通過cache的512M空間中,外設則是映射到0xA0000000-0xBFFFFFFF的不通過cache的512M空間中。當然,上面說的兩個512M空間都是MIPS的虛擬內存空間,它們的虛擬地址直接減去一個偏移得到的物理地址,是指向同一塊512M的物理空間。所以說“主內存和外設都只能映射在512MB的地址空間中”這句話是沒有錯的,只是說的比較隱晦。

(5)兩個虛擬地址映射到一個物理地址?

這種映射關係是不會衝突的,實際上使用時並不會真的用兩個不同的虛地址去映射同一個物理地址,因爲這兩個區域的屬性不同。kseg0是unmapped,cached;而kseg1是unmaped,uncached的,所以真正使用時kseg0一般給內核使用,kseg1一般作爲設備地址空間使用。劃分的話,可以各佔一半,譬如在mips32裏,kseg0使用0x8000_0000到0x9000_0000;kseg1使用0xB000_0000到0xC000_0000,各256M即可。

在重啓將系統初始化爲已知狀態的底層軟件中,小心地將同一塊物理內存區域同時映射爲緩存和非緩存是有用的,有時甚至是必要的。但對於後面運行的代碼來說,可能不是這樣。對於每塊物理內存,確定它是不是緩存的,然後嚴格按照剛剛確定的方式來對待。

 

參考:

《MIPS體系結構透視》

6 底層內存管理與TLB

10.3 <可見緩存的問題>

MIPS,PowerPC和ARM訪問I/O方式的比較

MIPS Cache

MIPS的存儲管理模型

MIPS存儲管理結構及Linux代碼分析


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