Linuxカーネルアドレス空間のレイアウトと実現コード

8056 ワード

http://www.linuxforum.net/forum/showflat.php?Board=linuxK&Number=147603
 
Linuxカーネルアドレス空間のレイアウト1)Linuxは、4 Gの線形アドレス空間全体をユーザ空間とカーネル空間の二つの部分に分け、カーネルアドレス空間は「物理メモリ領域」、「仮想メモリ割り当てエリア」、「ハイエンドページマッピングエリア」、「専用ページマップエリア」、「システム予約マップエリア」のいくつかの領域に分けられています.2)は、拡張ページ長(PSE)とグローバルページ(PGE)をサポートするマシン上にあります.物理領域は4 Mページを使用して、グローバルページとして処理しています.システム物理メモリが896 Mより大きい場合、物理領域を超える部分のメモリはハイエンドメモリ、ローエンドメモリ、ハイエンドメモリ用highmem_と呼ばれています.startPage変数は境界を決めます.カーネルはハイエンドメモリにアクセスする時、それらを「ハイエンドページマッピングエリア」にマッピングしなければなりません.3)Linuxはカーネル空間の一番上の128 K領域を保留エリアとして残しています.その直後に保留エリア以下のセクションは専用ページマッピングエリアです.その総サイズと各ページの用途はfixed uuです.address列挙構造は編み出す時に定義して、_u uを使います.fix_to_VIrt(index)は、専用エリア内の予め定義されたページの論理アドレスを取得することができます.専用ページエリアでは、CPUごとにハイエンドメモリマッピングページを定義し、中断処理においてハイエンドページのマッピング動作に使用します.4)カーネル空間の上部32 Mから、長さ4 Mの一部のエリアはハイエンドメモリマッピングエリアで、ちょうど1ページのフレームテーブルで表される物理メモリの総量を占有します.これは1024つのハイエンドページのマッピングをバッファすることができます.物理領域とハイエンドマッピングエリアの間は虚存メモリ割り当てエリアであり、vmalloc()関数のために使用されます.その前部と物理領域は8 M分離帯があり、後部とハイエンドマッピングエリアは8 Kの分離帯があります.5)システム物理メモリが4 Gを超えると、CPUの拡張ページ(PAE)を使用しなければなりません.モードで提供された64ビットのカタログ項目は、4 G以上の物理メモリにアクセスできます.PAEモードでは、線形アドレスから物理アドレスへの変換には、3段のページ表を使用しています.1段目のページカタログは、線形アドレスの最高2桁の索引で、各ディレクトリ項目は1 Gのアドレス空間に対応しています.2段目のページ目のエントリは9桁のインデックスで、各ディレクトリ項目は2 Mのアドレス空間に対応しています.各ディレクトリ項目は4 Kのページフレームに対応しています.ページカタログ項目に記載されている物理アドレスを36ビットに拡張したほか、64ビットと32ビットのページカタログ項目構造には違いがありません.PAEモードでは、PSEビットを含む中級ページカタログ項目に対応するページが4 Mから2 Mに減少しました.

/* Just any arbitrary offset to the start of the vmalloc VM area: the
* current 8MB value just means that there will be a 8MB "hole" after the
* physical memory until the kernel virtual memory starts. That means that
* any out-of-bounds memory accesses will hopefully be caught.
* The vmalloc() routines leaves a hole of 4kB between each vmalloced
* area for the same reason. ;)
*/
#define VMALLOC_OFFSET (8*1024*1024)
#define VMALLOC_START (((unsigned long) high_memory + 2*VMALLOC_OFFSET-1) & /
~(VMALLOC_OFFSET-1))
#define VMALLOC_VMADDR(x) ((unsigned long)(x))
#if CONFIG_HIGHMEM
# define VMALLOC_END (PKMAP_BASE-2*PAGE_SIZE)
#else
# define VMALLOC_END (FIXADDR_START-2*PAGE_SIZE)
#endif

#define PKMAP_BASE (0xfe000000UL)

#define FIXADDR_TOP (0xffffe000UL)
#define FIXADDR_SIZE (__end_of_fixed_addresses << PAGE_SHIFT)
#define FIXADDR_START (FIXADDR_TOP - FIXADDR_SIZE)

#define __fix_to_virt(x) (FIXADDR_TOP - ((x) << PAGE_SHIFT))

/*
* on UP currently we will have no trace of the fixmap mechanizm,
* no page table allocations, etc. This might change in the
* future, say framebuffers for the console driver(s) could be
* fix-mapped?
*/
enum fixed_addresses {
#ifdef CONFIG_X86_LOCAL_APIC
FIX_APIC_BASE, /* local (CPU) APIC) -- required for SMP or not */
#endif
#ifdef CONFIG_X86_IO_APIC
FIX_IO_APIC_BASE_0,
FIX_IO_APIC_BASE_END = FIX_IO_APIC_BASE_0 + MAX_IO_APICS-1,
#endif
#ifdef CONFIG_X86_VISWS_APIC
FIX_CO_CPU, /* Cobalt timer */
FIX_CO_APIC, /* Cobalt APIC Redirection Table */
FIX_LI_PCIA, /* Lithium PCI Bridge A */
FIX_LI_PCIB, /* Lithium PCI Bridge B */
#endif
#ifdef CONFIG_HIGHMEM
FIX_KMAP_BEGIN, /* reserved pte's for temporary kernel mappings */
FIX_KMAP_END = FIX_KMAP_BEGIN+(KM_TYPE_NR*NR_CPUS)-1,
#endif
__end_of_fixed_addresses
};
enum km_type {
KM_BOUNCE_READ,
KM_BOUNCE_WRITE,
KM_TYPE_NR
};

; arch/i386/mm/init.c:

static void __init pagetable_init (void)
{
unsigned long vaddr, end;
pgd_t *pgd, *pgd_base;
int i, j, k;
pmd_t *pmd;
pte_t *pte, *pte_base;

/*
* This can be zero as well - no problem, in that case we exit
* the loops anyway due to the PTRS_PER_* conditions.
*/
end = (unsigned long)__va(max_low_pfn*PAGE_SIZE);

pgd_base = swapper_pg_dir;
#if CONFIG_X86_PAE PAE , 4 64
for (i = 0; i < PTRS_PER_PGD; i++) {
pgd = pgd_base + i;
__pgd_clear(pgd);
}
#endif
i = __pgd_offset(PAGE_OFFSET);
pgd = pgd_base + i;

for (; i < PTRS_PER_PGD; pgd++, i++) {
vaddr = i*PGDIR_SIZE;
if (end && (vaddr >= end))
break;
#if CONFIG_X86_PAE
pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);
set_pgd(pgd, __pgd(__pa(pmd) + 0x1));
#else
pmd = (pmd_t *)pgd;
#endif
if (pmd != pmd_offset(pgd, 0))
BUG();
for (j = 0; j < PTRS_PER_PMD; pmd++, j++) {
, ,
vaddr = i*PGDIR_SIZE + j*PMD_SIZE;
if (end && (vaddr >= end))
break;
if (cpu_has_pse) { CPU
unsigned long __pe;

set_in_cr4(X86_CR4_PSE);
boot_cpu_data.wp_works_ok = 1;
__pe = _KERNPG_TABLE + _PAGE_PSE + _PAGE_USER + __pa(vaddr);
/* Make it "global" too if supported */
if (cpu_has_pge) {
set_in_cr4(X86_CR4_PGE);
__pe += _PAGE_GLOBAL;
}
set_pmd(pmd, __pmd(__pe));
continue;
}
;
pte_base = pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);

for (k = 0; k < PTRS_PER_PTE; pte++, k++) {
vaddr = i*PGDIR_SIZE + j*PMD_SIZE + k*PAGE_SIZE;
if (end && (vaddr >= end))
break;
*pte = mk_pte_phys(__pa(vaddr), PAGE_KERNEL);
}
set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte_base)));
if (pte_base != pte_offset(pmd, 0))
BUG();

}
}

/*
* Fixed mappings, only the page table structure has to be
* created - mappings will be set by set_fixmap():
*/
vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;

fixrange_init(vaddr, 0, pgd_base);

#if CONFIG_HIGHMEM
/*
* Permanent kmaps:
*/
vaddr = PKMAP_BASE;
fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base);

pgd = swapper_pg_dir + __pgd_offset(vaddr);
pmd = pmd_offset(pgd, vaddr);
pte = pte_offset(pmd, vaddr);
pkmap_page_table = pte;
#endif

#if CONFIG_X86_PAE
/*
* Add low memory identity-mappings - SMP needs it when
* starting up on an AP from real-mode. In the non-PAE
* case we already have these mappings through head.S.
* All user-space mappings are explicitly cleared after
* SMP startup.
*/
pgd_base[0] = pgd_base[USER_PTRS_PER_PGD];
#endif
}
static void __init fixrange_init (unsigned long start, unsigned long end, pgd_t
*pgd_base)
{
pgd_t *pgd;
pmd_t *pmd;
pte_t *pte;
int i, j;
unsigned long vaddr;

vaddr = start;
i = __pgd_offset(vaddr);
j = __pmd_offset(vaddr);
pgd = pgd_base + i;

for ( ; (i < PTRS_PER_PGD) && (vaddr != end); pgd++, i++) {
#if CONFIG_X86_PAE
if (pgd_none(*pgd)) {
pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);
set_pgd(pgd, __pgd(__pa(pmd) + 0x1));
if (pmd != pmd_offset(pgd, 0))
printk("PAE BUG #02!/n");
}
pmd = pmd_offset(pgd, vaddr);
#else
pmd = (pmd_t *)pgd;
#endif
for (; (j < PTRS_PER_PMD) && (vaddr != end); pmd++, j++) {
if (pmd_none(*pmd)) {
pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte)));
if (pte != pte_offset(pmd, 0))
BUG();
}
vaddr += PMD_SIZE;
}
j = 0;
}
}