xen PVonHVM: use E820_Reserved area for shared_info
This is a respin of 00e37bdb01
("xen PVonHVM: move shared_info to MMIO before kexec").
Currently kexec in a PVonHVM guest fails with a triple fault because the
new kernel overwrites the shared info page. The exact failure depends on
the size of the kernel image. This patch moves the pfn from RAM into an
E820 reserved memory area.
The pfn containing the shared_info is located somewhere in RAM. This will
cause trouble if the current kernel is doing a kexec boot into a new
kernel. The new kernel (and its startup code) can not know where the pfn
is, so it can not reserve the page. The hypervisor will continue to update
the pfn, and as a result memory corruption occours in the new kernel.
The toolstack marks the memory area FC000000-FFFFFFFF as reserved in the
E820 map. Within that range newer toolstacks (4.3+) will keep 1MB
starting from FE700000 as reserved for guest use. Older Xen4 toolstacks
will usually not allocate areas up to FE700000, so FE700000 is expected
to work also with older toolstacks.
In Xen3 there is no reserved area at a fixed location. If the guest is
started on such old hosts the shared_info page will be placed in RAM. As
a result kexec can not be used.
Signed-off-by: Olaf Hering <olaf@aepfle.de>
Signed-off-by: Konrad Rzeszutek Wilk <konrad.wilk@oracle.com>
This commit is contained in:
parent
8f0d8163b5
commit
9d02b43dee
3 changed files with 58 additions and 27 deletions
|
@ -1495,51 +1495,72 @@ asmlinkage void __init xen_start_kernel(void)
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void __ref xen_hvm_init_shared_info(void)
|
#ifdef CONFIG_XEN_PVHVM
|
||||||
{
|
#define HVM_SHARED_INFO_ADDR 0xFE700000UL
|
||||||
int cpu;
|
static struct shared_info *xen_hvm_shared_info;
|
||||||
struct xen_add_to_physmap xatp;
|
static unsigned long xen_hvm_sip_phys;
|
||||||
static struct shared_info *shared_info_page = 0;
|
static int xen_major, xen_minor;
|
||||||
|
|
||||||
|
static void xen_hvm_connect_shared_info(unsigned long pfn)
|
||||||
|
{
|
||||||
|
struct xen_add_to_physmap xatp;
|
||||||
|
|
||||||
if (!shared_info_page)
|
|
||||||
shared_info_page = (struct shared_info *)
|
|
||||||
extend_brk(PAGE_SIZE, PAGE_SIZE);
|
|
||||||
xatp.domid = DOMID_SELF;
|
xatp.domid = DOMID_SELF;
|
||||||
xatp.idx = 0;
|
xatp.idx = 0;
|
||||||
xatp.space = XENMAPSPACE_shared_info;
|
xatp.space = XENMAPSPACE_shared_info;
|
||||||
xatp.gpfn = __pa(shared_info_page) >> PAGE_SHIFT;
|
xatp.gpfn = pfn;
|
||||||
if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
|
if (HYPERVISOR_memory_op(XENMEM_add_to_physmap, &xatp))
|
||||||
BUG();
|
BUG();
|
||||||
|
|
||||||
HYPERVISOR_shared_info = (struct shared_info *)shared_info_page;
|
}
|
||||||
|
static void __init xen_hvm_set_shared_info(struct shared_info *sip)
|
||||||
|
{
|
||||||
|
int cpu;
|
||||||
|
|
||||||
|
HYPERVISOR_shared_info = sip;
|
||||||
|
|
||||||
/* xen_vcpu is a pointer to the vcpu_info struct in the shared_info
|
/* xen_vcpu is a pointer to the vcpu_info struct in the shared_info
|
||||||
* page, we use it in the event channel upcall and in some pvclock
|
* page, we use it in the event channel upcall and in some pvclock
|
||||||
* related functions. We don't need the vcpu_info placement
|
* related functions. We don't need the vcpu_info placement
|
||||||
* optimizations because we don't use any pv_mmu or pv_irq op on
|
* optimizations because we don't use any pv_mmu or pv_irq op on
|
||||||
* HVM.
|
* HVM. */
|
||||||
* When xen_hvm_init_shared_info is run at boot time only vcpu 0 is
|
for_each_online_cpu(cpu)
|
||||||
* online but xen_hvm_init_shared_info is run at resume time too and
|
|
||||||
* in that case multiple vcpus might be online. */
|
|
||||||
for_each_online_cpu(cpu) {
|
|
||||||
per_cpu(xen_vcpu, cpu) = &HYPERVISOR_shared_info->vcpu_info[cpu];
|
per_cpu(xen_vcpu, cpu) = &HYPERVISOR_shared_info->vcpu_info[cpu];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_XEN_PVHVM
|
/* Reconnect the shared_info pfn to a (new) mfn */
|
||||||
|
void xen_hvm_resume_shared_info(void)
|
||||||
|
{
|
||||||
|
xen_hvm_connect_shared_info(xen_hvm_sip_phys >> PAGE_SHIFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Xen tools prior to Xen 4 do not provide a E820_Reserved area for guest usage.
|
||||||
|
* On these old tools the shared info page will be placed in E820_Ram.
|
||||||
|
* Xen 4 provides a E820_Reserved area at 0xFC000000, and this code expects
|
||||||
|
* that nothing is mapped up to HVM_SHARED_INFO_ADDR.
|
||||||
|
* Xen 4.3+ provides an explicit 1MB area at HVM_SHARED_INFO_ADDR which is used
|
||||||
|
* here for the shared info page. */
|
||||||
|
static void __init xen_hvm_init_shared_info(void)
|
||||||
|
{
|
||||||
|
if (xen_major < 4) {
|
||||||
|
xen_hvm_shared_info = extend_brk(PAGE_SIZE, PAGE_SIZE);
|
||||||
|
xen_hvm_sip_phys = __pa(xen_hvm_shared_info);
|
||||||
|
} else {
|
||||||
|
xen_hvm_sip_phys = HVM_SHARED_INFO_ADDR;
|
||||||
|
set_fixmap(FIX_PARAVIRT_BOOTMAP, xen_hvm_sip_phys);
|
||||||
|
xen_hvm_shared_info =
|
||||||
|
(struct shared_info *)fix_to_virt(FIX_PARAVIRT_BOOTMAP);
|
||||||
|
}
|
||||||
|
xen_hvm_connect_shared_info(xen_hvm_sip_phys >> PAGE_SHIFT);
|
||||||
|
xen_hvm_set_shared_info(xen_hvm_shared_info);
|
||||||
|
}
|
||||||
|
|
||||||
static void __init init_hvm_pv_info(void)
|
static void __init init_hvm_pv_info(void)
|
||||||
{
|
{
|
||||||
int major, minor;
|
|
||||||
uint32_t eax, ebx, ecx, edx, pages, msr, base;
|
uint32_t eax, ebx, ecx, edx, pages, msr, base;
|
||||||
u64 pfn;
|
u64 pfn;
|
||||||
|
|
||||||
base = xen_cpuid_base();
|
base = xen_cpuid_base();
|
||||||
cpuid(base + 1, &eax, &ebx, &ecx, &edx);
|
|
||||||
|
|
||||||
major = eax >> 16;
|
|
||||||
minor = eax & 0xffff;
|
|
||||||
printk(KERN_INFO "Xen version %d.%d.\n", major, minor);
|
|
||||||
|
|
||||||
cpuid(base + 2, &pages, &msr, &ecx, &edx);
|
cpuid(base + 2, &pages, &msr, &ecx, &edx);
|
||||||
|
|
||||||
pfn = __pa(hypercall_page);
|
pfn = __pa(hypercall_page);
|
||||||
|
@ -1590,12 +1611,22 @@ static void __init xen_hvm_guest_init(void)
|
||||||
|
|
||||||
static bool __init xen_hvm_platform(void)
|
static bool __init xen_hvm_platform(void)
|
||||||
{
|
{
|
||||||
|
uint32_t eax, ebx, ecx, edx, base;
|
||||||
|
|
||||||
if (xen_pv_domain())
|
if (xen_pv_domain())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!xen_cpuid_base())
|
base = xen_cpuid_base();
|
||||||
|
if (!base)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
cpuid(base + 1, &eax, &ebx, &ecx, &edx);
|
||||||
|
|
||||||
|
xen_major = eax >> 16;
|
||||||
|
xen_minor = eax & 0xffff;
|
||||||
|
|
||||||
|
printk(KERN_INFO "Xen version %d.%d.\n", xen_major, xen_minor);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ void xen_arch_hvm_post_suspend(int suspend_cancelled)
|
||||||
{
|
{
|
||||||
#ifdef CONFIG_XEN_PVHVM
|
#ifdef CONFIG_XEN_PVHVM
|
||||||
int cpu;
|
int cpu;
|
||||||
xen_hvm_init_shared_info();
|
xen_hvm_resume_shared_info();
|
||||||
xen_callback_vector();
|
xen_callback_vector();
|
||||||
xen_unplug_emulated_devices();
|
xen_unplug_emulated_devices();
|
||||||
if (xen_feature(XENFEAT_hvm_safe_pvclock)) {
|
if (xen_feature(XENFEAT_hvm_safe_pvclock)) {
|
||||||
|
|
|
@ -40,7 +40,7 @@ void xen_enable_syscall(void);
|
||||||
void xen_vcpu_restore(void);
|
void xen_vcpu_restore(void);
|
||||||
|
|
||||||
void xen_callback_vector(void);
|
void xen_callback_vector(void);
|
||||||
void xen_hvm_init_shared_info(void);
|
void xen_hvm_resume_shared_info(void);
|
||||||
void xen_unplug_emulated_devices(void);
|
void xen_unplug_emulated_devices(void);
|
||||||
|
|
||||||
void __init xen_build_dynamic_phys_to_machine(void);
|
void __init xen_build_dynamic_phys_to_machine(void);
|
||||||
|
|
Loading…
Reference in a new issue