検索条件
タグで絞り込み
Eclipse(1)
Linux(4)
Linux::ARM(2)
Linux::kernel(1)
Programming(1)
app(1)
build_system(1)
git(1)
linux(5)
linux::ARM(2)
linux::debug(2)
shell(1)
全15件
(1/3ページ)
symbol | value |
---|---|
PAGE_SHIFT | 12(4k) |
PMD_SHIFT | 21 |
SECTION_SHIFT | 20 |
SUPERSECTION_SHIFT | 20 |
/* * Hardware-wise, we have a two level page table structure, where the first * level has 4096 entries, and the second level has 256 entries. Each entry * is one 32-bit word. Most of the bits in the second level entry are used * by hardware, and there aren't any "accessed" and "dirty" bits. * * Linux on the other hand has a three level page table structure, which can * be wrapped to fit a two level page table structure easily - using the PGD * and PTE only. However, Linux also expects one "PTE" table per page, and * at least a "dirty" bit. * * Therefore, we tweak the implementation slightly - we tell Linux that we * have 2048 entries in the first level, each of which is 8 bytes (iow, two * hardware pointers to the second level.) The second level contains two * hardware PTE tables arranged contiguously, preceded by Linux versions * which contain the state information Linux needs. We, therefore, end up * with 512 entries in the "PTE" level. * * This leads to the page tables having the following layout: * * pgd pte * | | * +--------+ * | | +------------+ +0 * +- - - - + | Linux pt 0 | * | | +------------+ +1024 * +--------+ +0 | Linux pt 1 | * | |-----> +------------+ +2048 * +- - - - + +4 | h/w pt 0 | * | |-----> +------------+ +3072 * +--------+ +8 | h/w pt 1 | * | | +------------+ +4096 * * See L_PTE_xxx below for definitions of bits in the "Linux pt", and * PTE_xxx for definitions of bits appearing in the "h/w pt". * * PMD_xxx definitions refer to bits in the first level page table. * * The "dirty" bit is emulated by only granting hardware write permission * iff the page is marked "writable" and "dirty" in the Linux PTE. This * means that a write to a clean page will cause a permission fault, and * the Linux MM layer will mark the page dirty via handle_pte_fault(). * For the hardware to notice the permission change, the TLB entry must * be flushed, and ptep_set_access_flags() does that for us. * * The "accessed" or "young" bit is emulated by a similar method; we only * allow accesses to the page if the "young" bit is set. Accesses to the * page will cause a fault, and handle_pte_fault() will set the young bit * for us as long as the page is marked present in the corresponding Linux * PTE entry. Again, ptep_set_access_flags() will ensure that the TLB is * up to date. * * However, when the "young" bit is cleared, we deny access to the page * by clearing the hardware PTE. Currently Linux does not flush the TLB * for us in this case, which means the TLB will retain the transation * until either the TLB entry is evicted under pressure, or a context * switch which changes the user space mapping occurs. */ #define PTRS_PER_PTE 512 #define PTRS_PER_PMD 1 #define PTRS_PER_PGD 2048 #define PTE_HWTABLE_PTRS (PTRS_PER_PTE) #define PTE_HWTABLE_OFF (PTE_HWTABLE_PTRS * sizeof(pte_t)) #define PTE_HWTABLE_SIZE (PTRS_PER_PTE * sizeof(u32)) /* * PMD_SHIFT determines the size of the area a second-level page table can map * PGDIR_SHIFT determines what a third-level page table entry can map */ #define PMD_SHIFT 21 #define PGDIR_SHIFT 21 #define PMD_SIZE (1UL << PMD_SHIFT) #define PMD_MASK (~(PMD_SIZE-1)) #define PGDIR_SIZE (1UL << PGDIR_SHIFT) #define PGDIR_MASK (~(PGDIR_SIZE-1))FILE: arch/arm/include/asm/pgtable.h
/* to find an entry in a page-table-directory */ #define pgd_index(addr) ((addr) >> PGDIR_SHIFT) /// 21bit@LAPEなし #define pgd_offset(mm, addr) ((mm)->pgd + pgd_index(addr)) /* to find an entry in a kernel page-table-directory */ #define pgd_offset_k(addr) pgd_offset(&init_mm, addr) static inline pte_t *pmd_page_vaddr(pmd_t pmd) { return __va(pmd_val(pmd) & PHYS_MASK & (s32)PAGE_MASK); } #ifndef CONFIG_HIGHPTE #define __pte_map(pmd) pmd_page_vaddr(*(pmd)) #define __pte_unmap(pte) do { } while (0) #else #endif #define pte_index(addr) (((addr) >> PAGE_SHIFT) & (PTRS_PER_PTE - 1)) #define pte_offset_kernel(pmd,addr) (pmd_page_vaddr(*(pmd)) + pte_index(addr)) #define pte_offset_map(pmd,addr) (__pte_map(pmd) + pte_index(addr)) #define pte_unmap(pte) __pte_unmap(pte) #define pte_pfn(pte) ((pte_val(pte) & PHYS_MASK) >> PAGE_SHIFT) #define pfn_pte(pfn,prot) __pte(__pfn_to_phys(pfn) | pgprot_val(prot)) #define pte_page(pte) pfn_to_page(pte_pfn(pte)) #define mk_pte(page,prot) pfn_pte(page_to_pfn(page), prot) #define pte_clear(mm,addr,ptep) set_pte_ext(ptep, __pte(0), 0) #define pte_none(pte) (!pte_val(pte)) #define pte_present(pte) (pte_val(pte) & L_PTE_PRESENT) #define pte_write(pte) (!(pte_val(pte) & L_PTE_RDONLY)) #define pte_dirty(pte) (pte_val(pte) & L_PTE_DIRTY) #define pte_young(pte) (pte_val(pte) & L_PTE_YOUNG) #define pte_exec(pte) (!(pte_val(pte) & L_PTE_XN)) #define pte_special(pte) (0)以上がソースを追うのに必要な情報(だったはず)。
/* * This is useful to dump out the page tables associated with * 'addr' in mm 'mm'. */ void show_pte(struct mm_struct *mm, unsigned long addr) { pgd_t *pgd; if (!mm) mm = &init_mm; printk(KERN_ALERT "pgd = %p\n", mm->pgd); pgd = pgd_offset(mm, addr); printk(KERN_ALERT "[%08lx] *pgd=%08llx", addr, (long long)pgd_val(*pgd)); ...第一レベル変換テーブルが mm_structのメンバpgdであることがわかる。
struct mm_struct init_mm = { .mm_rb = RB_ROOT, .pgd = swapper_pg_dir, .mm_users = ATOMIC_INIT(2), .mm_count = ATOMIC_INIT(1), .mmap_sem = __RWSEM_INITIALIZER(init_mm.mmap_sem), .page_table_lock = __SPIN_LOCK_UNLOCKED(init_mm.page_table_lock), .mmlist = LIST_HEAD_INIT(init_mm.mmlist), INIT_MM_CONTEXT(init_mm) };これ(swapper_pg_dir)の実態はどこにあるのか。
/* * swapper_pg_dir is the virtual address of the initial page table. * We place the page tables 16K below KERNEL_RAM_VADDR. Therefore, we must * make sure that KERNEL_RAM_VADDR is correctly set. Currently, we expect * the least significant 16 bits to be 0x8000, but we could probably * relax this restriction to KERNEL_RAM_VADDR >= PAGE_OFFSET + 0x4000. */ #define KERNEL_RAM_VADDR (PAGE_OFFSET + TEXT_OFFSET) #if (KERNEL_RAM_VADDR & 0xffff) != 0x8000 #error KERNEL_RAM_VADDR must start at 0xXXXX8000 #endif #ifdef CONFIG_ARM_LPAE /* LPAE requires an additional page for the PGD */ #define PG_DIR_SIZE 0x5000 #define PMD_ORDER 3 #else #define PG_DIR_SIZE 0x4000 #define PMD_ORDER 2 #endif .globl swapper_pg_dir .equ swapper_pg_dir, KERNEL_RAM_VADDR - PG_DIR_SIZE .macro pgtbl, rd, phys add \rd, \phys, #TEXT_OFFSET - PG_DIR_SIZE .endmMMUのテーブルウォークに使われる、レベル1変換テーブルをswapper_pg_dirに格納している。
PGD | 1MiB単位のブロック.. 1MiB x 4Ki個(12bit)のテーブルで 4GiBフルアドレッシング |
PTE | 最終的に4KiBをインデックスして 256個(8bit)テーブルがあれば 1MiBになる。 |
#ifdef CONFIG_ARM_LPAE #else #define cpu_get_pgd() \ ({ \ unsigned long pg; \ __asm__("mrc p15, 0, %0, c2, c0, 0" \ : "=r" (pg) : : "cc"); \ pg &= ~0x3fff; \ (pgd_t *)phys_to_virt(pg); \ }) #endif意訳すると、"Translation Table Base Register 0"を取得する
static int __kprobes do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs) { ... tsk = current; mm = tsk->mm; ... /* * If we're in an interrupt or have no user * context, we must not take the fault.. */ if (in_atomic() || !mm) goto no_context; ... fault = __do_page_fault(mm, addr, fsr, flags, tsk); .... if (likely(!(fault & (VM_FAULT_ERROR | VM_FAULT_BADMAP | VM_FAULT_BADACCESS)))) return 0; ... __do_user_fault(tsk, addr, fsr, sig, code, regs); return 0; no_context: __do_kernel_fault(mm, addr, fsr, regs); return 0;
static int __kprobes __do_page_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr, unsigned int flags, struct task_struct *tsk) { struct vm_area_struct *vma; int fault; vma = find_vma(mm, addr); fault = VM_FAULT_BADMAP; if (unlikely(!vma)) goto out; if (unlikely(vma->vm_start > addr)) goto check_stack; /* * Ok, we have a good vm_area for this * memory access, so we can handle it. */ good_area: if (access_error(fsr, vma)) { fault = VM_FAULT_BADACCESS; goto out; } return handle_mm_fault(mm, vma, addr & PAGE_MASK, flags); check_stack: /* Don't allow expansion below FIRST_USER_ADDRESS */ if (vma->vm_flags & VM_GROWSDOWN && addr >= FIRST_USER_ADDRESS && !expand_stack(vma, addr)) goto good_area; out: return fault; }ここはユーザ空間でのfaultで、タスクに割り当てられた空間かどうかをチェックしています。
/* * Something tried to access memory that isn't in our memory map.. * User mode accesses just cause a SIGSEGV */ static void __do_user_fault(struct task_struct *tsk, unsigned long addr, unsigned int fsr, unsigned int sig, int code, struct pt_regs *regs)
/* * Oops. The kernel tried to access some page that wasn't present. */ static void __do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr, struct pt_regs *regs) { /* * Are we prepared to handle this kernel fault? */ if (fixup_exception(regs)) return; /* * No handler, we'll have to terminate things with extreme prejudice. */ bust_spinlocks(1); printk(KERN_ALERT "Unable to handle kernel %s at virtual address %08lx\n", (addr < PAGE_SIZE) ? "NULL pointer dereference" : "paging request", addr); show_pte(mm, addr); die("Oops", regs, fsr); bust_spinlocks(0); do_exit(SIGKILL); }カーネル空間でfaultが起きることは不具合以外にはレアケースだと考えられます。
■Cortex-A Series Programmer's manual
8.9.2 Emulation of dirty and accessed bits
Linux makes use of a bit associated with each page which marks whether the page is dirty (the page has been modified by the application and may therefore need to be written from memory to disk when the page is no longer needed).
This is not directly supported in hardware by ARM processors, and must therefore be implemented by kernel software.
When a page is first created, it is marked as read-only.
The first write to such a page (a clean page) will cause an MMU permission fault and the kernel data abort handler will be called.
The Linux memory management code will mark the page as dirty, if the page should indeed be writable, using the function handle_pte_fault() .
The page table entry is modified to make it allow both reads and writes and the TLB entry invalidated from the cache to ensure the MMU now uses the newly modified entry.
The abort handler then returns to re-try the faulting access in the application.
Linuxは、ページがダーティであるか否かをマークし、各ページに対応するビットを利用する。*訳注
ダーティである、とは、ページがアプリケーションによって変更されるか、そのページが必要ではなくなったときにメモリからディスクへ書き出す必要があることを示す。
ARMプロセッサでは、直接的にハードウェアでサポートされていない。したがってカーネルソフトウェアで実装される必要がある。
ページが最初に作られたとき、read-onlyとしてマークされる。
そのページ(クリーンなページ)に対して、最初の書き込みは、MMUのpermission faultの要因となり、カーネルのデータアボートハンドラが呼ばれる。
Linuxのメモリ管理コードは、そのページをdirtyと設定します。そのページが書き込み可能であるべきならば、handle_pte_fault()を使う。
ページテーブルエントリーは、読み書き可能に変更される。そして、TLBエントリは、MMUが今新しく変更されたエントリを使うため、キャッシュから無効化される。
その後、アボートハンドラは、アプリケーション内でfaultアクセスした命令を再実行するため、リターンする。
NAME copy_to_user - Copy a block of data into user space. SYNOPSIS unsigned long copy_to_user(void __user * to, const void * from, unsigned long n); ARGUMENTS to Destination address, in user space. from Source address, in kernel space. n Number of bytes to copy. CONTEXT User context only. This function may sleep. DESCRIPTION Copy data from kernel space to user space. Returns number of bytes that could not be copied. On success, this will be zero.
.macro str1w ptr reg abort strusr \reg, \ptr, 4, abort=\abort .endmFILE: archinc/asm/assembler.h
.macro strusr, reg, ptr, inc, cond=al, rept=1, abort=9001f usracc str, \reg, \ptr, \inc, \cond, \rept, \abort .endm .macro usracc, instr, reg, ptr, inc, cond, rept, abort, t=TUSER() .rept \rept 9999: .if \inc == 1 \instr\cond\()b\()\t \reg, [\ptr], #\inc .elseif \inc == 4 \instr\cond\()\t \reg, [\ptr], #\inc .else .error "Unsupported inc macro argument" .endif .pushsection __ex_table,"a" .align 3 .long 9999b, \abort .popsection .endr .endm usracc str, \reg, \ptr, \inc(=4), \cond(=none,al), \rept(=none,1), \abort(=pop) stral reg, [ptr], #inc .pushsection __ex_table,"a" .align 3 .long 9999b, \abort
. = ALIGN(4); __ex_table : AT(ADDR(__ex_table) - LOAD_OFFSET) { __start___ex_table = .; #ifdef CONFIG_MMU *(__ex_table) #endif __stop___ex_table = .;
/* Given an address, look for it in the exception tables. */ const struct exception_table_entry *search_exception_tables(unsigned long addr)さらにこれを使っているところを探すと、アーキ依存部のpage fault処理部分が見つかる。
int fixup_exception(struct pt_regs *regs) { const struct exception_table_entry *fixup; fixup = search_exception_tables(instruction_pointer(regs)); if (fixup) { regs->ARM_pc = fixup->fixup; #ifdef CONFIG_THUMB2_KERNEL /* Clear the IT state to avoid nasty surprises in the fixup */ regs->ARM_cpsr &= ~PSR_IT_MASK; #endif } return fixup != NULL; }さらにさらに、これを使っているところを探すと、
/* * Oops. The kernel tried to access some page that wasn't present. */ static void __do_kernel_fault(struct mm_struct *mm, unsigned long addr, unsigned int fsr, struct pt_regs *regs) { /* * Are we prepared to handle this kernel fault? */ if (fixup_exception(regs)) return;ここに来れば faultが発生しても、Oopsを吐かずに指定されたアドレスへ
static int __kprobes do_page_fault(unsigned long addr, unsigned int fsr, struct pt_regs *regs)
[ALL ] ( echo '#define SYSCALL_NAME close'; echo '#define SYSCALL_NARGS 1'; echo '#define SYSCALL_SYMBOL __libc_close'; echo '#define SYSCALL_CANCELLABLE 1'; echo '#include <syscall-template.S>'; echo 'weak_alias (__libc_close, __close)'; echo 'libc_hidden_weak (__close)'; echo 'weak_alias (__libc_close, close)'; echo 'libc_hidden_weak (close)'; ) \ | arm-linaro-linux-gnueabi-gcc -c -I../include -I/PATH/TO/MYDIR/_build/arm-linaro-linux-gnueabi/build/build-libc-final/io -I/PATH/TO/MYDIR/_build/arm-linaro-linux-gnueabi/build/build-libc-final -I../ports/sysdeps/arm/elf -I../ports/sysdeps/unix/sysv/linux/arm/eabi/nptl -I../ports/sysdeps/unix/sysv/linux/arm/eabi -I../ports/sysdeps/unix/sysv/linux/arm/nptl -I../ports/sysdeps/unix/sysv/linux/arm -I../nptl/sysdeps/unix/sysv/linux -I../nptl/sysdeps/pthread -I../sysdeps/pthread -I../ports/sysdeps/unix/sysv/linux -I../sysdeps/unix/sysv/linux -I../sysdeps/gnu -I../sysdeps/unix/common -I../sysdeps/unix/mman -I../sysdeps/unix/inet -I../nptl/sysdeps/unix/sysv -I../ports/sysdeps/unix/sysv -I../sysdeps/unix/sysv -I../ports/sysdeps/unix/arm -I../nptl/sysdeps/unix -I../ports/sysdeps/unix -I../sysdeps/unix -I../sysdeps/posix -I../ports/sysdeps/arm/eabi -I../ports/sysdeps/arm/nptl -I../ports/sysdeps/arm -I../sysdeps/wordsize-32 -I../sysdeps/ieee754/flt-32 -I../sysdeps/ieee754/dbl-64 -I../sysdeps/ieee754 -I../sysdeps/generic/elf -I../sysdeps/generic -I../nptl -I../ports -I.. -I../libio -I. -nostdinc -isystem /PATH/TO/MYDIR/_build/arm-linaro-linux-gnueabi/buildtools/lib/gcc/arm-linaro-linux-gnueabi/4.7.4/include -isystem /PATH/TO/MYDIR/_build/arm-linaro-linux-gnueabi/buildtools/lib/gcc/arm-linaro-linux-gnueabi/4.7.4/include-fixed -isystem /opt/export/V980_cam/repo/_master_m8m/hosttool/arm-linaro-linux-gnueabi/arm-linaro-linux-gnueabi/sysroot/usr/include -D_LIBC_REENTRANT -include ../include/libc-symbols.h -DASSEMBLER -Wa,--noexecstack -o /PATH/TO/MYDIR/_build/arm-linaro-linux-gnueabi/build/build-libc-final/io/close.o -x assembler-with-cpp - -MD -MP -MF /PATH/TO/MYDIR/_build/arm-linaro-linux-gnueabi/build/build-libc-final/io/close.o.dt -MT /PATH/TO/MYDIR/_build/arm-linaro-linux-gnueabi/build/build-libc-final/io/close.oソースコードだけ見ててもアカンやつや...
src/eglibc-2_13/sysdeps/unix/syscall-template.S src/eglibc-2_13/ports/sysdeps/unix/sysv/linux/arm/nptl/sysdep-cancel.h※nptl/cancel可能を前提として.
>Memory testing with cache >Permalink Submitted by krishnakoyalmannam on Thu, 2011-05-19 02:00. When you are testing external DDR2 memory on a multicore system, with a shared Level 2 Cache, each core could be assigned a dedicated section of memory to test and all the cores memory tests in parallel. This causes the L2 cache to result in more cache miss than cache hit, thereby resulting in increased DRAM bus transactions, which could be very useful during HW Board BringUp. And yes, the ground bounce and cross-talk tests do check for signal integrity problems. There is also MARCH-B and MARCH-C memory testing algorithms that sweep memory with random patterns in one direction and sweep in the reverse direction, so, a cache flush would be required before starting the reverse sweep. And finally, an XOR test to uncover memory cell faults, would make the suite of memory tests quite comprehensive providing greater coverage.
#include <linux/fs.h> #include <linux/init.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> static int cmdline_proc_show(struct seq_file *m, void *v) { seq_printf(m, "%s\n", saved_command_line); return 0; } static int cmdline_proc_open(struct inode *inode, struct file *file) { return single_open(file, cmdline_proc_show, NULL); } static const struct file_operations cmdline_proc_fops = { .open = cmdline_proc_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static int __init proc_cmdline_init(void) { proc_create("cmdline", 0, NULL, &cmdline_proc_fops); return 0; } module_init(proc_cmdline_init);非常に判りよい例.. ;)