diff options
-rw-r--r-- | sys/arch/i386/i386/acpi_wakecode.S | 5 | ||||
-rw-r--r-- | sys/arch/i386/i386/hibernate_machdep.c | 104 | ||||
-rw-r--r-- | sys/arch/i386/include/hibernate.h | 3 | ||||
-rw-r--r-- | sys/kern/subr_hibernate.c | 90 | ||||
-rw-r--r-- | sys/sys/hibernate.h | 4 |
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_ */ |