oremap_phdhd ) )我想大家都很熟悉函数,现在我们来分析一下这个函数。 不恰当的地方请谅解。
对EHCI来说,它将自身的寄存器和内存映射到内存中区! 但是,从CPU的角度看,不能直接访问这个内存空间。 必须将设备的总线地址映射到CPU可以访问的线性地址。
ioremap_phdhd ) )调用函数后,返回线性地址。 此时,CPU可以访问设备的存储器。 (映射到线性地址空间。 此时,CPU可以使用访问内存的指令访问设备的内存空间)主机网桥确定是访问物理内存还是访问设备的内存。 此时,CPU可以访问与存储器相同的设备的存储器)寄存器!
内核版本2.6.22.1
cat /proc/iomem
在这种情况下,在此区间(0xd 84268000xd 8426 BFF )表示EHCI总线地址区间(机器存储器3G )
//*
* io remap _ PhD HD-mapbusmemoryintocpuspace
* @ offset : busaddressofthememory
* @ size : sizeoftheresourcetomap
*
* io remap _ phdhdperformsaplatformspecificsequenceofoperationsto
* makebusmemorycpuaccessibleviathereadb/readw/readl/writeb /
* writew/writelfunctionsandtheothermmiohelpers.the returned
* addressisnotguaranteedtobeusabledirectlyasavirtual
*地址。
*
* thisversionofioremapensuresthatthememoryismarkeduncachable
* onthecpuaswellashonouringexistingcachingrulesfromthingslike
* thepcibus.notethatthereareothercachesandbuffersonmany
* busses.inparticulardriverauthorsshouldreaduponpciwrites
*
* it ' susefulifsomecontrolregistersareinsuchanareaand
* writecombiningorreadcachingisnotdesirable :
*
* Must be freed with iounmap。
*/
(void _ iomem * io remap _ PhD HD (unsigned long phys _ addr,unsigned long size ) ) ) ) ) ) ) ) )
{
phys_addr EHCI总线地址
大小区间(1023 ) )。
无符号长last _ addr;
void _ iomem * p=_ io remap (phys _ addr,size,_PAGE_PCD );
#define _PAGE_PCD0x010
if (! p )
返回p;
/* Guaranteed to be phys_addr,as per __ioremap () /
last_addr=phys_addr size - 1;
if (last _ addr virt _ to _ phys (high _ memory )- 1 ) {
sructpage * p page=virt _ to _ page (_ va ) phys_addr );
无符号长npages;
phys_addr=PAGE_MASK;
/* thismightoverflowandbecomezero . * /
last_addr=page_align(last_addr );
/* . but that's ok,because modulo-2 * * narithmeticwillmake
* the page-aligned ' last-first ' come outright。
*/
npages=(last_addr-phys_addr ) PAGE_SHIFT;
if(change_page_attr(ppage,np
ages, PAGE_KERNEL_NOCACHE) < 0) {iounmap(p);
p = NULL;
}
global_flush_tlb();
}
return p;
}
非连续映射地址空间
非连续映射地址空间用于将连续的线性地址映射到不连续的物理地址!同时它也提供了一种访问高物理内存的方法!
内核主要在一下三种情况使用非连续映射地址空间
l映射设备的I/O空间
l为内核模块分配空间
l为交换分区分配空间
非连续映射地址空间的起始地址在常规映射地址空间的结束地址后8MB-16MB之间,而且保证8MB对齐(地址的低24位为0)
/*
* Remap an arbitrary physical address space into the kernel virtual
* address space. Needed when the kernel wants to access high addresses
* directly.
*
* NOTE! We need to allow non-page-aligned mappings too: we will obviously
* have to convert them into an offset in a page-aligned mapping, but the
* caller shouldn't need to know that small detail.
*/
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;
pgprot_t prot;
/* Don't allow wraparound or zero size */
last_addr = phys_addr + size - 1;
总线地址末端
if (!size || last_addr < phys_addr)
return NULL;
/*
* Don't remap the low PCI/ISA area, it's always mapped..
*/
if (phys_addr >= ISA_START_ADDRESS && last_addr < ISA_END_ADDRESS)
return (void __iomem *) phys_to_virt(phys_addr);
#define ISA_START_ADDRESS0xa0000
#define ISA_END_ADDRESS0x100000
640kb-1Mb之间(此空洞用于连接到ISA总线上的设备)
/*
* Don't allow anybody to remap normal RAM that we're using..
*/
if (phys_addr <= virt_to_phys(high_memory - 1)) {
high_memory为896Mb对应线性地址
phys_addr在小于896Mb的常规内存空间中
char *t_addr, *t_end;
struct page *page;
t_addr = __va(phys_addr);
转化成线性地址
t_end = t_addr + (size - 1);
若小于896MB则此页框应该被设置为保留
for(page = virt_to_page(t_addr); page <= virt_to_page(t_end); page++)
if(!PageReserved(page))
return NULL;
}
prot = __pgprot(_PAGE_PRESENT | _PAGE_RW | _PAGE_DIRTY
| _PAGE_ACCESSED | flags);
#define __pgprot(x)((pgprot_t) { (x) }
/*
* Mappings have to be page-aligned
*/
offset = phys_addr & ~PAGE_MASK;
取一页页框的偏移
phys_addr &= PAGE_MASK;
总线地址按4KB对齐
#define PAGE_SHIFT12
#define PAGE_SIZE(1UL << PAGE_SHIFT)
#define PAGE_MASK(~(PAGE_SIZE-1))
size = PAGE_ALIGN(last_addr+1) - phys_addr;
#define PAGE_ALIGN(addr)(((addr)+PAGE_SIZE-1)&PAGE_MASK)
/*
* Ok, go for it..
*/
area = get_vm_area(size, VM_IOREMAP | (flags << 20));
#define VM_IOREMAP0x00000001/* ioremap() and friends */
申请一个非连续映射节点描述符
if (!area)
return NULL;
area->phys_addr = phys_addr;
总线地址
addr = (void __iomem *) area->addr;
起始线性地址
if (ioremap_page_range((unsigned long) addr,
(unsigned long) addr + size, phys_addr, prot)) {
vunmap((void __force *) addr);
return NULL;
}
return (void __iomem *) (offset + (char __iomem *)addr);
offset + addr
offset为0
addr为线性地址,此地址被CPU用于读写EHCI I/O mem空间
这也验证那句话:在X86平台上总线地址就是物理地址
}
/**
*get_vm_area-reserve a contingous kernel virtual area
*@size:size of the area
*@flags:%VM_IOREMAP for I/O mappings or VM_ALLOC
*
*Search an area of @size in the kernel virtual mapping area,
*and reserved it for out purposes.Returns the area descriptor
*on success or %NULL on failure.
*/
struct vm_struct *get_vm_area(unsigned long size, unsigned long flags)
{
return __get_vm_area(size, flags, VMALLOC_START, VMALLOC_END);
}
struct vm_struct *__get_vm_area(unsigned long size, unsigned long flags,
unsigned long start, unsigned long end)
{
return __get_vm_area_node(size, flags, start, end, -1, GFP_KERNEL);
}