1. 程式人生 > >arm linux 頁表建立

arm linux 頁表建立

本文對arm linux頁表建立函式進行說明。在http://blog.csdn.net/flaoter/article/details/73381695中對MMU使能之前的臨時頁表進行了說明,此文是對kernel中正式頁表建立過程進行說明。文中使用的kernel版本為4.4。
arm linux使用兩級頁表,L1是pgd,L2是pte。其中L1頁表共2048項,每項佔用8bytes,每項對應2M記憶體,共佔用2048*8=16K bytes。L1項指向的page中放有2個L2頁表,每個頁表256項,每項佔用4 bytes,對應4K記憶體,共2*256*4=2K bytes,如下面程式碼中的h/w pt。Linux pt是linux管理用的頁表。
arch/arm/include/asm/pgtable-2level.h

   pgd             pte
 |        |
 +--------+
 |        |       +------------+ +0
 +- - - - +       | Linux pt 0 |
 |        |       +------------+ +1024
 +--------+ +0    | Linux pt 1 |
 |        |-----> +------------+ +2048
 +- - - - + +4    |  h/w pt 0  |
 |        |-----> +------------+ +3072
 +
--------+ +8 | h/w pt 1 | | | +------------+ +4096

頁表建立通過函式create_mapping實現,在kernel初始化時的paging_init函式中會對memblock進行頁表建立。

struct map_desc {
    unsigned long virtual; //虛擬地址
    unsigned long pfn; //page frame
    unsigned long length; //地址的長度
    unsigned int type;
};
struct mem_type {
    pteval_t prot_pte;
    pteval_t prot_pte_s2;
    pmdval_t prot_l1;
    pmdval_t prot_sect;
    unsigned
int domain; }; static void __init create_mapping(struct map_desc *md) { unsigned long addr, length, end; phys_addr_t phys; const struct mem_type *type; pgd_t *pgd; if (md->virtual != vectors_base() && md->virtual < TASK_SIZE) { //使用者空間返回 pr_warn("BUG: not creating mapping for 0x%08llx at 0x%08lx in user region\n", (long long)__pfn_to_phys((u64)md->pfn), md->virtual); return; } if ((md->type == MT_DEVICE || md->type == MT_ROM) && md->virtual >= PAGE_OFFSET && md->virtual < FIXADDR_START && (md->virtual < VMALLOC_START || md->virtual >= VMALLOC_END)) { //IO型別記憶體申請低端記憶體 pr_warn("BUG: mapping for 0x%08llx at 0x%08lx out of vmalloc space\n", (long long)__pfn_to_phys((u64)md->pfn), md->virtual); } type = &mem_types[md->type]; addr = md->virtual & PAGE_MASK; phys = __pfn_to_phys(md->pfn); length = PAGE_ALIGN(md->length + (md->virtual & ~PAGE_MASK)); if (type->prot_l1 == 0 && ((addr | phys | length) & ~SECTION_MASK)) { //非段對映,返回 pr_warn("BUG: map for 0x%08llx at 0x%08lx can not be mapped using pages, ignoring.\n", (long long)__pfn_to_phys(md->pfn), addr); return; } pgd = pgd_offset_k(addr); //(1) end = addr + length; do { unsigned long next = pgd_addr_end(addr, end); alloc_init_pud(pgd, addr, next, phys, type); //(2) phys += next - addr; addr = next; } while (pgd++, addr != end); }

(1) 查詢一級頁表項

#define PGDIR_SHIFT     21
#define pgd_index(addr)     ((addr) >> PGDIR_SHIFT)
#define pgd_offset(mm, addr)    ((mm)->pgd + pgd_index(addr))
#define pgd_offset_k(addr)  pgd_offset(&init_mm, addr)

在kernel初始化彙編階段的head.S中可知,一級頁表的地址範圍是0xC0004000~0xC0008000,即pgd的起始地址是0xC0004000, map_lowmem中addr=0xC0000000, pgd_offset_k(addr)解釋為
(pgd_t*)0xC0004000 + (0xC0000000) >> 21

pgtable-2level-types.h

typedef u32 pmdval_t;
typedef pmdval_t pgd_t[2];

pgd_t型別是u32[2]的陣列型別,所以(pgd_t*)0xC0004000 + (0xC0000000) >> 21=0xC0007000

(2) 呼叫alloc_init_pmd

static void __init alloc_init_pmd(pud_t *pud, unsigned long addr,
                      unsigned long end, phys_addr_t phys,
                      const struct mem_type *type)
{
    pmd_t *pmd = pmd_offset(pud, addr);
    unsigned long next;

    do {
        /*
         * With LPAE, we must loop over to map
         * all the pmds for the given range.
         */
        next = pmd_addr_end(addr, end);

        /*
         * Try a section mapping - addr, next and phys must all be
         * aligned to a section boundary.
         */
        if (type->prot_sect &&
                ((addr | next | phys) & ~SECTION_MASK) == 0) {
            __map_init_section(pmd, addr, next, phys, type);  //(a)
        } else {
            alloc_init_pte(pmd, addr, next,
                        __phys_to_pfn(phys), type);   //(b)
        }

        phys += next - addr;

    } while (pmd++, addr = next, addr != end);
}

(a)段對映,

static void __init __map_init_section(pmd_t *pmd, unsigned long addr,
            unsigned long end, phys_addr_t phys,
            const struct mem_type *type)
{
    pmd_t *p = pmd;

#ifndef CONFIG_ARM_LPAE
    /*
     * In classic MMU format, puds and pmds are folded in to
     * the pgds. pmd_offset gives the PGD entry. PGDs refer to a
     * group of L1 entries making up one logical pointer to
     * an L2 table (2MB), where as PMDs refer to the individual
     * L1 entries (1MB). Hence increment to get the correct
     * offset for odd 1MB sections.
     * (See arch/arm/include/asm/pgtable-2level.h)
     */
    if (addr & SECTION_SIZE)
        pmd++;
#endif
    do {
        *pmd = __pmd(phys | type->prot_sect);        //頁表填充項是(phys | type->prot_sect)
        phys += SECTION_SIZE;
    } while (pmd++, addr += SECTION_SIZE, addr != end);

    flush_pmd_entry(p);    //重新整理到記憶體
}
typedef u32 pmdval_t;
typedef pmdval_t pmd_t;

pmd_t是u32型別,指標值就是傳進來的pgd_t,*pmd填充的內容即頁表項就是(phys | type->prot_sect)。此處地址是按1M累加的,傳進來的addr是按2M累加的,所以一般情況此函式的do{}while()迴圈都是執行兩次。
下圖是我的平臺執行map_lowmem後的結果,一共佔用了0x74項內容,建立了虛擬地址0xC0000000~0xC7300000到實體地址0x80000000~0x87300000的對映。
0xC0000000 –> 頁表項地址0xC0007000 –>頁表項內容0x8001141E –>實體地址0x80000000
map_lowmem_section
(b) 二級頁表對映

pte_t * __init early_pte_alloc(pmd_t *pmd, unsigned long addr, unsigned long prot)
{
    if (pmd_none(*pmd)) {  //如果L1頁表沒對映
        pte_t *pte = early_alloc(PTE_HWTABLE_OFF + PTE_HWTABLE_SIZE);  //PTE_HWTABLE_OFF=512*4 bytes,PTE_HWTABLE_SIZE=512*4bystes,共申請4K
        __pmd_populate(pmd, __pa(pte), prot);   //填充L1頁表,使L1頁表關聯到L2頁表
    }
    BUG_ON(pmd_bad(*pmd));
    return pte_offset_kernel(pmd, addr);
}

void __init alloc_init_pte(pmd_t *pmd, unsigned long addr,
                  unsigned long end, unsigned long pfn,
                  const struct mem_type *type)
{
    pte_t *pte = early_pte_alloc(pmd, addr, type->prot_l1);  //pte為二級頁表指標
    do {
        set_pte_ext(pte, pfn_pte(pfn, __pgprot(type->prot_pte)), 0);  //填充L2頁表項
        pfn++;    //L2頁表每一項對應4k地址
    } while (pte++, addr += PAGE_SIZE, addr != end);
}

本例中virtual_addr = 0xFFFF0000, virtual_end=0xFFFF1000, pfn=0x000873FC,只進行4K地址的對映,因此只需要填充二級頁表的1項內容即可。
二級頁表指標pte = 0xC73FF7C0, [0xC73FF7C0] = 0x873FC5DF。建立了虛擬地址0xFFFF0000到實體地址0x873FC000的對映關係。

相關推薦

arm linux 建立

本文對arm linux頁表建立函式進行說明。在http://blog.csdn.net/flaoter/article/details/73381695中對MMU使能之前的臨時頁表進行了說明,此文是對kernel中正式頁表建立過程進行說明。文中使用的kerne

arm-linux記憶體建立

linux的記憶體(正式)頁表是在核心程式碼執行到start_kernel函式後執行paging _init函式建立的,這裡要注意一個事情就是說,這裡paging_init函式可以正常建立記憶體頁表的條件有兩個: 1、              meminfo已

記憶體知識梳理2. Linux 建立過程-x86

Linux的定址機制的正常執行機制的正常執行,有賴於在啟動過程中足部建立的核心頁和頁表。本節講述核心頁表的建立過程。 bootloader載入核心映象 作業系統啟動前時啟動bootloader,並用bootloader載入和解壓核心映象到記憶體中。真實模式下bootloader(示例中是grub)不能訪問1

ARM 全域性與二級錶轉換除錯

最近在看ARM的記憶體管理,剛好看到全域性頁表swapper_pg_dir,看了很久細想能否直接通過檢視記憶體方式檢視swapper_pg_dir的記憶體: 幸好之前弄過/dev/mem,知道可以把實體記憶體映射出來。 先貼工具程式碼: #include <stdio.h>

ARM-Linux (臨時,正式) 建立的比較

 很久沒有寫部落格了,由於之前的寫關於OMAP3530文章還沒有整理。再加上一直在找工作,找到工作後又投入到另外的平臺去工作。始終在忙忙碌碌,但是對於程式碼確實漸漸疏遠。 在做專案的時候要使用DDR3分配記憶體,不經意間使用要和MMU以及TLB打交道。因此特地寫下這

arm的2級Linux核心建立過程解析

系統DDR的基地址為0x0,記憶體為1GB,所以TTB的基地址為0x4000。下面要建立虛擬地址0xfe700000到實體地址0xffff0000之間的對映,對映大小為64KB,即16頁。由於實體地址不是1MB位元組對齊,所以必須建立兩級對映。   使用者空間/核心空間

ARM Linux 核心啟動總結 之 建立臨時

硬體平臺:S5PV210   核心版本:Linux2.6.32   檔案:head.S(linux/arch/arm/kernel/) #include <**********> #define KERNEL_RAM_VADDR    (PAGE_OFF

linux iotable_init 靜態對映與核心建立

arm32 linux3.18 mach-vexpress 常用的ioremap或者of_iomap都是動態對映,靜態對映的介面是iotable_init void __init iotable_init(struct map_desc *io_desc, int nr) struct

linux iotable_init 靜態對映與核心建立

arm32 linux3.18 mach-vexpress 常用的ioremap或者of_iomap都是動態對映,靜態對映的介面是iotable_init void __init iotable_init(struct map_desc *io_desc, int nr)

Linux核心追蹤[4.14] X86的5級管理

        X86的4級頁表已經能夠管理48bit(256TB)的VA,以及64TB的PA。不過由於某些供應商釋出了超過64T的超大實體記憶體,因此需要實現了一個5級頁表特性來進行支援。       &

深入淺出記憶體管理--建立

頁表的建立 Linux在啟動過程中,要首先進行記憶體的初始化,那麼就一定要首先建立頁表。我們知道每個程序都擁有各自的程序空間,而每個程序空間又分為核心空間和使用者空間。 以arm32為例,每個程序有4G的虛擬空間,其中0-3G屬於使用者地址空間,3G-4G屬於核心地址空間,核心地址空

深入淺出記憶體管理-Linux核心

核心頁表實現 新版本的Linux核心程式碼中支援4級對映,那麼一個虛擬地址是包含有如下幾個部分: PGD:Page Global Directory,L0級別頁表 PUD:Page Upper Directory,L1級別頁表 PMD : Page Middle Direc

Linux使用者程序記憶體分配及二級PTE的二三事

我們在用偵錯程式看Linux使用者程序程式碼時,發現了一件很有意思的事情,在一段記憶體空間中,有一整頁(4K)都是data abort,如下:第一頁4011c000資料正常... ...4011cfec [0xe28dd014]   add      r13,r13,#0x144011cff0 [0xe8bd

Linux核心4級的演進

Linux記憶體管理中core VM程式碼中,關於頁表(page tables)管理的程式碼是個重點,是虛擬記憶體(Virtual Memory, VM)的基石,本文探討Linux的頁表實現及發展過程。 頁表概覽 在虛擬記憶體中,頁表是個對映表的概念, 即從程序能理解的

轉:建立

       //當型別為MT_DEVICE或者MT_ROM,但是他們的虛擬地址       //又處於vmalloc的空間(c0000000----d0000000)       //VMALLOC_END是在我們include/asm-arm/arch-sep4020/vmalloc.h中定義      

linux的簡單理解!!!

在使用的計算機記憶體(4G)上面使用者可以使用的記憶體只有0~3G,3~4G是系統核心使用的區域。 但是在實體記憶體上面,任何的區域和位置都是可讀可寫操作的,假如程序直接訪問的是實體記憶體,那麼系統就會存在很大的不安全性。 頁表 頁表的存在很好的協調了

linux-----框(塊)

基本介紹 我們知道,在linux作業系統中,CPU在執行一個程序的時候,都會訪問記憶體。 但CPU並不是直接訪問實體記憶體地址,而是通過虛擬地址空間來間接的訪問實體記憶體地址。 所謂的虛擬地址空間,是作業系統為每一個正在執行的程序分配的一個邏輯地址,在32

oracle分建立和約束

oracle分頁 回顧mysql分頁,用limit關鍵字 查詢users表中前二條記錄select * from users limit 0,2 或 select * from users limit 2;0表示第一條記錄的索引號,索引號從0開始 2表示最多選取二個記錄

Linux啟動時的對映

核心啟動時進行記憶體對映, map_mem()->create_mapping() 核心支援4級對映(PGD->PUD->PMD->PTE) ,支援的level由巨集CONFIG_PGTABLE_LEVELS定義,目前為3級對映,也即PGD->

linux:程序 &

一個程序表示的,就是一個可執行程式的一次執行過程中的一個狀態。作業系統對程序的管理,典型的情況,是通過程序表完成的。程序表中的每一個表項,記錄的是當前作業系統中一個程序的情況。對於單CPU的情況而言,每一特定時刻只有一個程序佔用CPU,但是系統中可能同時存在多個活動的(等待執行或繼續執行的)程序。 一個稱為"