ioremap()

来源:百度文库 编辑:神马文学网 时间:2024/04/26 01:56:04
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; |

  |----------------------------------------------------------|

  area->phys_addr = phys_addr;

  addr = (void __iomem *) area->addr;

  ioremap_page_range()负责为一段物理地址(起始地址为phys_addr)和一段线性地址(起始地址为addr > VMALLOC_START, 大小为size)建立映射页表(建立的页表由内核页表的最高32项的其中几个目录项进行管理),如果建立成功则返回0

  |-----------------------------------------------------------|

  | if (ioremap_page_range( -(unsigned long) addr, |

  | (unsigned long) addr + size, |

  | phys_addr, |

  | flags)) |

  | { |

  | vunmap((void __force *) addr); |

  | return NULL; |

  | } |

  |-----------------------------------------------------------|

  return (void __iomem *) (offset + (char __iomem *)addr);

  }