1 /******************************************************************************
2  * machine_kexec.c
3  *
4  * Copyright (C) 2013 Citrix Systems R&D Ltd.
5  *
6  * Portions derived from Linux's arch/x86/kernel/machine_kexec_64.c.
7  *
8  *   Copyright (C) 2002-2005 Eric Biederman  <ebiederm@xmission.com>
9  *
10  * Xen port written by:
11  * - Simon 'Horms' Horman <horms@verge.net.au>
12  * - Magnus Damm <magnus@valinux.co.jp>
13  *
14  * This source code is licensed under the GNU General Public License,
15  * Version 2.  See the file COPYING for more details.
16  */
17 
18 #include <xen/types.h>
19 #include <xen/kexec.h>
20 #include <xen/guest_access.h>
21 #include <asm/fixmap.h>
22 #include <asm/hpet.h>
23 #include <asm/page.h>
24 #include <asm/machine_kexec.h>
25 
26 /*
27  * Add a mapping for a page to the page tables used during kexec.
28  */
machine_kexec_add_page(struct kexec_image * image,unsigned long vaddr,unsigned long maddr)29 int machine_kexec_add_page(struct kexec_image *image, unsigned long vaddr,
30                            unsigned long maddr)
31 {
32     struct page_info *l4_page;
33     struct page_info *l3_page;
34     struct page_info *l2_page;
35     struct page_info *l1_page;
36     l4_pgentry_t *l4 = NULL;
37     l3_pgentry_t *l3 = NULL;
38     l2_pgentry_t *l2 = NULL;
39     l1_pgentry_t *l1 = NULL;
40     int ret = -ENOMEM;
41 
42     l4_page = image->aux_page;
43     if ( !l4_page )
44     {
45         l4_page = kimage_alloc_control_page(image, 0);
46         if ( !l4_page )
47             goto out;
48         image->aux_page = l4_page;
49     }
50 
51     l4 = __map_domain_page(l4_page);
52     l4 += l4_table_offset(vaddr);
53     if ( !(l4e_get_flags(*l4) & _PAGE_PRESENT) )
54     {
55         l3_page = kimage_alloc_control_page(image, 0);
56         if ( !l3_page )
57             goto out;
58         l4e_write(l4, l4e_from_page(l3_page, __PAGE_HYPERVISOR));
59     }
60     else
61         l3_page = l4e_get_page(*l4);
62 
63     l3 = __map_domain_page(l3_page);
64     l3 += l3_table_offset(vaddr);
65     if ( !(l3e_get_flags(*l3) & _PAGE_PRESENT) )
66     {
67         l2_page = kimage_alloc_control_page(image, 0);
68         if ( !l2_page )
69             goto out;
70         l3e_write(l3, l3e_from_page(l2_page, __PAGE_HYPERVISOR));
71     }
72     else
73         l2_page = l3e_get_page(*l3);
74 
75     l2 = __map_domain_page(l2_page);
76     l2 += l2_table_offset(vaddr);
77     if ( !(l2e_get_flags(*l2) & _PAGE_PRESENT) )
78     {
79         l1_page = kimage_alloc_control_page(image, 0);
80         if ( !l1_page )
81             goto out;
82         l2e_write(l2, l2e_from_page(l1_page, __PAGE_HYPERVISOR));
83     }
84     else
85         l1_page = l2e_get_page(*l2);
86 
87     l1 = __map_domain_page(l1_page);
88     l1 += l1_table_offset(vaddr);
89     l1e_write(l1, l1e_from_pfn(maddr >> PAGE_SHIFT, __PAGE_HYPERVISOR));
90 
91     ret = 0;
92 out:
93     if ( l1 )
94         unmap_domain_page(l1);
95     if ( l2 )
96         unmap_domain_page(l2);
97     if ( l3 )
98         unmap_domain_page(l3);
99     if ( l4 )
100         unmap_domain_page(l4);
101     return ret;
102 }
103 
machine_kexec_load(struct kexec_image * image)104 int machine_kexec_load(struct kexec_image *image)
105 {
106     void *code_page;
107     int ret;
108 
109     switch ( image->arch )
110     {
111     case EM_386:
112     case EM_X86_64:
113         break;
114     default:
115         return -EINVAL;
116     }
117 
118     code_page = __map_domain_page(image->control_code_page);
119     memcpy(code_page, kexec_reloc, kexec_reloc_size);
120     unmap_domain_page(code_page);
121 
122     /*
123      * Add a mapping for the control code page to the same virtual
124      * address as kexec_reloc.  This allows us to keep running after
125      * these page tables are loaded in kexec_reloc.
126      */
127     ret = machine_kexec_add_page(image, (unsigned long)kexec_reloc,
128                                  page_to_maddr(image->control_code_page));
129     if ( ret < 0 )
130         return ret;
131 
132     return 0;
133 }
134 
machine_kexec_unload(struct kexec_image * image)135 void machine_kexec_unload(struct kexec_image *image)
136 {
137     /* no-op. kimage_free() frees all control pages. */
138 }
139 
machine_reboot_kexec(struct kexec_image * image)140 void machine_reboot_kexec(struct kexec_image *image)
141 {
142     BUG_ON(smp_processor_id() != 0);
143     smp_send_stop();
144     machine_kexec(image);
145     BUG();
146 }
147 
machine_kexec(struct kexec_image * image)148 void machine_kexec(struct kexec_image *image)
149 {
150     int i;
151     unsigned long reloc_flags = 0;
152 
153     /* We are about to permenantly jump out of the Xen context into the kexec
154      * purgatory code.  We really dont want to be still servicing interupts.
155      */
156     local_irq_disable();
157 
158     /* Now regular interrupts are disabled, we need to reduce the impact
159      * of interrupts not disabled by 'cli'.
160      *
161      * The NMI handlers have already been set up nmi_shootdown_cpus().  All
162      * pcpus other than us have the nmi_crash handler, while we have the nop
163      * handler.
164      *
165      * The MCE handlers touch extensive areas of Xen code and data.  At this
166      * point, there is nothing we can usefully do, so set the nop handler.
167      */
168     for ( i = 0; i < nr_cpu_ids; i++ )
169     {
170         if ( idt_tables[i] == NULL )
171             continue;
172         _update_gate_addr_lower(&idt_tables[i][TRAP_machine_check], &trap_nop);
173     }
174 
175     /* Explicitly enable NMIs on this CPU.  Some crashdump kernels do
176      * not like running with NMIs disabled. */
177     enable_nmis();
178 
179     if ( image->arch == EM_386 )
180         reloc_flags |= KEXEC_RELOC_FLAG_COMPAT;
181 
182     kexec_reloc(page_to_maddr(image->control_code_page),
183                 page_to_maddr(image->aux_page),
184                 image->head, image->entry_maddr, reloc_flags);
185 }
186 
machine_kexec_get(xen_kexec_range_t * range)187 int machine_kexec_get(xen_kexec_range_t *range)
188 {
189 	if (range->range != KEXEC_RANGE_MA_XEN)
190 		return -EINVAL;
191 	return machine_kexec_get_xen(range);
192 }
193 
arch_crash_save_vmcoreinfo(void)194 void arch_crash_save_vmcoreinfo(void)
195 {
196 	VMCOREINFO_SYMBOL(dom_xen);
197 	VMCOREINFO_SYMBOL(dom_io);
198 
199 	VMCOREINFO_SYMBOL_ALIAS(pgd_l4, idle_pg_table);
200 }
201 
202 /*
203  * Local variables:
204  * mode: C
205  * c-file-style: "BSD"
206  * c-basic-offset: 4
207  * tab-width: 4
208  * indent-tabs-mode: nil
209  * End:
210  */
211