Skip to content
  • Konrad Rzeszutek Wilk's avatar
    xen/mmu: Add workaround "x86-64, mm: Put early page table high" · a3864783
    Konrad Rzeszutek Wilk authored
    As a consequence of the commit:
    
    commit 4b239f45
    
    
    Author: Yinghai Lu <yinghai@kernel.org>
    Date:   Fri Dec 17 16:58:28 2010 -0800
    
        x86-64, mm: Put early page table high
    
    it causes the Linux kernel to crash under Xen:
    
    mapping kernel into physical memory
    Xen: setup ISA identity maps
    about to get started...
    (XEN) mm.c:2466:d0 Bad type (saw 7400000000000001 != exp 1000000000000000) for mfn b1d89 (pfn bacf7)
    (XEN) mm.c:3027:d0 Error while pinning mfn b1d89
    (XEN) traps.c:481:d0 Unhandled invalid opcode fault/trap [#6] on VCPU 0 [ec=0000]
    (XEN) domain_crash_sync called from entry.S
    (XEN) Domain 0 (vcpu#0) crashed on cpu#0:
    ...
    
    The reason is that at some point init_memory_mapping is going to reach
    the pagetable pages area and map those pages too (mapping them as normal
    memory that falls in the range of addresses passed to init_memory_mapping
    as argument). Some of those pages are already pagetable pages (they are
    in the range pgt_buf_start-pgt_buf_end) therefore they are going to be
    mapped RO and everything is fine.
    Some of these pages are not pagetable pages yet (they fall in the range
    pgt_buf_end-pgt_buf_top; for example the page at pgt_buf_end) so they
    are going to be mapped RW.  When these pages become pagetable pages and
    are hooked into the pagetable, xen will find that the guest has already
    a RW mapping of them somewhere and fail the operation.
    The reason Xen requires pagetables to be RO is that the hypervisor needs
    to verify that the pagetables are valid before using them. The validation
    operations are called "pinning" (more details in arch/x86/xen/mmu.c).
    
    In order to fix the issue we mark all the pages in the entire range
    pgt_buf_start-pgt_buf_top as RO, however when the pagetable allocation
    is completed only the range pgt_buf_start-pgt_buf_end is reserved by
    init_memory_mapping. Hence the kernel is going to crash as soon as one
    of the pages in the range pgt_buf_end-pgt_buf_top is reused (b/c those
    ranges are RO).
    
    For this reason, this function is introduced which is called _after_
    the init_memory_mapping has completed (in a perfect world we would
    call this function from init_memory_mapping, but lets ignore that).
    
    Because we are called _after_ init_memory_mapping the pgt_buf_[start,
    end,top] have all changed to new values (b/c another init_memory_mapping
    is called). Hence, the first time we enter this function, we save
    away the pgt_buf_start value and update the pgt_buf_[end,top].
    
    When we detect that the "old" pgt_buf_start through pgt_buf_end
    PFNs have been reserved (so memblock_x86_reserve_range has been called),
    we immediately set out to RW the "old" pgt_buf_end through pgt_buf_top.
    
    And then we update those "old" pgt_buf_[end|top] with the new ones
    so that we can redo this on the next pagetable.
    
    Acked-by: default avatar"H. Peter Anvin" <hpa@zytor.com>
    Reviewed-by: default avatarJeremy Fitzhardinge <jeremy.fitzhardinge@citrix.com>
    [v1: Updated with Jeremy's comments]
    [v2: Added the crash output]
    Signed-off-by: default avatarKonrad Rzeszutek Wilk <konrad.wilk@oracle.com>
    a3864783