1*e02c57ffSJustin Hibbits /*-
2*e02c57ffSJustin Hibbits * SPDX-License-Identifier: BSD-2-Clause
3*e02c57ffSJustin Hibbits *
4*e02c57ffSJustin Hibbits * Copyright (c) 2025 Juniper Networks, Inc.
5*e02c57ffSJustin Hibbits * All rights reserved.
6*e02c57ffSJustin Hibbits *
7*e02c57ffSJustin Hibbits * Redistribution and use in source and binary forms, with or without
8*e02c57ffSJustin Hibbits * modification, are permitted provided that the following conditions
9*e02c57ffSJustin Hibbits * are met:
10*e02c57ffSJustin Hibbits * 1. Redistributions of source code must retain the above copyright
11*e02c57ffSJustin Hibbits * notice, this list of conditions and the following disclaimer.
12*e02c57ffSJustin Hibbits * 2. Redistributions in binary form must reproduce the above copyright
13*e02c57ffSJustin Hibbits * notice, this list of conditions and the following disclaimer in the
14*e02c57ffSJustin Hibbits * documentation and/or other materials provided with the distribution.
15*e02c57ffSJustin Hibbits *
16*e02c57ffSJustin Hibbits * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17*e02c57ffSJustin Hibbits * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18*e02c57ffSJustin Hibbits * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19*e02c57ffSJustin Hibbits * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20*e02c57ffSJustin Hibbits * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21*e02c57ffSJustin Hibbits * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22*e02c57ffSJustin Hibbits * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23*e02c57ffSJustin Hibbits * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24*e02c57ffSJustin Hibbits * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25*e02c57ffSJustin Hibbits * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26*e02c57ffSJustin Hibbits * SUCH DAMAGE.
27*e02c57ffSJustin Hibbits */
28*e02c57ffSJustin Hibbits
29*e02c57ffSJustin Hibbits #include <sys/param.h>
30*e02c57ffSJustin Hibbits #include <sys/bus.h>
31*e02c57ffSJustin Hibbits #include <sys/eventhandler.h>
32*e02c57ffSJustin Hibbits #include <sys/kernel.h>
33*e02c57ffSJustin Hibbits #ifdef INTRNG
34*e02c57ffSJustin Hibbits #include <sys/intr.h>
35*e02c57ffSJustin Hibbits #endif
36*e02c57ffSJustin Hibbits #include <sys/kexec.h>
37*e02c57ffSJustin Hibbits #include <sys/malloc.h>
38*e02c57ffSJustin Hibbits #include <sys/proc.h>
39*e02c57ffSJustin Hibbits #include <sys/priv.h>
40*e02c57ffSJustin Hibbits #include <sys/reboot.h>
41*e02c57ffSJustin Hibbits #include <sys/rman.h>
42*e02c57ffSJustin Hibbits #include <sys/rwlock.h>
43*e02c57ffSJustin Hibbits #include <sys/smp.h>
44*e02c57ffSJustin Hibbits #include <sys/syscallsubr.h>
45*e02c57ffSJustin Hibbits #include <sys/sysproto.h>
46*e02c57ffSJustin Hibbits
47*e02c57ffSJustin Hibbits #include <vm/vm.h>
48*e02c57ffSJustin Hibbits #include <vm/pmap.h>
49*e02c57ffSJustin Hibbits #include <vm/vm_extern.h>
50*e02c57ffSJustin Hibbits #include <vm/vm_kern.h>
51*e02c57ffSJustin Hibbits #include <vm/vm_map.h>
52*e02c57ffSJustin Hibbits #include <vm/vm_object.h>
53*e02c57ffSJustin Hibbits #include <vm/vm_page.h>
54*e02c57ffSJustin Hibbits #include <vm/vm_pagequeue.h>
55*e02c57ffSJustin Hibbits #include <vm/vm_phys.h>
56*e02c57ffSJustin Hibbits #include <vm/vm_radix.h>
57*e02c57ffSJustin Hibbits
58*e02c57ffSJustin Hibbits #include <machine/kexec.h>
59*e02c57ffSJustin Hibbits
60*e02c57ffSJustin Hibbits #ifndef KEXEC_MD_PAGES
61*e02c57ffSJustin Hibbits /*
62*e02c57ffSJustin Hibbits * Number of MD pages for extra bookkeeping.
63*e02c57ffSJustin Hibbits * This is a macro because it can be a constant (some architectures make it 0).
64*e02c57ffSJustin Hibbits * It accepts an argument, which is an array of
65*e02c57ffSJustin Hibbits * kexec_segment[KEXEC_SEGMENT_MAX].
66*e02c57ffSJustin Hibbits */
67*e02c57ffSJustin Hibbits #define KEXEC_MD_PAGES(x) 0
68*e02c57ffSJustin Hibbits #endif
69*e02c57ffSJustin Hibbits
70*e02c57ffSJustin Hibbits /*
71*e02c57ffSJustin Hibbits * Basic design:
72*e02c57ffSJustin Hibbits *
73*e02c57ffSJustin Hibbits * Given an array of "segment descriptors" stage an image to be loaded and
74*e02c57ffSJustin Hibbits * jumped to at reboot, instead of rebooting via firmware.
75*e02c57ffSJustin Hibbits *
76*e02c57ffSJustin Hibbits * Constraints:
77*e02c57ffSJustin Hibbits * - The segment descriptors' "mem" and "memsz" must each fit within a
78*e02c57ffSJustin Hibbits * vm_phys_seg segment, which can be obtained via the `vm.phys_segs` sysctl.
79*e02c57ffSJustin Hibbits * A single segment cannot span multiple vm_phys_seg segments, even if the
80*e02c57ffSJustin Hibbits * vm_phys_seg segments are adjacent.
81*e02c57ffSJustin Hibbits *
82*e02c57ffSJustin Hibbits * Technical details:
83*e02c57ffSJustin Hibbits *
84*e02c57ffSJustin Hibbits * Take advantage of the VM subsystem and create a vm_object to hold the staged
85*e02c57ffSJustin Hibbits * image. When grabbing pages for the object, sort the pages so that if a page
86*e02c57ffSJustin Hibbits * in the object is located in the physical range of any of the kexec segment
87*e02c57ffSJustin Hibbits * targets then it gets placed at the pindex corresponding to that physical
88*e02c57ffSJustin Hibbits * address. This avoids the chance of corruption by writing over the page in
89*e02c57ffSJustin Hibbits * the final copy, or the need for a copy buffer page.
90*e02c57ffSJustin Hibbits */
91*e02c57ffSJustin Hibbits
92*e02c57ffSJustin Hibbits static struct kexec_image staged_image;
93*e02c57ffSJustin Hibbits static vm_offset_t stage_addr;
94*e02c57ffSJustin Hibbits static vm_object_t kexec_obj;
95*e02c57ffSJustin Hibbits
96*e02c57ffSJustin Hibbits static eventhandler_tag kexec_reboot_handler;
97*e02c57ffSJustin Hibbits static struct mtx kexec_mutex;
98*e02c57ffSJustin Hibbits
99*e02c57ffSJustin Hibbits static MALLOC_DEFINE(M_KEXEC, "kexec", "Kexec segments");
100*e02c57ffSJustin Hibbits
101*e02c57ffSJustin Hibbits
102*e02c57ffSJustin Hibbits static void
kexec_reboot(void * junk __unused,int howto)103*e02c57ffSJustin Hibbits kexec_reboot(void *junk __unused, int howto)
104*e02c57ffSJustin Hibbits {
105*e02c57ffSJustin Hibbits if ((howto & RB_KEXEC) == 0 || kexec_obj == NULL)
106*e02c57ffSJustin Hibbits return;
107*e02c57ffSJustin Hibbits
108*e02c57ffSJustin Hibbits #ifdef SMP
109*e02c57ffSJustin Hibbits cpu_mp_stop();
110*e02c57ffSJustin Hibbits #endif /* SMP */
111*e02c57ffSJustin Hibbits intr_disable();
112*e02c57ffSJustin Hibbits printf("Starting kexec reboot\n");
113*e02c57ffSJustin Hibbits
114*e02c57ffSJustin Hibbits scheduler_stopped = true;
115*e02c57ffSJustin Hibbits kexec_reboot_md(&staged_image);
116*e02c57ffSJustin Hibbits }
117*e02c57ffSJustin Hibbits
118*e02c57ffSJustin Hibbits MTX_SYSINIT(kexec_mutex, &kexec_mutex, "kexec", MTX_DEF);
119*e02c57ffSJustin Hibbits
120*e02c57ffSJustin Hibbits /* Sort the segment list once copied in */
121*e02c57ffSJustin Hibbits static int
seg_cmp(const void * seg1,const void * seg2)122*e02c57ffSJustin Hibbits seg_cmp(const void *seg1, const void *seg2)
123*e02c57ffSJustin Hibbits {
124*e02c57ffSJustin Hibbits const struct kexec_segment *s1, *s2;
125*e02c57ffSJustin Hibbits
126*e02c57ffSJustin Hibbits s1 = seg1;
127*e02c57ffSJustin Hibbits s2 = seg2;
128*e02c57ffSJustin Hibbits
129*e02c57ffSJustin Hibbits return ((uintptr_t)s1->mem - (uintptr_t)s2->mem);
130*e02c57ffSJustin Hibbits }
131*e02c57ffSJustin Hibbits
132*e02c57ffSJustin Hibbits static bool
segment_fits(struct kexec_segment * seg)133*e02c57ffSJustin Hibbits segment_fits(struct kexec_segment *seg)
134*e02c57ffSJustin Hibbits {
135*e02c57ffSJustin Hibbits vm_paddr_t v = (vm_paddr_t)(uintptr_t)seg->mem;
136*e02c57ffSJustin Hibbits
137*e02c57ffSJustin Hibbits for (int i = 0; i < vm_phys_nsegs; i++) {
138*e02c57ffSJustin Hibbits if (v >= vm_phys_segs[i].start &&
139*e02c57ffSJustin Hibbits (v + seg->memsz - 1) <= vm_phys_segs[i].end)
140*e02c57ffSJustin Hibbits return (true);
141*e02c57ffSJustin Hibbits }
142*e02c57ffSJustin Hibbits
143*e02c57ffSJustin Hibbits return (false);
144*e02c57ffSJustin Hibbits }
145*e02c57ffSJustin Hibbits
146*e02c57ffSJustin Hibbits static vm_paddr_t
pa_for_pindex(struct kexec_segment_stage * segs,int count,vm_pindex_t pind)147*e02c57ffSJustin Hibbits pa_for_pindex(struct kexec_segment_stage *segs, int count, vm_pindex_t pind)
148*e02c57ffSJustin Hibbits {
149*e02c57ffSJustin Hibbits for (int i = count; i > 0; --i) {
150*e02c57ffSJustin Hibbits if (pind >= segs[i - 1].pindex)
151*e02c57ffSJustin Hibbits return (ptoa(pind - segs[i-1].pindex) + segs[i - 1].target);
152*e02c57ffSJustin Hibbits }
153*e02c57ffSJustin Hibbits
154*e02c57ffSJustin Hibbits panic("No segment for pindex %ju\n", (uintmax_t)pind);
155*e02c57ffSJustin Hibbits }
156*e02c57ffSJustin Hibbits
157*e02c57ffSJustin Hibbits /*
158*e02c57ffSJustin Hibbits * For now still tied to the system call, so assumes all memory is userspace.
159*e02c57ffSJustin Hibbits */
160*e02c57ffSJustin Hibbits int
kern_kexec_load(struct thread * td,u_long entry,u_long nseg,struct kexec_segment * seg,u_long flags)161*e02c57ffSJustin Hibbits kern_kexec_load(struct thread *td, u_long entry, u_long nseg,
162*e02c57ffSJustin Hibbits struct kexec_segment *seg, u_long flags)
163*e02c57ffSJustin Hibbits {
164*e02c57ffSJustin Hibbits static int kexec_loading;
165*e02c57ffSJustin Hibbits struct kexec_segment segtmp[KEXEC_SEGMENT_MAX];
166*e02c57ffSJustin Hibbits struct kexec_image *new_image_stage = 0;
167*e02c57ffSJustin Hibbits vm_object_t new_segments = NULL;
168*e02c57ffSJustin Hibbits uint8_t *buf;
169*e02c57ffSJustin Hibbits int err = 0;
170*e02c57ffSJustin Hibbits int i;
171*e02c57ffSJustin Hibbits const size_t segsize = nseg * sizeof(struct kexec_segment);
172*e02c57ffSJustin Hibbits vm_page_t *page_list = 0;
173*e02c57ffSJustin Hibbits vm_size_t image_count, md_pages, page_count, tmpsize;
174*e02c57ffSJustin Hibbits vm_offset_t segment_va = 0;
175*e02c57ffSJustin Hibbits /*
176*e02c57ffSJustin Hibbits * - Do any sanity checking
177*e02c57ffSJustin Hibbits * - Load the new segments to temporary
178*e02c57ffSJustin Hibbits * - Remove the old segments
179*e02c57ffSJustin Hibbits * - Install the new segments
180*e02c57ffSJustin Hibbits */
181*e02c57ffSJustin Hibbits
182*e02c57ffSJustin Hibbits if (nseg > KEXEC_SEGMENT_MAX)
183*e02c57ffSJustin Hibbits return (EINVAL);
184*e02c57ffSJustin Hibbits
185*e02c57ffSJustin Hibbits if (atomic_cmpset_acq_int(&kexec_loading, false, true) == 0)
186*e02c57ffSJustin Hibbits return (EBUSY);
187*e02c57ffSJustin Hibbits
188*e02c57ffSJustin Hibbits /* Only do error checking if we're installing new segments. */
189*e02c57ffSJustin Hibbits if (nseg > 0) {
190*e02c57ffSJustin Hibbits /* Create the new kexec object before destroying the old one. */
191*e02c57ffSJustin Hibbits bzero(&segtmp, sizeof(segtmp));
192*e02c57ffSJustin Hibbits err = copyin(seg, segtmp, segsize);
193*e02c57ffSJustin Hibbits if (err != 0)
194*e02c57ffSJustin Hibbits goto out;
195*e02c57ffSJustin Hibbits qsort(segtmp, nseg, sizeof(*segtmp), seg_cmp);
196*e02c57ffSJustin Hibbits new_image_stage = malloc(sizeof(*new_image_stage), M_TEMP, M_WAITOK | M_ZERO);
197*e02c57ffSJustin Hibbits /*
198*e02c57ffSJustin Hibbits * Sanity checking:
199*e02c57ffSJustin Hibbits * - All segments must not overlap the kernel, so must be fully enclosed
200*e02c57ffSJustin Hibbits * in a vm_phys_seg (each kexec segment must be in a single
201*e02c57ffSJustin Hibbits * vm_phys_seg segment, cannot cross even adjacent segments).
202*e02c57ffSJustin Hibbits */
203*e02c57ffSJustin Hibbits image_count = 0;
204*e02c57ffSJustin Hibbits for (i = 0; i < nseg; i++) {
205*e02c57ffSJustin Hibbits if (!segment_fits(&segtmp[i]) ||
206*e02c57ffSJustin Hibbits segtmp[i].bufsz > segtmp[i].memsz) {
207*e02c57ffSJustin Hibbits err = EINVAL;
208*e02c57ffSJustin Hibbits goto out;
209*e02c57ffSJustin Hibbits }
210*e02c57ffSJustin Hibbits new_image_stage->segments[i].pindex = image_count;
211*e02c57ffSJustin Hibbits new_image_stage->segments[i].target = (vm_offset_t)segtmp[i].mem;
212*e02c57ffSJustin Hibbits new_image_stage->segments[i].size = segtmp[i].memsz;
213*e02c57ffSJustin Hibbits image_count += atop(segtmp[i].memsz);
214*e02c57ffSJustin Hibbits }
215*e02c57ffSJustin Hibbits md_pages = KEXEC_MD_PAGES(segtmp);
216*e02c57ffSJustin Hibbits page_count = image_count + md_pages;
217*e02c57ffSJustin Hibbits new_segments = vm_object_allocate(OBJT_PHYS, page_count);
218*e02c57ffSJustin Hibbits page_list = malloc(page_count * sizeof(vm_page_t), M_TEMP, M_WAITOK);
219*e02c57ffSJustin Hibbits
220*e02c57ffSJustin Hibbits /*
221*e02c57ffSJustin Hibbits * - Grab all pages for all segments (use pindex to slice it)
222*e02c57ffSJustin Hibbits * - Walk the list (once)
223*e02c57ffSJustin Hibbits * - At each pindex, check if the target PA that corresponds
224*e02c57ffSJustin Hibbits * to that index is in the object. If so, swap the pages.
225*e02c57ffSJustin Hibbits * - At the end of this the list will be "best" sorted.
226*e02c57ffSJustin Hibbits */
227*e02c57ffSJustin Hibbits vm_page_grab_pages_unlocked(new_segments, 0,
228*e02c57ffSJustin Hibbits VM_ALLOC_NORMAL | VM_ALLOC_WAITOK | VM_ALLOC_WIRED | VM_ALLOC_NOBUSY | VM_ALLOC_ZERO,
229*e02c57ffSJustin Hibbits page_list, page_count);
230*e02c57ffSJustin Hibbits
231*e02c57ffSJustin Hibbits /* Sort the pages to best match the PA */
232*e02c57ffSJustin Hibbits VM_OBJECT_WLOCK(new_segments);
233*e02c57ffSJustin Hibbits for (i = 0; i < image_count; i++) {
234*e02c57ffSJustin Hibbits vm_page_t curpg, otherpg, tmp;
235*e02c57ffSJustin Hibbits vm_pindex_t otheridx;
236*e02c57ffSJustin Hibbits
237*e02c57ffSJustin Hibbits curpg = page_list[i];
238*e02c57ffSJustin Hibbits otherpg = PHYS_TO_VM_PAGE(pa_for_pindex(new_image_stage->segments,
239*e02c57ffSJustin Hibbits nseg, curpg->pindex));
240*e02c57ffSJustin Hibbits otheridx = otherpg->pindex;
241*e02c57ffSJustin Hibbits
242*e02c57ffSJustin Hibbits if (otherpg->object == new_segments) {
243*e02c57ffSJustin Hibbits /*
244*e02c57ffSJustin Hibbits * Swap 'curpg' and 'otherpg', since 'otherpg'
245*e02c57ffSJustin Hibbits * is at the PA 'curpg' covers.
246*e02c57ffSJustin Hibbits */
247*e02c57ffSJustin Hibbits vm_radix_remove(&new_segments->rtree, otheridx);
248*e02c57ffSJustin Hibbits vm_radix_remove(&new_segments->rtree, i);
249*e02c57ffSJustin Hibbits otherpg->pindex = i;
250*e02c57ffSJustin Hibbits curpg->pindex = otheridx;
251*e02c57ffSJustin Hibbits vm_radix_insert(&new_segments->rtree, curpg);
252*e02c57ffSJustin Hibbits vm_radix_insert(&new_segments->rtree, otherpg);
253*e02c57ffSJustin Hibbits tmp = curpg;
254*e02c57ffSJustin Hibbits page_list[i] = otherpg;
255*e02c57ffSJustin Hibbits page_list[otheridx] = tmp;
256*e02c57ffSJustin Hibbits }
257*e02c57ffSJustin Hibbits }
258*e02c57ffSJustin Hibbits for (i = 0; i < nseg; i++) {
259*e02c57ffSJustin Hibbits new_image_stage->segments[i].first_page =
260*e02c57ffSJustin Hibbits vm_radix_lookup(&new_segments->rtree,
261*e02c57ffSJustin Hibbits new_image_stage->segments[i].pindex);
262*e02c57ffSJustin Hibbits }
263*e02c57ffSJustin Hibbits if (md_pages > 0)
264*e02c57ffSJustin Hibbits new_image_stage->first_md_page =
265*e02c57ffSJustin Hibbits vm_radix_lookup(&new_segments->rtree,
266*e02c57ffSJustin Hibbits page_count - md_pages);
267*e02c57ffSJustin Hibbits else
268*e02c57ffSJustin Hibbits new_image_stage->first_md_page = NULL;
269*e02c57ffSJustin Hibbits VM_OBJECT_WUNLOCK(new_segments);
270*e02c57ffSJustin Hibbits
271*e02c57ffSJustin Hibbits /* Map the object to do the copies */
272*e02c57ffSJustin Hibbits err = vm_map_find(kernel_map, new_segments, 0, &segment_va,
273*e02c57ffSJustin Hibbits ptoa(page_count), 0, VMFS_ANY_SPACE,
274*e02c57ffSJustin Hibbits VM_PROT_RW, VM_PROT_RW, MAP_PREFAULT);
275*e02c57ffSJustin Hibbits if (err != 0)
276*e02c57ffSJustin Hibbits goto out;
277*e02c57ffSJustin Hibbits buf = (void *)segment_va;
278*e02c57ffSJustin Hibbits new_image_stage->map_addr = segment_va;
279*e02c57ffSJustin Hibbits new_image_stage->map_size = ptoa(new_segments->size);
280*e02c57ffSJustin Hibbits new_image_stage->entry = entry;
281*e02c57ffSJustin Hibbits new_image_stage->map_obj = new_segments;
282*e02c57ffSJustin Hibbits for (i = 0; i < nseg; i++) {
283*e02c57ffSJustin Hibbits err = copyin(segtmp[i].buf, buf, segtmp[i].bufsz);
284*e02c57ffSJustin Hibbits if (err != 0) {
285*e02c57ffSJustin Hibbits goto out;
286*e02c57ffSJustin Hibbits }
287*e02c57ffSJustin Hibbits new_image_stage->segments[i].map_buf = buf;
288*e02c57ffSJustin Hibbits buf += segtmp[i].bufsz;
289*e02c57ffSJustin Hibbits tmpsize = segtmp[i].memsz - segtmp[i].bufsz;
290*e02c57ffSJustin Hibbits if (tmpsize > 0)
291*e02c57ffSJustin Hibbits memset(buf, 0, tmpsize);
292*e02c57ffSJustin Hibbits buf += tmpsize;
293*e02c57ffSJustin Hibbits }
294*e02c57ffSJustin Hibbits /* What's left are the MD pages, so zero them all out. */
295*e02c57ffSJustin Hibbits if (md_pages > 0)
296*e02c57ffSJustin Hibbits bzero(buf, ptoa(md_pages));
297*e02c57ffSJustin Hibbits
298*e02c57ffSJustin Hibbits cpu_flush_dcache((void *)segment_va, ptoa(page_count));
299*e02c57ffSJustin Hibbits if ((err = kexec_load_md(new_image_stage)) != 0)
300*e02c57ffSJustin Hibbits goto out;
301*e02c57ffSJustin Hibbits }
302*e02c57ffSJustin Hibbits if (kexec_obj != NULL) {
303*e02c57ffSJustin Hibbits vm_object_unwire(kexec_obj, 0, kexec_obj->size, 0);
304*e02c57ffSJustin Hibbits KASSERT(stage_addr != 0, ("Mapped kexec_obj without address"));
305*e02c57ffSJustin Hibbits vm_map_remove(kernel_map, stage_addr, stage_addr + kexec_obj->size);
306*e02c57ffSJustin Hibbits }
307*e02c57ffSJustin Hibbits kexec_obj = new_segments;
308*e02c57ffSJustin Hibbits bzero(&staged_image, sizeof(staged_image));
309*e02c57ffSJustin Hibbits if (nseg > 0)
310*e02c57ffSJustin Hibbits memcpy(&staged_image, new_image_stage, sizeof(*new_image_stage));
311*e02c57ffSJustin Hibbits
312*e02c57ffSJustin Hibbits printf("trampoline at %#jx\n", (uintmax_t)staged_image.entry);
313*e02c57ffSJustin Hibbits if (nseg > 0) {
314*e02c57ffSJustin Hibbits if (kexec_reboot_handler == NULL)
315*e02c57ffSJustin Hibbits kexec_reboot_handler =
316*e02c57ffSJustin Hibbits EVENTHANDLER_REGISTER(shutdown_final, kexec_reboot, NULL,
317*e02c57ffSJustin Hibbits SHUTDOWN_PRI_DEFAULT - 150);
318*e02c57ffSJustin Hibbits } else {
319*e02c57ffSJustin Hibbits if (kexec_reboot_handler != NULL)
320*e02c57ffSJustin Hibbits EVENTHANDLER_DEREGISTER(shutdown_final, kexec_reboot_handler);
321*e02c57ffSJustin Hibbits }
322*e02c57ffSJustin Hibbits out:
323*e02c57ffSJustin Hibbits /* Clean up the mess if we've gotten far. */
324*e02c57ffSJustin Hibbits if (err != 0 && new_segments != NULL) {
325*e02c57ffSJustin Hibbits vm_object_unwire(new_segments, 0, new_segments->size, 0);
326*e02c57ffSJustin Hibbits if (segment_va != 0)
327*e02c57ffSJustin Hibbits vm_map_remove(kernel_map, segment_va, segment_va + kexec_obj->size);
328*e02c57ffSJustin Hibbits else
329*e02c57ffSJustin Hibbits vm_object_deallocate(new_segments);
330*e02c57ffSJustin Hibbits }
331*e02c57ffSJustin Hibbits atomic_store_rel_int(&kexec_loading, false);
332*e02c57ffSJustin Hibbits if (new_image_stage != NULL)
333*e02c57ffSJustin Hibbits free(new_image_stage, M_TEMP);
334*e02c57ffSJustin Hibbits if (page_list != 0)
335*e02c57ffSJustin Hibbits free(page_list, M_TEMP);
336*e02c57ffSJustin Hibbits
337*e02c57ffSJustin Hibbits return (err);
338*e02c57ffSJustin Hibbits }
339*e02c57ffSJustin Hibbits
340*e02c57ffSJustin Hibbits int
sys_kexec_load(struct thread * td,struct kexec_load_args * uap)341*e02c57ffSJustin Hibbits sys_kexec_load(struct thread *td, struct kexec_load_args *uap)
342*e02c57ffSJustin Hibbits {
343*e02c57ffSJustin Hibbits int error;
344*e02c57ffSJustin Hibbits
345*e02c57ffSJustin Hibbits // FIXME: Do w need a better privilege check than PRIV_REBOOT here?
346*e02c57ffSJustin Hibbits error = priv_check(td, PRIV_REBOOT);
347*e02c57ffSJustin Hibbits if (error != 0)
348*e02c57ffSJustin Hibbits return (error);
349*e02c57ffSJustin Hibbits return (kern_kexec_load(td, uap->entry, uap->nseg, uap->segments, uap->flags));
350*e02c57ffSJustin Hibbits }
351