summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/arch/i386/i386/acpi_wakecode.S5
-rw-r--r--sys/arch/i386/i386/hibernate_machdep.c104
-rw-r--r--sys/arch/i386/include/hibernate.h3
-rw-r--r--sys/kern/subr_hibernate.c90
-rw-r--r--sys/sys/hibernate.h4
5 files changed, 188 insertions, 18 deletions
diff --git a/sys/arch/i386/i386/acpi_wakecode.S b/sys/arch/i386/i386/acpi_wakecode.S
index 9940f409303..fd4261c5a0a 100644
--- a/sys/arch/i386/i386/acpi_wakecode.S
+++ b/sys/arch/i386/i386/acpi_wakecode.S
@@ -385,6 +385,11 @@ NENTRY(hibernate_switch_stack)
/* On our own stack from here onward */
ret
+NENTRY(hibernate_flush)
+ wbinvd
+ invlpg HIBERNATE_TEMP_PAGE
+ ret
+
.code16
.align 8
_ACPI_TRMP_OFFSET(tmp_gdt)
diff --git a/sys/arch/i386/i386/hibernate_machdep.c b/sys/arch/i386/i386/hibernate_machdep.c
index 39c04951d2b..61f3809829d 100644
--- a/sys/arch/i386/i386/hibernate_machdep.c
+++ b/sys/arch/i386/i386/hibernate_machdep.c
@@ -49,11 +49,14 @@
int hibernate_write_image(void);
int hibernate_read_image(void);
void hibernate_unpack_image(void);
-void hibernate_enter_resume_pte(vaddr_t, paddr_t);
+void hibernate_enter_resume_4k_pte(vaddr_t, paddr_t);
+void hibernate_enter_resume_4k_pde(vaddr_t);
+void hibernate_enter_resume_4m_pde(vaddr_t, paddr_t);
void hibernate_populate_resume_pt(paddr_t *, paddr_t *);
int get_hibernate_info_md(union hibernate_info *);
int hibernate_write_signature(void);
int hibernate_clear_signature(void);
+int hibernate_inflate_skip(paddr_t);
union hibernate_info *global_hiber_info;
paddr_t global_image_start;
@@ -61,6 +64,7 @@ paddr_t global_image_start;
extern void hibernate_resume_machine(void);
extern void hibernate_activate_resume_pt(void);
extern void hibernate_switch_stack(void);
+extern void hibernate_flush(void);
extern char *disk_readlabel(struct disklabel *, dev_t, char *, size_t);
extern caddr_t start, end;
extern int ndumpmem;
@@ -163,20 +167,70 @@ get_hibernate_info_md(union hibernate_info *hiber_info)
}
/*
- * Enter a 4MB PTE mapping for the supplied VA/PA
- * into the resume-time page table.
+ * Enter a mapping for va->pa in the resume pagetable, using
+ * the specified size hint.
+ *
+ * hint : 0 if a 4KB mapping is desired
+ * 1 if a 4MB mapping is desired
+ */
+void
+hibernate_enter_resume_mapping(vaddr_t va, paddr_t pa, int hint)
+{
+ if (hint)
+ return hibernate_enter_resume_4m_pde(va, pa);
+ else {
+ hibernate_enter_resume_4k_pde(va);
+ return hibernate_enter_resume_4k_pte(va, pa);
+ }
+}
+
+/*
+ * Enter a 4MB PDE mapping for the supplied VA/PA
+ * into the resume-time pmap
+ */
+void
+hibernate_enter_resume_4m_pde(vaddr_t va, paddr_t pa)
+{
+ pt_entry_t *pde, npde;
+
+ pde = s4pde_4m(va);
+ npde = (pa & PMAP_PA_MASK_4M) | PG_RW | PG_V | PG_u | PG_M | PG_PS;
+ *pde = npde;
+}
+
+/*
+ * Enter a 4KB PTE mapping for the supplied VA/PA
+ * into the resume-time pmap. This should only be
+ * used to map the special pages and tramps below
+ * 1MB phys
*/
void
-hibernate_enter_resume_pte(vaddr_t va, paddr_t pa)
+hibernate_enter_resume_4k_pte(vaddr_t va, paddr_t pa)
{
pt_entry_t *pte, npte;
- pte = s4pde_4m(va);
- npte = (pa & PMAP_PA_MASK_4M) | PG_RW | PG_V | PG_U | PG_M | PG_PS;
+ pte = s4pte_4k(va);
+ npte = (pa & PMAP_PA_MASK) | PG_RW | PG_V | PG_u | PG_M;
*pte = npte;
}
/*
+ * Enter a 4KB PDE mapping for the supplied VA
+ * into the resume-time pmap. This should only be
+ * used to map the special pages and tramps below
+ * 1MB phys
+ */
+void
+hibernate_enter_resume_4k_pde(vaddr_t va)
+{
+ pt_entry_t *pde, npde;
+
+ pde = s4pde_4k(va);
+ npde = (HIBERNATE_PT_PAGE & PMAP_PA_MASK) | PG_RW | PG_V | PG_u | PG_M;
+ *pde = npde;
+}
+
+/*
* Create the resume-time page table. This table maps the image(pig) area,
* the kernel text area, and various utility pages located in low memory for
* use during resume, since we cannot overwrite the resuming kernel's
@@ -203,7 +257,7 @@ hibernate_populate_resume_pt(paddr_t *image_start, paddr_t *image_end)
* Identity map first 4M physical for tramps and special utility
* pages
*/
- hibernate_enter_resume_pte(0, 0);
+ hibernate_enter_resume_mapping(0, 0, 1);
/*
* Map current kernel VA range using 4M pages
@@ -216,7 +270,7 @@ hibernate_populate_resume_pt(paddr_t *image_start, paddr_t *image_end)
page += NBPD, phys_page_number++) {
pa = (paddr_t)(phys_page_number * NBPD);
- hibernate_enter_resume_pte(page, pa);
+ hibernate_enter_resume_mapping(page, pa, 1);
}
/*
@@ -229,7 +283,7 @@ hibernate_populate_resume_pt(paddr_t *image_start, paddr_t *image_end)
page += NBPD, phys_page_number++) {
pa = (paddr_t)(phys_page_number * NBPD);
- hibernate_enter_resume_pte(page, pa);
+ hibernate_enter_resume_mapping(page, pa, 1);
}
}
@@ -343,7 +397,7 @@ hibernate_unpack_image()
pig_base = base + global_image_start;
for (j=base; j< (end - base)/NBPD; j++) {
- hibernate_enter_resume_pte(base, base);
+ hibernate_enter_resume_mapping(base, base, 0);
bcopy((caddr_t)pig_base, (caddr_t)base, NBPD);
}
}
@@ -479,4 +533,34 @@ hibernate_clear_signature()
return (0);
}
+
+/*
+ * hibernate_inflate_skip
+ *
+ * During inflate, certain pages that contain our bookkeeping information
+ * (eg, the chunk table, scratch pages, etc) need to be skipped over and
+ * not inflated into.
+ *
+ * Returns 1 if the physical page at dest should be skipped, 0 otherwise
+ */
+int
+hibernate_inflate_skip(paddr_t dest)
+{
+ /* Chunk Table */
+ if (dest >= HIBERNATE_CHUNK_TABLE_START &&
+ dest <= HIBERNATE_CHUNK_TABLE_END)
+ return (1);
+
+ /* Contiguous utility pages */
+ if (dest >= HIBERNATE_STACK_PAGE &&
+ dest <= HIBERNATE_CHUNKS_PAGE)
+ return (1);
+
+ /* libz hiballoc arena */
+ if (dest >= HIBERNATE_ZLIB_SCRATCH &&
+ dest <= HIBERNATE_ZLIB_END)
+ return (1);
+
+ return (0);
+}
#endif /* !SMALL_KERNEL */
diff --git a/sys/arch/i386/include/hibernate.h b/sys/arch/i386/include/hibernate.h
index 8efe8d2e31c..5f3b7042eae 100644
--- a/sys/arch/i386/include/hibernate.h
+++ b/sys/arch/i386/include/hibernate.h
@@ -19,6 +19,9 @@
/* i386 hibernate support structures and functions */
int get_hibernate_info_md(union hibernate_info *);
+void hibernate_flush(void);
+void hibernate_enter_resume_mapping(vaddr_t, paddr_t, int);
int hibernate_zlib_reset(int);
+int hibernate_inflate_skip(paddr_t);
int hibernate_suspend(void);
void hibernate_resume(void);
diff --git a/sys/kern/subr_hibernate.c b/sys/kern/subr_hibernate.c
index 60db171c1ac..bbb7a9f7de3 100644
--- a/sys/kern/subr_hibernate.c
+++ b/sys/kern/subr_hibernate.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: subr_hibernate.c,v 1.9 2011/07/09 00:27:31 mlarkin Exp $ */
+/* $OpenBSD: subr_hibernate.c,v 1.10 2011/07/09 00:55:00 mlarkin Exp $ */
/*
* Copyright (c) 2011 Ariane van der Steldt <ariane@stack.nl>
@@ -610,14 +610,90 @@ void
}
/*
- * hibernate_zlib_free
+ * hibernate_inflate
+ *
+ * Inflate size bytes from src into dest, skipping any pages in
+ * [src..dest] that are special (see hibernate_inflate_skip)
+ *
+ * For each page of output data, we map HIBERNATE_TEMP_PAGE
+ * to the current output page, and tell inflate() to inflate
+ * its data there, resulting in the inflated data being placed
+ * at the proper paddr.
+ *
+ * This function executes while using the resume-time stack
+ * and pmap, and therefore cannot use ddb/printf/etc. Doing so
+ * will likely hang or reset the machine.
*
- * Free the memory pointed to by addr in the hiballoc area presently in
- * use
- *
*/
void
-hibernate_zlib_free(void *unused, void *addr)
+hibernate_inflate(paddr_t dest, paddr_t src, size_t size)
+{
+ int i;
+
+ hibernate_state->hib_stream.avail_in = size;
+ hibernate_state->hib_stream.next_in = (char *)src;
+
+ do {
+ /* Flush cache and TLB */
+ hibernate_flush();
+
+ /*
+ * Is this a special page? If yes, redirect the
+ * inflate output to a scratch page (eg, discard it)
+ */
+ if (hibernate_inflate_skip(dest))
+ hibernate_enter_resume_mapping(HIBERNATE_TEMP_PAGE,
+ HIBERNATE_TEMP_PAGE, 0);
+ else
+ hibernate_enter_resume_mapping(HIBERNATE_TEMP_PAGE,
+ dest, 0);
+
+ /* Set up the stream for inflate */
+ hibernate_state->hib_stream.avail_out = PAGE_SIZE;
+ hibernate_state->hib_stream.next_out =
+ (char *)HIBERNATE_TEMP_PAGE;
+
+ /* Process next block of data */
+ i = inflate(&hibernate_state->hib_stream, Z_PARTIAL_FLUSH);
+ if (i != Z_OK && i != Z_STREAM_END) {
+ /*
+ * XXX - this will likely reboot/hang most machines,
+ * but there's not much else we can do here.
+ */
+ panic("inflate error");
+ }
+
+ dest += PAGE_SIZE - hibernate_state->hib_stream.avail_out;
+ } while (i != Z_STREAM_END);
+}
+
+/*
+ * hibernate_deflate
+ *
+ * deflate from src into the I/O page, up to 'remaining' bytes
+ *
+ * Returns number of input bytes consumed, and may reset
+ * the 'remaining' parameter if not all the output space was consumed
+ * (this information is needed to know how much to write to disk
+ *
+ */
+size_t
+hibernate_deflate(paddr_t src, size_t *remaining)
{
- hib_free(&hibernate_state->hiballoc_arena, addr);
+ /* Set up the stream for deflate */
+ hibernate_state->hib_stream.avail_in = PAGE_SIZE -
+ (src & PAGE_MASK);
+ hibernate_state->hib_stream.avail_out = *remaining;
+ hibernate_state->hib_stream.next_in = (caddr_t)src;
+ hibernate_state->hib_stream.next_out = (caddr_t)HIBERNATE_IO_PAGE +
+ (PAGE_SIZE - *remaining);
+
+ /* Process next block of data */
+ if (deflate(&hibernate_state->hib_stream, Z_PARTIAL_FLUSH) != Z_OK)
+ panic("hibernate zlib deflate error\n");
+
+ /* Update pointers and return number of bytes consumed */
+ *remaining = hibernate_state->hib_stream.avail_out;
+ return (PAGE_SIZE - (src & PAGE_MASK)) -
+ hibernate_state->hib_stream.avail_in;
}
diff --git a/sys/sys/hibernate.h b/sys/sys/hibernate.h
index 354f1a038c9..58b28f15550 100644
--- a/sys/sys/hibernate.h
+++ b/sys/sys/hibernate.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: hibernate.h,v 1.8 2011/07/09 00:27:31 mlarkin Exp $ */
+/* $OpenBSD: hibernate.h,v 1.9 2011/07/09 00:55:00 mlarkin Exp $ */
/*
* Copyright (c) 2011 Ariane van der Steldt <ariane@stack.nl>
@@ -116,5 +116,7 @@ int get_hibernate_info(union hibernate_info *);
void *hibernate_zlib_alloc(void *, int, int);
void hibernate_zlib_free(void *, void *);
+void hibernate_inflate(paddr_t, paddr_t, size_t);
+size_t hibernate_deflate(paddr_t, size_t *);
#endif /* _SYS_HIBERNATE_H_ */