From 06615f62b17d7de6e12d2f5ec6b88cf30af08413 Mon Sep 17 00:00:00 2001 From: Ralf Baechle Date: Thu, 23 Nov 2000 02:00:47 +0000 Subject: Merge with Linux 2.4.0-test10. --- arch/i386/mm/fault.c | 76 +++++++++++++++++++++++++++++--------------------- arch/i386/mm/init.c | 55 +++++++++++++++++++++++++----------- arch/i386/mm/ioremap.c | 1 - 3 files changed, 82 insertions(+), 50 deletions(-) (limited to 'arch/i386/mm') diff --git a/arch/i386/mm/fault.c b/arch/i386/mm/fault.c index b88c4a422..946d1f40a 100644 --- a/arch/i386/mm/fault.c +++ b/arch/i386/mm/fault.c @@ -77,31 +77,6 @@ bad_area: return 0; } -static void __init handle_wp_test (void) -{ - const unsigned long vaddr = PAGE_OFFSET; - pgd_t *pgd; - pmd_t *pmd; - pte_t *pte; - - /* - * make it read/writable temporarily, so that the fault - * can be handled. - */ - pgd = swapper_pg_dir + __pgd_offset(vaddr); - pmd = pmd_offset(pgd, vaddr); - pte = pte_offset(pmd, vaddr); - *pte = mk_pte_phys(0, PAGE_KERNEL); - __flush_tlb_all(); - - boot_cpu_data.wp_works_ok = 1; - /* - * Beware: Black magic here. The printk is needed here to flush - * CPU state on certain buggy processors. - */ - printk("Ok"); -} - asmlinkage void do_invalid_op(struct pt_regs *, unsigned long); extern unsigned long idt; @@ -130,6 +105,19 @@ asmlinkage void do_page_fault(struct pt_regs *regs, unsigned long error_code) __asm__("movl %%cr2,%0":"=r" (address)); tsk = current; + + /* + * We fault-in kernel-space virtual memory on-demand. The + * 'reference' page table is init_mm.pgd. + * + * NOTE! We MUST NOT take any locks for this case. We may + * be in an interrupt or a critical region, and should + * only copy the information from the master page table, + * nothing more. + */ + if (address >= TASK_SIZE) + goto vmalloc_fault; + mm = tsk->mm; info.si_code = SEGV_MAPERR; @@ -223,6 +211,7 @@ good_area: bad_area: up(&mm->mmap_sem); +bad_area_nosemaphore: /* User mode accesses just cause a SIGSEGV */ if (error_code & 4) { tsk->thread.cr2 = address; @@ -260,14 +249,7 @@ no_context: /* * Oops. The kernel tried to access some bad page. We'll have to * terminate things with extreme prejudice. - * - * First we check if it was the bootup rw-test, though.. */ - if (boot_cpu_data.wp_works_ok < 0 && - address == PAGE_OFFSET && (error_code & 1)) { - handle_wp_test(); - return; - } if (address < PAGE_SIZE) printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference"); @@ -318,4 +300,34 @@ do_sigbus: /* Kernel mode? Handle exceptions or die */ if (!(error_code & 4)) goto no_context; + return; + +vmalloc_fault: + { + /* + * Synchronize this task's top level page-table + * with the 'reference' page table. + */ + int offset = __pgd_offset(address); + pgd_t *pgd, *pgd_k; + pmd_t *pmd, *pmd_k; + + pgd = tsk->active_mm->pgd + offset; + pgd_k = init_mm.pgd + offset; + + if (!pgd_present(*pgd)) { + if (!pgd_present(*pgd_k)) + goto bad_area_nosemaphore; + set_pgd(pgd, *pgd_k); + return; + } + + pmd = pmd_offset(pgd, address); + pmd_k = pmd_offset(pgd_k, address); + + if (pmd_present(*pmd) || !pmd_present(*pmd_k)) + goto bad_area_nosemaphore; + set_pmd(pmd, *pmd_k); + return; + } } diff --git a/arch/i386/mm/init.c b/arch/i386/mm/init.c index 9ba2baa31..39a6ce0f8 100644 --- a/arch/i386/mm/init.c +++ b/arch/i386/mm/init.c @@ -37,8 +37,8 @@ #include unsigned long highstart_pfn, highend_pfn; -static unsigned long totalram_pages = 0; -static unsigned long totalhigh_pages = 0; +static unsigned long totalram_pages; +static unsigned long totalhigh_pages; /* * BAD_PAGE is the page that is used for page faults when linux @@ -491,16 +491,21 @@ void __init paging_init(void) * before and after the test are here to work-around some nasty CPU bugs. */ +/* + * This function cannot be __init, since exceptions don't work in that + * section. + */ +static int do_test_wp_bit(unsigned long vaddr); + void __init test_wp_bit(void) { /* - * Ok, all PAE-capable CPUs are definitely handling the WP bit right. + * Ok, all PSE-capable CPUs are definitely handling the WP bit right. */ const unsigned long vaddr = PAGE_OFFSET; pgd_t *pgd; pmd_t *pmd; pte_t *pte, old_pte; - char tmp_reg; printk("Checking if this processor honours the WP bit even in supervisor mode... "); @@ -511,27 +516,19 @@ void __init test_wp_bit(void) *pte = mk_pte_phys(0, PAGE_READONLY); local_flush_tlb(); - __asm__ __volatile__( - "jmp 1f; 1:\n" - "movb %0,%1\n" - "movb %1,%0\n" - "jmp 1f; 1:\n" - :"=m" (*(char *) vaddr), - "=q" (tmp_reg) - :/* no inputs */ - :"memory"); + boot_cpu_data.wp_works_ok = do_test_wp_bit(vaddr); *pte = old_pte; local_flush_tlb(); - if (boot_cpu_data.wp_works_ok < 0) { - boot_cpu_data.wp_works_ok = 0; + if (!boot_cpu_data.wp_works_ok) { printk("No.\n"); #ifdef CONFIG_X86_WP_WORKS_OK panic("This kernel doesn't support CPU's with broken WP. Recompile it for a 386!"); #endif - } else - printk(".\n"); + } else { + printk("Ok.\n"); + } } static inline int page_is_ram (unsigned long pagenr) @@ -634,6 +631,30 @@ void __init mem_init(void) } +/* Put this after the callers, so that it cannot be inlined */ +static int do_test_wp_bit(unsigned long vaddr) +{ + char tmp_reg; + int flag; + + __asm__ __volatile__( + " movb %0,%1 \n" + "1: movb %1,%0 \n" + " xorl %2,%2 \n" + "2: \n" + ".section __ex_table,\"a\"\n" + " .align 4 \n" + " .long 1b,2b \n" + ".previous \n" + :"=m" (*(char *) vaddr), + "=q" (tmp_reg), + "=r" (flag) + :"2" (1) + :"memory"); + + return flag; +} + void free_initmem(void) { unsigned long addr; diff --git a/arch/i386/mm/ioremap.c b/arch/i386/mm/ioremap.c index 66f846925..bb72da8c8 100644 --- a/arch/i386/mm/ioremap.c +++ b/arch/i386/mm/ioremap.c @@ -78,7 +78,6 @@ static int remap_area_pages(unsigned long address, unsigned long phys_addr, if (remap_area_pmd(pmd, address, end - address, phys_addr + address, flags)) return -ENOMEM; - set_pgdir(address, *dir); address = (address + PGDIR_SIZE) & PGDIR_MASK; dir++; } while (address && (address < end)); -- cgit v1.2.3