summaryrefslogtreecommitdiffstats
path: root/arch/i386/mm
diff options
context:
space:
mode:
authorRalf Baechle <ralf@linux-mips.org>2000-11-23 02:00:47 +0000
committerRalf Baechle <ralf@linux-mips.org>2000-11-23 02:00:47 +0000
commit06615f62b17d7de6e12d2f5ec6b88cf30af08413 (patch)
tree8766f208847d4876a6db619aebbf54d53b76eb44 /arch/i386/mm
parentfa9bdb574f4febb751848a685d9a9017e04e1d53 (diff)
Merge with Linux 2.4.0-test10.
Diffstat (limited to 'arch/i386/mm')
-rw-r--r--arch/i386/mm/fault.c76
-rw-r--r--arch/i386/mm/init.c55
-rw-r--r--arch/i386/mm/ioremap.c1
3 files changed, 82 insertions, 50 deletions
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 <asm/apic.h>
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));