summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--sys/arch/i386/i386/hibernate_machdep.c109
-rw-r--r--sys/arch/i386/include/hibernate.h17
-rw-r--r--sys/arch/i386/include/hibernate_var.h29
-rw-r--r--sys/kern/subr_hibernate.c76
-rw-r--r--sys/sys/hibernate.h72
5 files changed, 217 insertions, 86 deletions
diff --git a/sys/arch/i386/i386/hibernate_machdep.c b/sys/arch/i386/i386/hibernate_machdep.c
index f69a9e6ded2..c5a81b837bb 100644
--- a/sys/arch/i386/i386/hibernate_machdep.c
+++ b/sys/arch/i386/i386/hibernate_machdep.c
@@ -20,13 +20,17 @@
#include <sys/device.h>
#include <sys/disk.h>
#include <sys/disklabel.h>
+#include <sys/hibernate.h>
#include <sys/timeout.h>
#include <sys/malloc.h>
+#include <dev/acpi/acpivar.h>
+
#include <uvm/uvm_extern.h>
#include <uvm/uvm_pmemrange.h>
#include <machine/hibernate.h>
+#include <machine/hibernate_var.h>
#include <machine/kcore.h>
#include <machine/pmap.h>
@@ -37,6 +41,7 @@
#include <machine/mpbiosvar.h>
#endif /* MULTIPROCESSOR */
+#include "acpi.h"
#include "wd.h"
#ifndef SMALL_KERNEL
@@ -44,13 +49,12 @@
int hibernate_write_image(void);
int hibernate_read_image(void);
void hibernate_unpack_image(void);
-void *get_hibernate_io_function(void);
-int get_hibernate_info(struct hibernate_info *);
void hibernate_enter_resume_pte(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);
-struct hibernate_info *global_hiber_info;
+union hibernate_info *global_hiber_info;
paddr_t global_image_start;
extern void hibernate_resume_machine(void);
@@ -66,11 +70,16 @@ extern struct dumpmem dumpmem[];
* i386 MD Hibernate functions
*/
+/*
+ * get_hibernate_io_function
+ *
+ * Returns the hibernate write I/O function to use on this machine
+ *
+ */
void *
get_hibernate_io_function()
{
-
-#if NWD > 0
+#if NWD > 0
/* XXX - Only support wd hibernate presently */
if (strcmp(findblkname(major(swdevt[0].sw_dev)), "wd") == 0)
return wd_hibernate_io;
@@ -81,20 +90,15 @@ get_hibernate_io_function()
#endif
}
+/*
+ * get_hibernate_info_md
+ *
+ * Gather MD-specific data and store into hiber_info
+ */
int
-get_hibernate_info(struct hibernate_info *hiber_info)
+get_hibernate_info_md(union hibernate_info *hiber_info)
{
int i;
- struct disklabel dl;
- char err_string[128], *dl_ret;
-
- /* Determine I/O function to use */
- hiber_info->io_func = get_hibernate_io_function();
- if (hiber_info->io_func == NULL)
- return (0);
-
- /* Calculate hibernate device */
- hiber_info->device = swdevt[0].sw_dev;
/* Calculate memory ranges */
hiber_info->nranges = ndumpmem;
@@ -102,10 +106,9 @@ get_hibernate_info(struct hibernate_info *hiber_info)
for(i=0; i<ndumpmem; i++) {
hiber_info->ranges[i].base = dumpmem[i].start * PAGE_SIZE;
- hiber_info->ranges[i].end =
- (dumpmem[i].end * PAGE_SIZE);
- hiber_info->image_size +=
- hiber_info->ranges[i].end - hiber_info->ranges[i].base;
+ hiber_info->ranges[i].end = dumpmem[i].end * PAGE_SIZE;
+ hiber_info->image_size += hiber_info->ranges[i].end -
+ hiber_info->ranges[i].base;
}
#if NACPI > 0
@@ -122,29 +125,7 @@ get_hibernate_info(struct hibernate_info *hiber_info)
hiber_info->image_size += PAGE_SIZE;
#endif
- /* Read disklabel (used to calculate signature and image offsets */
- dl_ret = disk_readlabel(&dl, hiber_info->device, err_string, 128);
-
- if (dl_ret) {
- printf("Hibernate error: %s\n", dl_ret);
- return (0);
- }
-
- /* Calculate signature block offset in swap */
- hiber_info->sig_offset = DL_BLKTOSEC(&dl,
- (dl.d_partitions[1].p_size - 1)) *
- DL_BLKSPERSEC(&dl);
-
- /* Calculate memory image offset in swap */
- hiber_info->image_offset = dl.d_partitions[1].p_offset +
- dl.d_partitions[1].p_size -
- (hiber_info->image_size / 512) -1;
-
- /* Stash kernel version information */
- bcopy(version, &hiber_info->kernel_version,
- min(strlen(version), sizeof(hiber_info->kernel_version)));
-
- return (1);
+ return (0);
}
/*
@@ -156,7 +137,7 @@ hibernate_enter_resume_pte(vaddr_t va, paddr_t pa)
{
pt_entry_t *pte, npte;
- pte = s4pte_4m(va);
+ pte = s4pde_4m(va);
npte = (pa & PMAP_PA_MASK_4M) | PG_RW | PG_V | PG_U | PG_M | PG_PS;
*pte = npte;
}
@@ -176,7 +157,7 @@ hibernate_populate_resume_pt(paddr_t *image_start, paddr_t *image_end)
vaddr_t kern_start_4m_va, kern_end_4m_va, page;
/* Get the pig (largest contiguous physical range) from uvm */
- if (uvm_pmr_alloc_pig(&pig_start, &pig_sz) == ENOMEM)
+ if (uvm_pmr_alloc_pig(&pig_start, pig_sz) == ENOMEM)
panic("Insufficient memory for resume");
*image_start = pig_start;
@@ -221,14 +202,14 @@ hibernate_populate_resume_pt(paddr_t *image_start, paddr_t *image_end)
int
hibernate_write_image()
{
- struct hibernate_info hiber_info;
+ union hibernate_info hiber_info;
int i, j;
paddr_t range_base, range_end, addr;
daddr_t blkctr;
/* Get current running machine's hibernate info */
- if (!get_hibernate_info(&hiber_info))
- return (0);
+ if (get_hibernate_info(&hiber_info))
+ return (1);
pmap_kenter_pa(HIBERNATE_TEMP_PAGE, HIBERNATE_TEMP_PAGE, VM_PROT_ALL);
pmap_kenter_pa(HIBERNATE_ALLOC_PAGE, HIBERNATE_ALLOC_PAGE, VM_PROT_ALL);
@@ -261,14 +242,14 @@ hibernate_write_image()
int
hibernate_read_image()
{
- struct hibernate_info hiber_info;
+ union hibernate_info hiber_info;
int i, j;
paddr_t range_base, range_end, addr, image_start, image_end;
daddr_t blkctr;
/* Get current running machine's hibernate info */
- if (!get_hibernate_info(&hiber_info))
- return (0);
+ if (get_hibernate_info(&hiber_info))
+ return (1);
pmap_kenter_pa(HIBERNATE_TEMP_PAGE, HIBERNATE_TEMP_PAGE, VM_PROT_ALL);
pmap_kenter_pa(HIBERNATE_ALLOC_PAGE, HIBERNATE_ALLOC_PAGE, VM_PROT_ALL);
@@ -316,7 +297,7 @@ hibernate_suspend()
void
hibernate_unpack_image()
{
- struct hibernate_info *hiber_info = global_hiber_info;
+ union hibernate_info *hiber_info = global_hiber_info;
int i, j;
paddr_t base, end, pig_base;
@@ -337,13 +318,13 @@ hibernate_unpack_image()
void
hibernate_resume()
{
- struct hibernate_info hiber_info, disk_hiber_info;
+ union hibernate_info hiber_info, disk_hiber_info;
u_int8_t *io_page;
int s;
paddr_t image_start, image_end;
/* Get current running machine's hibernate info */
- if (!get_hibernate_info(&hiber_info))
+ if (get_hibernate_info(&hiber_info))
return;
io_page = malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT);
@@ -358,7 +339,7 @@ hibernate_resume()
free(io_page, M_DEVBUF);
if (memcmp(&hiber_info, &disk_hiber_info,
- sizeof(struct hibernate_info)) !=0) {
+ sizeof(union hibernate_info)) !=0) {
return;
}
@@ -402,7 +383,7 @@ hibernate_resume()
hibernate_switch_stack();
/* Read the image from disk into the image (pig) area */
- if (!hibernate_read_image())
+ if (hibernate_read_image())
panic("Failed to restore the hibernate image");
/*
@@ -423,16 +404,16 @@ hibernate_resume()
int
hibernate_write_signature()
{
- struct hibernate_info hiber_info;
+ union hibernate_info hiber_info;
u_int8_t *io_page;
/* Get current running machine's hibernate info */
- if (!get_hibernate_info(&hiber_info))
- return (0);
+ if (get_hibernate_info(&hiber_info))
+ return (1);
io_page = malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT);
if (!io_page)
- return (0);
+ return (1);
/* Write hibernate info to disk */
hiber_info.io_func(hiber_info.device, hiber_info.sig_offset,
@@ -440,13 +421,13 @@ hibernate_write_signature()
free(io_page, M_DEVBUF);
- return (1);
+ return (0);
}
int
hibernate_clear_signature()
{
- struct hibernate_info hiber_info;
+ union hibernate_info hiber_info;
u_int8_t *io_page;
/* Zero out a blank hiber_info */
@@ -454,7 +435,7 @@ hibernate_clear_signature()
io_page = malloc(PAGE_SIZE, M_DEVBUF, M_NOWAIT);
if (!io_page)
- return (0);
+ return (1);
/* Write (zeroed) hibernate info to disk */
hiber_info.io_func(hiber_info.device, hiber_info.sig_offset,
@@ -462,6 +443,6 @@ hibernate_clear_signature()
free(io_page, M_DEVBUF);
- return (1);
+ return (0);
}
#endif /* !SMALL_KERNEL */
diff --git a/sys/arch/i386/include/hibernate.h b/sys/arch/i386/include/hibernate.h
index 9a38cb77081..352de896b7c 100644
--- a/sys/arch/i386/include/hibernate.h
+++ b/sys/arch/i386/include/hibernate.h
@@ -18,21 +18,6 @@
/* i386 hibernate support structures and functions */
-struct hibernate_memory_range {
- paddr_t base;
- paddr_t end;
-};
-
-struct hibernate_info {
- u_int nranges;
- u_int64_t image_size;
- dev_t device;
- daddr_t sig_offset;
- daddr_t image_offset;
- struct hibernate_memory_range ranges[VM_PHYSSEG_MAX];
- char kernel_version[128];
- int (*io_func)(dev_t, daddr_t, vaddr_t, size_t, int, void *);
-};
-
+int get_hibernate_info_md(union hibernate_info *);
int hibernate_suspend(void);
void hibernate_resume(void);
diff --git a/sys/arch/i386/include/hibernate_var.h b/sys/arch/i386/include/hibernate_var.h
index b7aed052c7c..07a14b9836f 100644
--- a/sys/arch/i386/include/hibernate_var.h
+++ b/sys/arch/i386/include/hibernate_var.h
@@ -23,9 +23,32 @@
#define HIBERNATE_STACK_PAGE (PAGE_SIZE * 5)
#define HIBERNATE_IO_PAGE (PAGE_SIZE * 6)
#define HIBERNATE_TEMP_PAGE (PAGE_SIZE * 10)
-#define HIBERNATE_PT_PAGE (PAGE_SIZE * 11)
-#define HIBERNATE_ALLOC_PAGE (PAGE_SIZE * 12)
+#define HIBERNATE_TEMP_PAGE2 (PAGE_SIZE * 11)
+#define HIBERNATE_PD_PAGE (PAGE_SIZE * 12)
+#define HIBERNATE_PT_PAGE (PAGE_SIZE * 13)
+#define HIBERNATE_ALLOC_PAGE (PAGE_SIZE * 14)
+
+#define HIBERNATE_CHUNKS_PAGE (PAGE_SIZE * 15)
+
+/* Use 4MB hibernation chunks */
+#define HIBERNATE_CHUNK_SIZE 0x400000
+
+/* 1MB of chunk table from 1MB-2MB phys */
+#define HIBERNATE_CHUNK_TABLE_START 0x100000
+#define HIBERNATE_CHUNK_TABLE_END 0x200000
+#define HIBERNATE_CHUNK_TABLE_SIZE (HIBERNATE_CHUNK_TABLE_END - \
+ HIBERNATE_CHUNK_TABLE_START)
+
+/* 320KB (80 pages) for gzip allocator */
+#define HIBERNATE_ZLIB_SCRATCH (PAGE_SIZE * 20)
+#define HIBERNATE_ZLIB_START (PAGE_SIZE * 21)
+#define HIBERNATE_ZLIB_END (PAGE_SIZE * (21 + 80))
+#define HIBERNATE_ZLIB_SIZE (HIBERNATE_ZLIB_END - HIBERNATE_ZLIB_START)
+
#define HIBERNATE_STACK_OFFSET 0x0F00
#define atop_4m(x) ((x) >> PAGE_SHIFT_4M)
-#define s4pte_4m(va) ((pt_entry_t *)HIBERNATE_PT_PAGE + atop_4m(va))
+#define atop_4k(x) ((x) >> PAGE_SHIFT)
+#define s4pde_4m(va) ((pt_entry_t *)HIBERNATE_PD_PAGE + atop_4m(va))
+#define s4pde_4k(va) ((pt_entry_t *)HIBERNATE_PD_PAGE + atop_4k(va))
+#define s4pte_4k(va) ((pt_entry_t *)HIBERNATE_PT_PAGE + atop_4k(va))
diff --git a/sys/kern/subr_hibernate.c b/sys/kern/subr_hibernate.c
index 056eb882009..41767cc3abf 100644
--- a/sys/kern/subr_hibernate.c
+++ b/sys/kern/subr_hibernate.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: subr_hibernate.c,v 1.7 2011/07/08 21:02:49 ariane Exp $ */
+/* $OpenBSD: subr_hibernate.c,v 1.8 2011/07/09 00:08:04 mlarkin Exp $ */
/*
* Copyright (c) 2011 Ariane van der Steldt <ariane@stack.nl>
@@ -21,8 +21,12 @@
#include <sys/tree.h>
#include <sys/types.h>
#include <sys/systm.h>
+#include <sys/disklabel.h>
+#include <sys/conf.h>
#include <uvm/uvm.h>
+#include <machine/hibernate.h>
+extern char *disk_readlabel(struct disklabel *, dev_t, char *, size_t);
/*
* Hib alloc enforced alignment.
@@ -287,7 +291,7 @@ uvm_pmr_dirty_everything(void)
}
/* Dirty multi page ranges. */
- while ((pg = RB_ROOT(&pmr->size[UVM_PMR_MEMTYPE_ZEOR]))
+ while ((pg = RB_ROOT(&pmr->size[UVM_PMR_MEMTYPE_ZERO]))
!= NULL) {
pg--; /* Size tree always has second page. */
uvm_pmr_remove(pmr, pg);
@@ -522,3 +526,71 @@ uvm_page_rle(paddr_t addr)
(pg_end->pg_flags & PQ_FREE) == PQ_FREE; pg_end++);
return pg_end - pg;
}
+
+/*
+ * get_hibernate_info
+ *
+ * Fills out the hibernate_info union pointed to by hiber_info
+ * with information about this machine (swap signature block
+ * offsets, number of memory ranges, kernel in use, etc)
+ *
+ */
+int
+get_hibernate_info(union hibernate_info *hiber_info)
+{
+ int chunktable_size;
+ struct disklabel dl;
+ char err_string[128], *dl_ret;
+
+ /* Determine I/O function to use */
+ hiber_info->io_func = get_hibernate_io_function();
+ if (hiber_info->io_func == NULL)
+ return (1);
+
+ /* Calculate hibernate device */
+ hiber_info->device = swdevt[0].sw_dev;
+
+ /* Read disklabel (used to calculate signature and image offsets) */
+ dl_ret = disk_readlabel(&dl, hiber_info->device, err_string, 128);
+
+ if (dl_ret) {
+ printf("Hibernate error reading disklabel: %s\n", dl_ret);
+ return (1);
+ }
+
+ hiber_info->secsize = dl.d_secsize;
+
+ /* Make sure the signature can fit in one block */
+ KASSERT(sizeof(union hibernate_info)/hiber_info->secsize == 1);
+
+ /* Calculate swap offset from start of disk */
+ hiber_info->swap_offset = dl.d_partitions[1].p_offset;
+
+ /* Calculate signature block location */
+ hiber_info->sig_offset = dl.d_partitions[1].p_offset +
+ dl.d_partitions[1].p_size -
+ sizeof(union hibernate_info)/hiber_info->secsize;
+
+ chunktable_size = HIBERNATE_CHUNK_TABLE_SIZE / hiber_info->secsize;
+
+ /* Calculate memory image location */
+ hiber_info->image_offset = dl.d_partitions[1].p_offset +
+ dl.d_partitions[1].p_size -
+ (hiber_info->image_size / hiber_info->secsize) -
+ sizeof(union hibernate_info)/hiber_info->secsize -
+ chunktable_size;
+
+ /* Stash kernel version information */
+ bzero(&hiber_info->kernel_version, 128);
+ bcopy(version, &hiber_info->kernel_version,
+ min(strlen(version), sizeof(hiber_info->kernel_version)-1));
+
+ /* Allocate piglet region */
+ if (uvm_pmr_alloc_piglet(&hiber_info->piglet_base, HIBERNATE_CHUNK_SIZE,
+ HIBERNATE_CHUNK_SIZE)) {
+ printf("Hibernate failed to allocate the piglet\n");
+ return (1);
+ }
+
+ return get_hibernate_info_md(hiber_info);
+}
diff --git a/sys/sys/hibernate.h b/sys/sys/hibernate.h
index 8bf90a3ccb5..275a8edb9ce 100644
--- a/sys/sys/hibernate.h
+++ b/sys/sys/hibernate.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: hibernate.h,v 1.6 2011/07/08 21:00:53 ariane Exp $ */
+/* $OpenBSD: hibernate.h,v 1.7 2011/07/09 00:08:04 mlarkin Exp $ */
/*
* Copyright (c) 2011 Ariane van der Steldt <ariane@stack.nl>
@@ -21,6 +21,12 @@
#include <sys/types.h>
#include <sys/tree.h>
+#include <lib/libz/zlib.h>
+#include <machine/vmparam.h>
+
+#define HIBERNATE_CHUNK_USED 1
+#define HIBERNATE_CHUNK_CONFLICT 2
+#define HIBERNATE_CHUNK_PLACED 4
struct hiballoc_entry;
@@ -35,6 +41,67 @@ struct hiballoc_arena
hib_addrs;
};
+/*
+ * struct hibernate_state
+ *
+ * Describes a zlib compression stream and its associated hiballoc area
+ */
+struct hibernate_state {
+ z_stream hib_stream;
+ struct hiballoc_arena hiballoc_arena;
+};
+
+/*
+ * struct hibernate_memory_range
+ *
+ * Describes a range of physical memory on the machine
+ */
+struct hibernate_memory_range {
+ paddr_t base;
+ paddr_t end;
+};
+
+/*
+ * struct hibernate_disk_chunk
+ *
+ * Describes a hibernate chunk structure, used when splitting the memory
+ * image of the machine into easy-to-manage pieces.
+ */
+struct hibernate_disk_chunk {
+ paddr_t base; /* Base of chunk */
+ paddr_t end; /* End of chunk */
+ size_t offset; /* Abs. disk block locating chunk */
+ size_t compressed_size; /* Compressed size on disk */
+ short flags; /* Flags */
+};
+
+/*
+ * union hibernate_info
+ *
+ * Used to store information about the hibernation state of the machine,
+ * such as memory range count and extents, disk sector size, and various
+ * offsets where things are located on disk.
+ */
+union hibernate_info {
+ struct {
+ size_t nranges;
+ size_t image_size;
+ size_t chunk_ctr;
+ u_int32_t secsize;
+ dev_t device;
+ daddr_t swap_offset;
+ daddr_t sig_offset;
+ daddr_t image_offset;
+ paddr_t piglet_base;
+ struct hibernate_memory_range ranges[VM_PHYSSEG_MAX];
+ char kernel_version[128];
+ int (*io_func)(dev_t, daddr_t, vaddr_t, size_t, int, void *);
+ };
+
+ /* XXX - remove restriction to have this union fit in a single block */
+ char pad[512]; /* Pad to 512 bytes */
+};
+
void *hib_alloc(struct hiballoc_arena*, size_t);
void hib_free(struct hiballoc_arena*, void*);
int hiballoc_init(struct hiballoc_arena*, void*, size_t len);
@@ -44,4 +111,7 @@ int uvm_pmr_alloc_pig(paddr_t*, psize_t);
int uvm_pmr_alloc_piglet(paddr_t*, psize_t, paddr_t);
psize_t uvm_page_rle(paddr_t);
+void *get_hibernate_io_function(void);
+int get_hibernate_info(union hibernate_info *);
+
#endif /* _SYS_HIBERNATE_H_ */