aboutsummaryrefslogtreecommitdiffstats
path: root/xen/arch/x86/machine_kexec.c
blob: 68b970510268359205dfe903668495cccfd0f2f1 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/******************************************************************************
 * machine_kexec.c
 *
 * Xen port written by:
 * - Simon 'Horms' Horman <horms@verge.net.au>
 * - Magnus Damm <magnus@valinux.co.jp>
 */

#include <xen/types.h>
#include <xen/kexec.h>
#include <xen/guest_access.h>
#include <asm/fixmap.h>
#include <asm/hpet.h>

typedef void (*relocate_new_kernel_t)(
                unsigned long indirection_page,
                unsigned long *page_list,
                unsigned long start_address,
                unsigned int preserve_context);

int machine_kexec_load(int type, int slot, xen_kexec_image_t *image)
{
    unsigned long prev_ma = 0;
    int fix_base = FIX_KEXEC_BASE_0 + (slot * (KEXEC_XEN_NO_PAGES >> 1));
    int k;

    /* setup fixmap to point to our pages and record the virtual address
     * in every odd index in page_list[].
     */

    for ( k = 0; k < KEXEC_XEN_NO_PAGES; k++ )
    {
        if ( (k & 1) == 0 )
        {
            /* Even pages: machine address. */
            prev_ma = image->page_list[k];
        }
        else
        {
            /* Odd pages: va for previous ma. */
            if ( is_pv_32on64_domain(dom0) )
            {
                /*
                 * The compatability bounce code sets up a page table
                 * with a 1-1 mapping of the first 1G of memory so
                 * VA==PA here.
                 *
                 * This Linux purgatory code still sets up separate
                 * high and low mappings on the control page (entries
                 * 0 and 1) but it is harmless if they are equal since
                 * that PT is not live at the time.
                 */
                image->page_list[k] = prev_ma;
            }
            else
            {
                set_fixmap(fix_base + (k >> 1), prev_ma);
                image->page_list[k] = fix_to_virt(fix_base + (k >> 1));
            }
        }
    }

    return 0;
}

void machine_kexec_unload(int type, int slot, xen_kexec_image_t *image)
{
}

void machine_reboot_kexec(xen_kexec_image_t *image)
{
    BUG_ON(smp_processor_id() != 0);
    smp_send_stop();
    machine_kexec(image);
    BUG();
}

void machine_kexec(xen_kexec_image_t *image)
{
    struct desc_ptr gdt_desc = {
        .base = (unsigned long)(boot_cpu_gdt_table - FIRST_RESERVED_GDT_ENTRY),
        .limit = LAST_RESERVED_GDT_BYTE
    };
    int i;

    /* We are about to permenantly jump out of the Xen context into the kexec
     * purgatory code.  We really dont want to be still servicing interupts.
     */
    local_irq_disable();

    /* Now regular interrupts are disabled, we need to reduce the impact
     * of interrupts not disabled by 'cli'.
     *
     * The NMI handlers have already been set up nmi_shootdown_cpus().  All
     * pcpus other than us have the nmi_crash handler, while we have the nop
     * handler.
     *
     * The MCE handlers touch extensive areas of Xen code and data.  At this
     * point, there is nothing we can usefully do, so set the nop handler.
     */
    for ( i = 0; i < nr_cpu_ids; i++ )
    {
        if ( idt_tables[i] == NULL )
            continue;
        _update_gate_addr_lower(&idt_tables[i][TRAP_machine_check], &trap_nop);
    }

    /* Explicitly enable NMIs on this CPU.  Some crashdump kernels do
     * not like running with NMIs disabled. */
    enable_nmis();

    /*
     * compat_machine_kexec() returns to idle pagetables, which requires us
     * to be running on a static GDT mapping (idle pagetables have no GDT
     * mappings in their per-domain mapping area).
     */
    asm volatile ( "lgdt %0" : : "m" (gdt_desc) );

    if ( is_pv_32on64_domain(dom0) )
    {
        compat_machine_kexec(image->page_list[1],
                             image->indirection_page,
                             image->page_list,
                             image->start_address);
    }
    else
    {
        relocate_new_kernel_t rnk;

        rnk = (relocate_new_kernel_t) image->page_list[1];
        (*rnk)(image->indirection_page, image->page_list,
               image->start_address,
               0 /* preserve_context */);
    }
}

int machine_kexec_get(xen_kexec_range_t *range)
{
	if (range->range != KEXEC_RANGE_MA_XEN)
		return -EINVAL;
	return machine_kexec_get_xen(range);
}

void arch_crash_save_vmcoreinfo(void)
{
	VMCOREINFO_SYMBOL(dom_xen);
	VMCOREINFO_SYMBOL(dom_io);

	VMCOREINFO_SYMBOL_ALIAS(pgd_l4, idle_pg_table);
}

/*
 * Local variables:
 * mode: C
 * c-file-style: "BSD"
 * c-basic-offset: 4
 * tab-width: 4
 * indent-tabs-mode: nil
 * End:
 */