summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorMike Larkin <mlarkin@cvs.openbsd.org>2011-11-13 18:38:11 +0000
committerMike Larkin <mlarkin@cvs.openbsd.org>2011-11-13 18:38:11 +0000
commitfc5e7c71f13392cfc1167445c150e9e2c55d3a1b (patch)
tree30cd77047f528a8d8500400523e55a800bda8724 /sys
parent5283aacecf7ce3c8ed5b87bfc800614878e91940 (diff)
Fix a handful of bugs that were causing reboots and other bad behavior
during hibernate resumes.
Diffstat (limited to 'sys')
-rw-r--r--sys/arch/i386/i386/acpi_wakecode.S6
-rw-r--r--sys/arch/i386/i386/hibernate_machdep.c4
-rw-r--r--sys/arch/i386/include/hibernate_var.h3
-rw-r--r--sys/kern/subr_hibernate.c101
4 files changed, 66 insertions, 48 deletions
diff --git a/sys/arch/i386/i386/acpi_wakecode.S b/sys/arch/i386/i386/acpi_wakecode.S
index 45e3aa877e7..9df1da9ffaf 100644
--- a/sys/arch/i386/i386/acpi_wakecode.S
+++ b/sys/arch/i386/i386/acpi_wakecode.S
@@ -369,7 +369,7 @@ _ACPI_TRMP_LABEL(hibernate_resume_vector_3)
/* Switch to hibernate resume pagetable */
NENTRY(hibernate_activate_resume_pt_machdep)
/* Enable large pages */
- movl %eax, %cr4
+ movl %cr4, %eax
orl $(CR4_PSE), %eax
movl %eax, %cr4
@@ -394,8 +394,8 @@ NENTRY(hibernate_switch_stack_machdep)
NENTRY(hibernate_flush)
wbinvd
- movl $hibernate_inflate_page, %eax
- invlpg (%eax)
+ invlpg HIBERNATE_INFLATE_PAGE
+ invlpg HIBERNATE_COPY_PAGE
ret
#endif /* HIBERNATE */
diff --git a/sys/arch/i386/i386/hibernate_machdep.c b/sys/arch/i386/i386/hibernate_machdep.c
index cc409067b68..33b7acbf6e8 100644
--- a/sys/arch/i386/i386/hibernate_machdep.c
+++ b/sys/arch/i386/i386/hibernate_machdep.c
@@ -52,8 +52,6 @@ void hibernate_enter_resume_4k_pde(vaddr_t);
void hibernate_enter_resume_4m_pde(vaddr_t, paddr_t);
int hibernate_read_chunks(union hibernate_info *, paddr_t, paddr_t, size_t);
-extern vaddr_t hibernate_inflate_page;
-
extern void hibernate_resume_machdep(void);
extern void hibernate_flush(void);
extern caddr_t start, end;
@@ -190,8 +188,6 @@ hibernate_populate_resume_pt(union hibernate_info *hib_info,
pmap_kenter_pa(HIBERNATE_STACK_PAGE, HIBERNATE_STACK_PAGE, VM_PROT_ALL);
pmap_activate(curproc);
- hibernate_inflate_page = HIBERNATE_INFLATE_PAGE;
-
bzero((caddr_t)HIBERNATE_PT_PAGE, PAGE_SIZE);
bzero((caddr_t)HIBERNATE_PD_PAGE, PAGE_SIZE);
bzero((caddr_t)HIBERNATE_STACK_PAGE, PAGE_SIZE);
diff --git a/sys/arch/i386/include/hibernate_var.h b/sys/arch/i386/include/hibernate_var.h
index b34fd9395cb..86299d8b0a9 100644
--- a/sys/arch/i386/include/hibernate_var.h
+++ b/sys/arch/i386/include/hibernate_var.h
@@ -20,10 +20,13 @@
#define PAGE_MASK_4M (NBPD - 1)
#define PMAP_PA_MASK_4M ~((paddr_t)PAGE_MASK_4M)
+#define PIGLET_PAGE_MASK ~((paddr_t)PAGE_MASK_4M)
+
#define HIBERNATE_PD_PAGE (PAGE_SIZE * 5)
#define HIBERNATE_PT_PAGE (PAGE_SIZE * 6)
#define HIBERNATE_STACK_PAGE (PAGE_SIZE * 7)
#define HIBERNATE_INFLATE_PAGE (PAGE_SIZE * 8)
+#define HIBERNATE_COPY_PAGE (PAGE_SIZE * 9)
/* Use 4MB hibernation chunks */
#define HIBERNATE_CHUNK_SIZE 0x400000
diff --git a/sys/kern/subr_hibernate.c b/sys/kern/subr_hibernate.c
index 83d4f271e26..60b9e7a5784 100644
--- a/sys/kern/subr_hibernate.c
+++ b/sys/kern/subr_hibernate.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: subr_hibernate.c,v 1.18 2011/09/22 22:12:45 deraadt Exp $ */
+/* $OpenBSD: subr_hibernate.c,v 1.19 2011/11/13 18:38:10 mlarkin Exp $ */
/*
* Copyright (c) 2011 Ariane van der Steldt <ariane@stack.nl>
@@ -40,10 +40,10 @@ vaddr_t hibernate_copy_page;
vaddr_t hibernate_stack_page;
vaddr_t hibernate_fchunk_area;
vaddr_t hibernate_chunktable_area;
-vaddr_t hibernate_inflate_page;
/* Hibernate info as read from disk during resume */
union hibernate_info disk_hiber_info;
+paddr_t global_pig_start;
/*
* Hib alloc enforced alignment.
@@ -708,8 +708,6 @@ hibernate_inflate(union hibernate_info *hiber_info, paddr_t dest,
hibernate_state->hib_stream.avail_in = size;
hibernate_state->hib_stream.next_in = (char *)src;
- hibernate_inflate_page = hiber_info->piglet_va + 2 * PAGE_SIZE;
-
do {
/* Flush cache and TLB */
hibernate_flush();
@@ -720,16 +718,16 @@ hibernate_inflate(union hibernate_info *hiber_info, paddr_t dest,
*/
if (hibernate_inflate_skip(hiber_info, dest))
hibernate_enter_resume_mapping(
- hibernate_inflate_page,
- hiber_info->piglet_pa + 2 * PAGE_SIZE, 0);
+ HIBERNATE_INFLATE_PAGE,
+ HIBERNATE_INFLATE_PAGE, 0);
else
hibernate_enter_resume_mapping(
- hibernate_inflate_page, dest, 0);
+ HIBERNATE_INFLATE_PAGE, dest, 0);
/* Set up the stream for inflate */
hibernate_state->hib_stream.avail_out = PAGE_SIZE;
- hibernate_state->hib_stream.next_out =
- (char *)hiber_info->piglet_va + 2 * PAGE_SIZE;
+ hibernate_state->hib_stream.next_out =
+ (char *)HIBERNATE_INFLATE_PAGE;
/* Process next block of data */
i = inflate(&hibernate_state->hib_stream, Z_PARTIAL_FLUSH);
@@ -1124,14 +1122,15 @@ void
hibernate_unpack_image(union hibernate_info *hiber_info)
{
struct hibernate_disk_chunk *chunks;
- paddr_t image_cur;
+ union hibernate_info local_hiber_info;
+ paddr_t image_cur = global_pig_start;
vaddr_t tempva;
int *fchunks, i;
- char *pva;
-
- pva = (char *)hiber_info->piglet_va;
+ char *pva = (char *)hiber_info->piglet_va;
- fchunks = (int *)(pva + (4 * PAGE_SIZE));
+ /* Mask off based on arch-specific piglet page size */
+ pva = (char *)((paddr_t)pva & (PIGLET_PAGE_MASK));
+ fchunks = (int *)(pva + (6 * PAGE_SIZE));
/* Copy temporary chunktable to piglet */
tempva = (vaddr_t)km_alloc(HIBERNATE_CHUNK_TABLE_SIZE, &kv_any,
@@ -1145,11 +1144,14 @@ hibernate_unpack_image(union hibernate_info *hiber_info)
chunks = (struct hibernate_disk_chunk *)(pva + HIBERNATE_CHUNK_SIZE);
+ /* Can't use hiber_info that's passed in after here */
+ bcopy(hiber_info, &local_hiber_info, sizeof(union hibernate_info));
+
hibernate_activate_resume_pt_machdep();
- for (i = 0; i < hiber_info->chunk_ctr; i++) {
+ for (i = 0; i < local_hiber_info.chunk_ctr; i++) {
/* Reset zlib for inflate */
- if (hibernate_zlib_reset(hiber_info, 0) != Z_OK)
+ if (hibernate_zlib_reset(&local_hiber_info, 0) != Z_OK)
panic("hibernate failed to reset zlib for inflate\n");
/*
@@ -1157,15 +1159,16 @@ hibernate_unpack_image(union hibernate_info *hiber_info)
* before unpacking it to its original location.
*/
if ((chunks[fchunks[i]].flags & HIBERNATE_CHUNK_CONFLICT) == 0)
- hibernate_inflate(hiber_info, chunks[fchunks[i]].base,
- image_cur, chunks[fchunks[i]].compressed_size);
+ hibernate_inflate(&local_hiber_info,
+ chunks[fchunks[i]].base, image_cur,
+ chunks[fchunks[i]].compressed_size);
else {
bcopy((caddr_t)image_cur,
- (caddr_t)hiber_info->piglet_va +
- HIBERNATE_CHUNK_SIZE * 2,
+ pva + (HIBERNATE_CHUNK_SIZE * 2),
chunks[fchunks[i]].compressed_size);
- hibernate_inflate(hiber_info, chunks[fchunks[i]].base,
- hiber_info->piglet_va + HIBERNATE_CHUNK_SIZE * 2,
+ hibernate_inflate(&local_hiber_info,
+ chunks[fchunks[i]].base,
+ (vaddr_t)(pva + (HIBERNATE_CHUNK_SIZE * 2)),
chunks[fchunks[i]].compressed_size);
}
image_cur += chunks[fchunks[i]].compressed_size;
@@ -1238,7 +1241,7 @@ hibernate_write_chunks(union hibernate_info *hiber_info)
pmap_kenter_pa(hibernate_copy_page,
(hiber_info->piglet_pa + 3*PAGE_SIZE), VM_PROT_ALL);
- /* XXX - needed on i386. check other archs */
+ /* XXX - not needed on all archs */
pmap_activate(curproc);
chunks = (struct hibernate_disk_chunk *)(hiber_info->piglet_va +
@@ -1287,12 +1290,17 @@ hibernate_write_chunks(union hibernate_info *hiber_info)
while (out_remaining > 0 && inaddr < range_end) {
pmap_kenter_pa(hibernate_temp_page,
inaddr & PMAP_PA_MASK, VM_PROT_ALL);
+
+ /* XXX - not needed on all archs */
pmap_activate(curproc);
bcopy((caddr_t)hibernate_temp_page,
(caddr_t)hibernate_copy_page, PAGE_SIZE);
- /* Adjust for non page-sized regions */
+ /*
+ * Adjust for regions that are not evenly
+ * divisible by PAGE_SIZE
+ */
temp_inaddr = (inaddr & PAGE_MASK) +
hibernate_copy_page;
@@ -1372,11 +1380,12 @@ hibernate_zlib_reset(union hibernate_info *hiber_info, int deflate)
{
vaddr_t hibernate_zlib_start;
size_t hibernate_zlib_size;
+ char *pva = (char *)hiber_info->piglet_va;
- hibernate_state = (struct hibernate_zlib_state *)hiber_info->piglet_va +
- (4 * PAGE_SIZE);
+ hibernate_state = (struct hibernate_zlib_state *)
+ (pva + (7 * PAGE_SIZE));
- hibernate_zlib_start = hiber_info->piglet_va + (5 * PAGE_SIZE);
+ hibernate_zlib_start = (vaddr_t)(pva + (8 * PAGE_SIZE));
hibernate_zlib_size = 80 * PAGE_SIZE;
bzero((caddr_t)hibernate_zlib_start, hibernate_zlib_size);
@@ -1489,26 +1498,32 @@ hibernate_read_chunks(union hibernate_info *hib_info, paddr_t pig_start,
paddr_t piglet_end = piglet_base + HIBERNATE_CHUNK_SIZE;
daddr_t blkctr;
size_t processed, compressed_size, read_size;
- int i, j, overlap, found, nchunks, nochunks = 0, nfchunks = 0, npchunks = 0;
+ int i, j, overlap, found, nchunks;
+ int nochunks = 0, nfchunks = 0, npchunks = 0;
struct hibernate_disk_chunk *chunks;
- u_int8_t *ochunks, *pchunks, *fchunks;
+ int *ochunks, *pchunks, *fchunks;
- /* Map the chunk ordering region */
- pmap_kenter_pa(hibernate_fchunk_area,
- piglet_base + (4*PAGE_SIZE), VM_PROT_ALL);
- pmap_kenter_pa(hibernate_fchunk_area + PAGE_SIZE,
- piglet_base + (5*PAGE_SIZE), VM_PROT_ALL);
- pmap_kenter_pa(hibernate_fchunk_area + 2*PAGE_SIZE,
- piglet_base + (6*PAGE_SIZE), VM_PROT_ALL);
+ global_pig_start = pig_start;
+
+ /* XXX - dont need this on all archs */
+ pmap_activate(curproc);
/* Temporary output chunk ordering */
- ochunks = (u_int8_t *)hibernate_fchunk_area;
+ ochunks = (int *)hibernate_fchunk_area;
/* Piglet chunk ordering */
- pchunks = (u_int8_t *)hibernate_fchunk_area + PAGE_SIZE;
+ pchunks = (int *)(hibernate_fchunk_area + PAGE_SIZE);
/* Final chunk ordering */
- fchunks = (u_int8_t *)hibernate_fchunk_area + 2*PAGE_SIZE;
+ fchunks = (int *)(hibernate_fchunk_area + (2*PAGE_SIZE));
+
+ /* Map the chunk ordering region */
+ pmap_kenter_pa(hibernate_fchunk_area,
+ piglet_base + (4*PAGE_SIZE), VM_PROT_ALL);
+ pmap_kenter_pa((vaddr_t)pchunks, piglet_base + (5*PAGE_SIZE),
+ VM_PROT_ALL);
+ pmap_kenter_pa((vaddr_t)fchunks, piglet_base + (6*PAGE_SIZE),
+ VM_PROT_ALL);
nchunks = hib_info->chunk_ctr;
chunks = (struct hibernate_disk_chunk *)hibernate_chunktable_area;
@@ -1639,7 +1654,7 @@ hibernate_read_chunks(union hibernate_info *hib_info, paddr_t pig_start,
pmap_kenter_pa(hibernate_temp_page + PAGE_SIZE,
img_cur+PAGE_SIZE, VM_PROT_ALL);
- /* XXX - needed on i386. check other archs */
+ /* XXX - not needed on all archs */
pmap_activate(curproc);
if (compressed_size - processed >= PAGE_SIZE)
read_size = PAGE_SIZE;
@@ -1697,5 +1712,9 @@ hibernate_suspend(void)
if (hibernate_write_chunktable(&hib_info))
return (1);
- return hibernate_write_signature(&hib_info);
+ if (hibernate_write_signature(&hib_info))
+ return (1);
+
+ delay(100000);
+ return (0);
}