diff options
-rw-r--r-- | sys/arch/i386/i386/hibernate_machdep.c | 109 | ||||
-rw-r--r-- | sys/arch/i386/include/hibernate.h | 17 | ||||
-rw-r--r-- | sys/arch/i386/include/hibernate_var.h | 29 | ||||
-rw-r--r-- | sys/kern/subr_hibernate.c | 76 | ||||
-rw-r--r-- | sys/sys/hibernate.h | 72 |
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_ */ |