1 /* $OpenBSD: hibernate_machdep.c,v 1.62 2024/06/19 13:27:26 jsg Exp $ */
2
3 /*
4 * Copyright (c) 2011 Mike Larkin <mlarkin@openbsd.org>
5 *
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 */
18
19 #include <sys/param.h>
20 #include <sys/systm.h>
21 #include <sys/device.h>
22 #include <sys/disk.h>
23 #include <sys/disklabel.h>
24 #include <sys/hibernate.h>
25
26 #include <uvm/uvm_extern.h>
27
28 #include <machine/biosvar.h>
29 #include <machine/hibernate.h>
30 #include <machine/kcore.h>
31 #include <machine/pmap.h>
32
33 #ifdef MULTIPROCESSOR
34 #include <machine/mpbiosvar.h>
35 #endif /* MULTIPROCESSOR */
36
37 #include <dev/acpi/acpivar.h>
38
39 #include "acpi.h"
40 #include "wd.h"
41 #include "ahci.h"
42 #include "softraid.h"
43 #include "sd.h"
44 #include "sdmmc.h"
45
46 /* Hibernate support */
47 void hibernate_enter_resume_4k_pte(vaddr_t, paddr_t);
48 void hibernate_enter_resume_4k_pde(vaddr_t);
49 void hibernate_enter_resume_4m_pde(vaddr_t, paddr_t);
50
51 extern caddr_t start, end;
52 extern int ndumpmem;
53 extern struct dumpmem dumpmem[];
54 extern bios_memmap_t *bios_memmap;
55
56 /*
57 * Hibernate always uses non-PAE page tables during resume, so
58 * redefine masks and pt_entry_t sizes in case PAE is in use.
59 */
60 #define PAGE_MASK_L2 (NBPD - 1)
61 typedef uint32_t pt_entry_t;
62
63 /*
64 * i386 MD Hibernate functions
65 *
66 * see i386 hibernate.h for lowmem layout used during hibernate
67 */
68
69 /*
70 * Returns the hibernate write I/O function to use on this machine
71 */
72 hibio_fn
get_hibernate_io_function(dev_t dev)73 get_hibernate_io_function(dev_t dev)
74 {
75 char *blkname = findblkname(major(dev));
76
77 if (blkname == NULL)
78 return NULL;
79
80 #if NWD > 0
81 if (strcmp(blkname, "wd") == 0) {
82 extern int wd_hibernate_io(dev_t dev, daddr_t blkno,
83 vaddr_t addr, size_t size, int op, void *page);
84 return wd_hibernate_io;
85 }
86 #endif
87 #if NSD > 0
88 if (strcmp(blkname, "sd") == 0) {
89 extern struct cfdriver sd_cd;
90 extern int ahci_hibernate_io(dev_t dev, daddr_t blkno,
91 vaddr_t addr, size_t size, int op, void *page);
92 extern int sr_hibernate_io(dev_t dev, daddr_t blkno,
93 vaddr_t addr, size_t size, int op, void *page);
94 extern int sdmmc_scsi_hibernate_io(dev_t dev, daddr_t blkno,
95 vaddr_t addr, size_t size, int op, void *page);
96 struct device *dv = disk_lookup(&sd_cd, DISKUNIT(dev));
97 struct {
98 const char *driver;
99 hibio_fn io_func;
100 } sd_io_funcs[] = {
101 #if NAHCI > 0
102 { "ahci", ahci_hibernate_io },
103 #endif
104 #if NSOFTRAID > 0
105 { "softraid", sr_hibernate_io },
106 #endif
107 #if NSDMMC > 0
108 { "sdmmc", sdmmc_scsi_hibernate_io },
109 #endif
110 };
111
112 if (dv && dv->dv_parent && dv->dv_parent->dv_parent) {
113 const char *driver = dv->dv_parent->dv_parent->dv_cfdata->
114 cf_driver->cd_name;
115 int i;
116
117 for (i = 0; i < nitems(sd_io_funcs); i++) {
118 if (strcmp(driver, sd_io_funcs[i].driver) == 0)
119 return sd_io_funcs[i].io_func;
120 }
121 }
122 }
123 #endif /* NSD > 0 */
124 return NULL;
125 }
126
127 /*
128 * Gather MD-specific data and store into hiber_info
129 */
130 int
get_hibernate_info_md(union hibernate_info * hiber_info)131 get_hibernate_info_md(union hibernate_info *hiber_info)
132 {
133 int i;
134 bios_memmap_t *bmp;
135
136 /* Calculate memory ranges */
137 hiber_info->nranges = ndumpmem;
138 hiber_info->image_size = 0;
139
140 for (i = 0; i < ndumpmem; i++) {
141 hiber_info->ranges[i].base = dumpmem[i].start * PAGE_SIZE;
142 hiber_info->ranges[i].end = dumpmem[i].end * PAGE_SIZE;
143 hiber_info->image_size += hiber_info->ranges[i].end -
144 hiber_info->ranges[i].base;
145 }
146
147 /* Record lowmem PTP page */
148 if (hiber_info->nranges >= nitems(hiber_info->ranges))
149 return (1);
150 hiber_info->ranges[hiber_info->nranges].base = PTP0_PA;
151 hiber_info->ranges[hiber_info->nranges].end =
152 hiber_info->ranges[hiber_info->nranges].base + PAGE_SIZE;
153 hiber_info->image_size += PAGE_SIZE;
154 hiber_info->nranges++;
155
156 #if NACPI > 0
157 /* Record ACPI trampoline code page */
158 if (hiber_info->nranges >= nitems(hiber_info->ranges))
159 return (1);
160 hiber_info->ranges[hiber_info->nranges].base = ACPI_TRAMPOLINE;
161 hiber_info->ranges[hiber_info->nranges].end =
162 hiber_info->ranges[hiber_info->nranges].base + PAGE_SIZE;
163 hiber_info->image_size += PAGE_SIZE;
164 hiber_info->nranges++;
165
166 /* Record ACPI trampoline data page */
167 if (hiber_info->nranges >= nitems(hiber_info->ranges))
168 return (1);
169 hiber_info->ranges[hiber_info->nranges].base = ACPI_TRAMP_DATA;
170 hiber_info->ranges[hiber_info->nranges].end =
171 hiber_info->ranges[hiber_info->nranges].base + PAGE_SIZE;
172 hiber_info->image_size += PAGE_SIZE;
173 hiber_info->nranges++;
174 #endif
175 #ifdef MULTIPROCESSOR
176 /* Record MP trampoline code page */
177 if (hiber_info->nranges >= nitems(hiber_info->ranges))
178 return (1);
179 hiber_info->ranges[hiber_info->nranges].base = MP_TRAMPOLINE;
180 hiber_info->ranges[hiber_info->nranges].end =
181 hiber_info->ranges[hiber_info->nranges].base + PAGE_SIZE;
182 hiber_info->image_size += PAGE_SIZE;
183 hiber_info->nranges++;
184
185 /* Record MP trampoline data page */
186 if (hiber_info->nranges >= nitems(hiber_info->ranges))
187 return (1);
188 hiber_info->ranges[hiber_info->nranges].base = MP_TRAMP_DATA;
189 hiber_info->ranges[hiber_info->nranges].end =
190 hiber_info->ranges[hiber_info->nranges].base + PAGE_SIZE;
191 hiber_info->image_size += PAGE_SIZE;
192 hiber_info->nranges++;
193 #endif /* MULTIPROCESSOR */
194
195 for (bmp = bios_memmap; bmp->type != BIOS_MAP_END; bmp++) {
196 /* Skip non-NVS ranges (already processed) */
197 if (bmp->type != BIOS_MAP_NVS)
198 continue;
199 if (hiber_info->nranges >= nitems(hiber_info->ranges))
200 return (1);
201
202 i = hiber_info->nranges;
203 hiber_info->ranges[i].base = round_page(bmp->addr);
204 hiber_info->ranges[i].end = trunc_page(bmp->addr + bmp->size);
205 hiber_info->image_size += hiber_info->ranges[i].end -
206 hiber_info->ranges[i].base;
207 hiber_info->nranges++;
208 }
209
210 hibernate_sort_ranges(hiber_info);
211
212 return (0);
213 }
214
215 /*
216 * Enter a mapping for va->pa in the resume pagetable, using
217 * the specified size.
218 *
219 * size : 0 if a 4KB mapping is desired
220 * 1 if a 4MB mapping is desired
221 */
222 void
hibernate_enter_resume_mapping(vaddr_t va,paddr_t pa,int size)223 hibernate_enter_resume_mapping(vaddr_t va, paddr_t pa, int size)
224 {
225 if (size)
226 return hibernate_enter_resume_4m_pde(va, pa);
227 else
228 return hibernate_enter_resume_4k_pte(va, pa);
229 }
230
231 /*
232 * Enter a 4MB PDE mapping for the supplied VA/PA into the resume-time pmap
233 */
234 void
hibernate_enter_resume_4m_pde(vaddr_t va,paddr_t pa)235 hibernate_enter_resume_4m_pde(vaddr_t va, paddr_t pa)
236 {
237 pt_entry_t *pde, npde;
238
239 pde = s4pde_4m(va);
240 npde = (pa & HIB_PD_MASK) | PG_RW | PG_V | PG_M | PG_PS;
241 *pde = npde;
242 }
243
244 /*
245 * Enter a 4KB PTE mapping for the supplied VA/PA into the resume-time pmap.
246 */
247 void
hibernate_enter_resume_4k_pte(vaddr_t va,paddr_t pa)248 hibernate_enter_resume_4k_pte(vaddr_t va, paddr_t pa)
249 {
250 pt_entry_t *pte, npte;
251
252 pte = s4pte_4k(va);
253 npte = (pa & PMAP_PA_MASK) | PG_RW | PG_V | PG_M;
254 *pte = npte;
255 }
256
257 /*
258 * Enter a 4KB PDE mapping for the supplied VA into the resume-time pmap.
259 */
260 void
hibernate_enter_resume_4k_pde(vaddr_t va)261 hibernate_enter_resume_4k_pde(vaddr_t va)
262 {
263 pt_entry_t *pde, npde;
264
265 pde = s4pde_4k(va);
266 npde = (HIBERNATE_PT_PAGE & PMAP_PA_MASK) | PG_RW | PG_V | PG_M;
267 *pde = npde;
268 }
269
270 /*
271 * Create the resume-time page table. This table maps the image(pig) area,
272 * the kernel text area, and various utility pages for use during resume,
273 * since we cannot overwrite the resuming kernel's page table during inflate
274 * and expect things to work properly.
275 */
276 void
hibernate_populate_resume_pt(union hibernate_info * hib_info,paddr_t image_start,paddr_t image_end)277 hibernate_populate_resume_pt(union hibernate_info *hib_info,
278 paddr_t image_start, paddr_t image_end)
279 {
280 int phys_page_number, i;
281 paddr_t pa;
282 vaddr_t kern_start_4m_va, kern_end_4m_va, page;
283 vaddr_t piglet_start_va, piglet_end_va;
284 struct pmap *kpm = pmap_kernel();
285
286 /* Identity map PD, PT, and stack pages */
287 pmap_kenter_pa(HIBERNATE_PT_PAGE, HIBERNATE_PT_PAGE, PROT_MASK);
288 pmap_kenter_pa(HIBERNATE_PD_PAGE, HIBERNATE_PD_PAGE, PROT_MASK);
289 pmap_kenter_pa(HIBERNATE_STACK_PAGE, HIBERNATE_STACK_PAGE, PROT_MASK);
290 pmap_activate(curproc);
291
292 bzero((caddr_t)HIBERNATE_PT_PAGE, PAGE_SIZE);
293 bzero((caddr_t)HIBERNATE_PD_PAGE, PAGE_SIZE);
294 bzero((caddr_t)HIBERNATE_STACK_PAGE, PAGE_SIZE);
295
296 /* PDE for low pages */
297 hibernate_enter_resume_4k_pde(0);
298
299 /*
300 * Identity map low physical pages.
301 * See arch/i386/include/hibernate_var.h for page ranges used here.
302 */
303 for (i = ACPI_TRAMPOLINE; i <= HIBERNATE_HIBALLOC_PAGE; i += PAGE_SIZE)
304 hibernate_enter_resume_mapping(i, i, 0);
305
306 /*
307 * Map current kernel VA range using 4M pages
308 */
309 kern_start_4m_va = (vaddr_t)&start & ~(PAGE_MASK_L2);
310 kern_end_4m_va = (vaddr_t)&end & ~(PAGE_MASK_L2);
311
312 /* i386 kernels load at 2MB phys (on the 0th 4mb page) */
313 phys_page_number = 0;
314
315 for (page = kern_start_4m_va; page <= kern_end_4m_va;
316 page += NBPD, phys_page_number++) {
317 pa = (paddr_t)(phys_page_number * NBPD);
318 hibernate_enter_resume_mapping(page, pa, 1);
319 }
320
321 /*
322 * Identity map the image (pig) area
323 */
324 phys_page_number = image_start / NBPD;
325 image_start &= ~(PAGE_MASK_L2);
326 image_end &= ~(PAGE_MASK_L2);
327 for (page = image_start; page <= image_end ;
328 page += NBPD, phys_page_number++) {
329 pa = (paddr_t)(phys_page_number * NBPD);
330 hibernate_enter_resume_mapping(page, pa, 1);
331 }
332
333 /*
334 * Identity map the piglet using 4MB pages.
335 */
336 phys_page_number = hib_info->piglet_pa / NBPD;
337
338 /* VA == PA */
339 piglet_start_va = hib_info->piglet_pa;
340 piglet_end_va = piglet_start_va + HIBERNATE_CHUNK_SIZE * 4;
341
342 for (page = piglet_start_va; page <= piglet_end_va;
343 page += NBPD, phys_page_number++) {
344 pa = (paddr_t)(phys_page_number * NBPD);
345 hibernate_enter_resume_mapping(page, pa, 1);
346 }
347
348 /*
349 * Fill last 8 slots of the new PD with the PAE PDPTEs of the
350 * kernel pmap, such that we can easily switch back into
351 * non-PAE mode. If we're running in non-PAE mode, this will
352 * just fill the slots with zeroes.
353 */
354 ((uint64_t *)HIBERNATE_PD_PAGE)[508] = kpm->pm_pdidx[0];
355 ((uint64_t *)HIBERNATE_PD_PAGE)[509] = kpm->pm_pdidx[1];
356 ((uint64_t *)HIBERNATE_PD_PAGE)[510] = kpm->pm_pdidx[2];
357 ((uint64_t *)HIBERNATE_PD_PAGE)[511] = kpm->pm_pdidx[3];
358
359 /* Unmap MMU pages (stack remains mapped) */
360 pmap_kremove(HIBERNATE_PT_PAGE, PAGE_SIZE);
361 pmap_kremove(HIBERNATE_PD_PAGE, PAGE_SIZE);
362 pmap_activate(curproc);
363 }
364
365 /*
366 * During inflate, certain pages that contain our bookkeeping information
367 * (eg, the chunk table, scratch pages, retguard region, etc) need to be
368 * skipped over and not inflated into.
369 *
370 * Return values:
371 * HIB_MOVE: if the physical page at dest should be moved to the retguard save
372 * region in the piglet
373 * HIB_SKIP: if the physical page at dest should be skipped
374 * 0: otherwise (no special treatment needed)
375 */
376 int
hibernate_inflate_skip(union hibernate_info * hib_info,paddr_t dest)377 hibernate_inflate_skip(union hibernate_info *hib_info, paddr_t dest)
378 {
379 extern paddr_t retguard_start_phys, retguard_end_phys;
380
381 if (dest >= hib_info->piglet_pa &&
382 dest <= (hib_info->piglet_pa + 4 * HIBERNATE_CHUNK_SIZE))
383 return (HIB_SKIP);
384
385 if (dest >= retguard_start_phys && dest <= retguard_end_phys)
386 return (HIB_MOVE);
387
388 return (0);
389 }
390
391 void
hibernate_enable_intr_machdep(void)392 hibernate_enable_intr_machdep(void)
393 {
394 intr_enable();
395 }
396
397 void
hibernate_disable_intr_machdep(void)398 hibernate_disable_intr_machdep(void)
399 {
400 intr_disable();
401 }
402
403 #ifdef MULTIPROCESSOR
404 /*
405 * On i386, the APs have not been hatched at the time hibernate resume is
406 * called, so there is no need to quiesce them. We do want to make sure
407 * however that we are on the BSP.
408 */
409 void
hibernate_quiesce_cpus(void)410 hibernate_quiesce_cpus(void)
411 {
412 KASSERT(CPU_IS_PRIMARY(curcpu()));
413 }
414 #endif /* MULTIPROCESSOR */
415