1. 程式人生 > >linux核心函式 ioremap()的原理及意義

linux核心函式 ioremap()的原理及意義

作業系統:ubuntu10.04



1,ioremap


void * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags) 
引數: 
phys_addr:要對映的起始的IO地址; 

size:要對映的空間的大小; 

flags:要對映的IO空間的和許可權有關的標誌; 

功能: 將一個IO地址空間對映到核心的虛擬地址空間上去,便於訪問; 

實現:對要對映的IO地址空間進行判斷,PCI/ISA地址不需要重新對映,也不允許使用者將IO地址空間對映到正在使用的RAM中,最後申請一 個vm_area_struct結構,呼叫remap_area_pages填寫頁表,若填寫過程不成功則釋放申請的vm_area_struct空 間;

意義:
比如isa裝置和pci裝置,或者是fb,硬體的跳線或者是物理連線方式決定了硬體上的記憶體影射到的cpu實體地址。 
在核心訪問這些地址必須分配給這段記憶體以虛擬地址

,這正是__ioremap的意義所在 ,需要注意的是,實體記憶體已經"存在"了,無需alloc page給這段地址了. 



為了使軟體訪問I/O記憶體,必須為裝置分配虛擬地址.這就是ioremap的工作.這個函式專門用來為I/O記憶體區域分配虛擬地址(空間).對於直接對映的I/O地址ioremap不做任何事情(uClinux中是這麼實現的??) 
有了ioremap(和iounmap),裝置就可以訪問任何I/O記憶體空間,不論它是否直接對映到虛擬地址空間.但是,這些地址永遠不能直接使用(指實體地址),而要用readb這種函式.

根據計算機平臺和所使用匯流排的不同,I/O 記憶體可能是,也可能不是通過頁表訪問的,通過頁表訪問的是統一編址(PowerPC),否則是獨立編址(Intel)。如果訪問是經由頁表進行的,核心必須首先安排實體地址使其對裝置驅動 程式可見(這通常意味著在進行任何 I/O 之前必須先呼叫ioremap)。如果訪問無需頁表,那麼 I/O 記憶體區域就很象 I/O 埠,可以使 用適當形式的函式讀寫它們。 

不管訪問
 I/O 記憶體時是否需要呼叫 ioremap,都不鼓勵直接使用指向 I/O 記憶體的指標。儘管(在“I/O 埠和 I/O 記憶體” 介紹過)I/O 記憶體在硬體一級是象普通 RAM 一樣定址的,但在“I/O 暫存器和常規記憶體”中描述過的那些需要額外小心的情況中已經建議不要使用普 通指標。相反,使用“包裝的”函式訪問 I/O 記憶體,一方面在所有平臺上都是安全的,另一方面,在可以直接對指標指向的記憶體區域執行操作的時候,該函式 是經過優化的


在X86體系下的,CPU的實體地址和PCI匯流排地址共用一個空間。linux核心將3G-4G的虛擬地址固定對映到了實體地址的0-1G的地方。但是如果外圍裝置上的地址高於1G,例如某塊PCI卡分配到了一個高於1G的地址,就需要呼叫ioremap來重新建立該實體地址(匯流排地址)和虛擬地址之間的對映。這個對映過程是這樣的:在ioremap.c檔案的__ioremap函式中首先對將來對映的實體地址進行檢查,也就是不能重新對映640K-1M地址(由於歷史的原因,實體地址640k到1M空間被保留給了顯示卡),普通的 ram地也不能重新被對映。之後呼叫get_vm_area獲得可用的虛擬地址,然後根這虛擬地址和欲對映的實體地址修改頁表,之後核心就可以用這個虛擬地址來訪問對映的實體地址了。

2,ioremap

offset: 物理空間(I/O裝置上的一塊實體記憶體)的起始地址
size:   物理空間的大小

給一段實體地址(起始地址offset)建立頁表(地址對映)
--------------------------------------------------
static inline void __iomem * ioremap(unsigned long offset, unsigned long size)
{   
    return __ioremap(offset, size, 0);
}    



    Remap an arbitrary physical address space into the kernel virtual address space. Needed when the kernel wants to access high addresses directly.
--------------------------------------------------
void __iomem * __ioremap(unsigned long phys_addr, unsigned long size, unsigned long flags){
    void __iomem * addr;
    struct vm_struct * area;
    unsigned long offset, last_addr;
    last_addr = phys_addr + size - 1;
    if (!size || last_addr < phys_addr)
        return NULL;
    if (phys_addr >= ISA_START_ADDRESS && last_addr < ISA_END_ADDRESS)
        return (void __iomem *) phys_to_virt(phys_addr);

如果需要對映的空間的起始地址phys_addr為於ZONE_NORMAL區,通過virt_to_page()進行對映
|------------------------------------------------------------------------------|
|   if (phys_addr <= virt_to_phys(high_memory - 1))                            |
|    {                                                                         | 
|       char *t_addr, *t_end;                                                  |
|       struct page *page;                                                     |
|       t_addr = __va(phys_addr);                                              |
|       t_end = t_addr + (size - 1);                                           |
|       for(page = virt_to_page(t_addr); page <= virt_to_page(t_end); page++) -|
|           if(!PageReserved(page))                                            |
|               return NULL;                                                   |
|   }                                                                          |
|------------------------------------------------------------------------------|
    offset = phys_addr & ~PAGE_MASK;
    phys_addr &= PAGE_MASK;
    size = PAGE_ALIGN(last_addr+1) - phys_addr;


get_vm_area()建立型別為vm_struct的新描述符
get_vm_area()首先呼叫kmalloc()為新描述符獲得一個儲存區;然後掃描型別為struct vm_struct的描述符表,查詢一個可用線性地址空間(至少包含size+4096個地址)。
|----------------------------------------------------------|
|   area = get_vm_area(size, VM_IOREMAP | (flags << 20)); -|
|   if (!area)                                             |
|       return NULL;                                       |
|----------------------------------------------------------|



3,參考檔案
1,http://wenku.baidu.com/link?url=uM3vDOg6SgUNegieKZ8yFHQ0cO8CsUs9TcSJuBxx4dHoDoP3XYpWkEXrCzXd2mRgOIFQHV-ZZZbnk8dPgoE4qcZzjLZYeXXOImI1B8VsT4C