| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468 |
- /*
- * Copyright (C) 2004-2014 Nigel Cunningham (nigel at tuxonice net)
- *
- * This file is released under the GPLv2.
- */
- #include <linux/resume-trace.h>
- #include <linux/kernel.h>
- #include <linux/swap.h>
- #include <linux/syscalls.h>
- #include <linux/bio.h>
- #include <linux/root_dev.h>
- #include <linux/freezer.h>
- #include <linux/reboot.h>
- #include <linux/writeback.h>
- #include <linux/tty.h>
- #include <linux/crypto.h>
- #include <linux/cpu.h>
- #include <linux/ctype.h>
- #include <linux/kthread.h>
- #include "tuxonice_io.h"
- #include "tuxonice.h"
- #include "tuxonice_extent.h"
- #include "tuxonice_netlink.h"
- #include "tuxonice_prepare_image.h"
- #include "tuxonice_ui.h"
- #include "tuxonice_sysfs.h"
- #include "tuxonice_pagedir.h"
- #include "tuxonice_modules.h"
- #include "tuxonice_builtin.h"
- #include "tuxonice_power_off.h"
- #include "tuxonice_alloc.h"
- unsigned long toi_bootflags_mask;
- EXPORT_SYMBOL_GPL(toi_bootflags_mask);
- /*
- * Highmem related functions (x86 only).
- */
- #ifdef CONFIG_HIGHMEM
- /**
- * copyback_high: Restore highmem pages.
- *
- * Highmem data and pbe lists are/can be stored in highmem.
- * The format is slightly different to the lowmem pbe lists
- * used for the assembly code: the last pbe in each page is
- * a struct page * instead of struct pbe *, pointing to the
- * next page where pbes are stored (or NULL if happens to be
- * the end of the list). Since we don't want to generate
- * unnecessary deltas against swsusp code, we use a cast
- * instead of a union.
- **/
- static void copyback_high(void)
- {
- struct page *pbe_page = (struct page *)restore_highmem_pblist;
- struct pbe *this_pbe, *first_pbe;
- unsigned long *origpage, *copypage;
- int pbe_index = 1;
- if (!pbe_page)
- return;
- this_pbe = (struct pbe *)kmap_atomic(pbe_page);
- first_pbe = this_pbe;
- while (this_pbe) {
- int loop = (PAGE_SIZE / sizeof(unsigned long)) - 1;
- origpage = kmap_atomic(pfn_to_page((unsigned long)this_pbe->orig_address));
- copypage = kmap_atomic((struct page *)this_pbe->address);
- while (loop >= 0) {
- *(origpage + loop) = *(copypage + loop);
- loop--;
- }
- kunmap_atomic(origpage);
- kunmap_atomic(copypage);
- if (!this_pbe->next)
- break;
- if (pbe_index < PBES_PER_PAGE) {
- this_pbe++;
- pbe_index++;
- } else {
- pbe_page = (struct page *)this_pbe->next;
- kunmap_atomic(first_pbe);
- if (!pbe_page)
- return;
- this_pbe = (struct pbe *)kmap_atomic(pbe_page);
- first_pbe = this_pbe;
- pbe_index = 1;
- }
- }
- kunmap_atomic(first_pbe);
- }
- #else /* CONFIG_HIGHMEM */
- static void copyback_high(void)
- {
- }
- #endif
- char toi_wait_for_keypress_dev_console(int timeout)
- {
- int fd, this_timeout = 255, orig_kthread = 0;
- char key = '\0';
- struct termios t, t_backup;
- /* We should be guaranteed /dev/console exists after populate_rootfs()
- * in init/main.c.
- */
- fd = sys_open("/dev/console", O_RDONLY, 0);
- if (fd < 0) {
- pr_warn("Couldn't open /dev/console.\n");
- return key;
- }
- if (sys_ioctl(fd, TCGETS, (long)&t) < 0)
- goto out_close;
- memcpy(&t_backup, &t, sizeof(t));
- t.c_lflag &= ~(ISIG | ICANON | ECHO);
- t.c_cc[VMIN] = 0;
- new_timeout:
- if (timeout > 0) {
- this_timeout = timeout < 26 ? timeout : 25;
- timeout -= this_timeout;
- this_timeout *= 10;
- }
- t.c_cc[VTIME] = this_timeout;
- if (sys_ioctl(fd, TCSETS, (long)&t) < 0)
- goto out_restore;
- if (current->flags & PF_KTHREAD) {
- orig_kthread = (current->flags & PF_KTHREAD);
- current->flags &= ~PF_KTHREAD;
- }
- while (1) {
- if (sys_read(fd, &key, 1) <= 0) {
- if (timeout)
- goto new_timeout;
- key = '\0';
- break;
- }
- key = tolower(key);
- if (test_toi_state(TOI_SANITY_CHECK_PROMPT)) {
- if (key == 'c') {
- set_toi_state(TOI_CONTINUE_REQ);
- break;
- } else if (key == ' ')
- break;
- } else
- break;
- }
- if (orig_kthread)
- current->flags |= PF_KTHREAD;
- out_restore:
- sys_ioctl(fd, TCSETS, (long)&t_backup);
- out_close:
- sys_close(fd);
- return key;
- }
- EXPORT_SYMBOL_GPL(toi_wait_for_keypress_dev_console);
- struct toi_boot_kernel_data __nosavedata __aligned(PAGE_SIZE) toi_bkd = {
- MY_BOOT_KERNEL_DATA_VERSION,
- 0,
- #ifdef CONFIG_TOI_REPLACE_SWSUSP
- (1 << TOI_REPLACE_SWSUSP) |
- #endif
- (1 << TOI_NO_FLUSHER_THREAD) | (1 << TOI_PAGESET2_FULL)
- };
- EXPORT_SYMBOL_GPL(toi_bkd);
- struct block_device *toi_open_by_devnum(dev_t dev)
- {
- struct block_device *bdev = bdget(dev);
- int err = -ENOMEM;
- if (bdev)
- err = blkdev_get(bdev, FMODE_READ | FMODE_NDELAY, NULL);
- return err ? ERR_PTR(err) : bdev;
- }
- EXPORT_SYMBOL_GPL(toi_open_by_devnum);
- /**
- * toi_close_bdev: Close a swap bdev.
- *
- * int: The swap entry number to close.
- */
- void toi_close_bdev(struct block_device *bdev)
- {
- blkdev_put(bdev, FMODE_READ | FMODE_NDELAY);
- }
- EXPORT_SYMBOL_GPL(toi_close_bdev);
- int toi_wait = CONFIG_TOI_DEFAULT_WAIT;
- EXPORT_SYMBOL_GPL(toi_wait);
- struct toi_core_fns *toi_core_fns;
- EXPORT_SYMBOL_GPL(toi_core_fns);
- unsigned long toi_result;
- EXPORT_SYMBOL_GPL(toi_result);
- struct pagedir pagedir1 = { 1 };
- EXPORT_SYMBOL_GPL(pagedir1);
- unsigned long toi_get_nonconflicting_page(void)
- {
- return toi_core_fns->get_nonconflicting_page();
- }
- int toi_post_context_save(void)
- {
- return toi_core_fns->post_context_save();
- }
- int try_tuxonice_hibernate(void)
- {
- if (!toi_core_fns)
- return -ENODEV;
- return toi_core_fns->try_hibernate();
- }
- static int num_resume_calls;
- #ifdef CONFIG_TOI_IGNORE_LATE_INITCALL
- static int ignore_late_initcall = 1;
- #else
- static int ignore_late_initcall;
- #endif
- #ifdef CONFIG_TOI_ENHANCE
- int toi_ignore_late_initcall(void)
- {
- return ignore_late_initcall;
- }
- EXPORT_SYMBOL_GPL(toi_ignore_late_initcall);
- #endif
- int toi_translate_err_default = TOI_CONTINUE_REQ;
- EXPORT_SYMBOL_GPL(toi_translate_err_default);
- void try_tuxonice_resume(void)
- {
- if (!hibernation_available())
- return;
- /* Don't let it wrap around eventually */
- if (num_resume_calls < 2)
- num_resume_calls++;
- if (num_resume_calls == 1 && ignore_late_initcall) {
- pr_warn("TuxOnIce: Ignoring late initcall, as requested.\n");
- return;
- }
- if (toi_core_fns)
- toi_core_fns->try_resume();
- else
- pr_warn("TuxOnIce core not loaded yet.\n");
- }
- int toi_lowlevel_builtin(void)
- {
- int error = 0;
- save_processor_state();
- error = swsusp_arch_suspend();
- if (error)
- pr_err("Error %d hibernating\n", error);
- #ifdef CONFIG_TOI_ENHANCE
- if (test_result_state(TOI_ARCH_PREPARE_FAILED))
- hib_err("CAUTION: error(%d/0x%08x)\n", error, (unsigned int)toi_result);
- #endif
- /* Restore control flow appears here */
- if (!toi_in_hibernate) {
- copyback_high();
- set_toi_state(TOI_NOW_RESUMING);
- }
- restore_processor_state();
- return error;
- }
- EXPORT_SYMBOL_GPL(toi_lowlevel_builtin);
- unsigned long toi_compress_bytes_in;
- EXPORT_SYMBOL_GPL(toi_compress_bytes_in);
- unsigned long toi_compress_bytes_out;
- EXPORT_SYMBOL_GPL(toi_compress_bytes_out);
- int toi_in_suspend(void)
- {
- return in_suspend;
- }
- EXPORT_SYMBOL_GPL(toi_in_suspend);
- unsigned long toi_state = ((1 << TOI_BOOT_TIME) |
- (1 << TOI_IGNORE_LOGLEVEL) | (1 << TOI_IO_STOPPED));
- EXPORT_SYMBOL_GPL(toi_state);
- /* The number of hibernates we have started (some may have been cancelled) */
- unsigned int nr_hibernates;
- EXPORT_SYMBOL_GPL(nr_hibernates);
- int toi_running;
- EXPORT_SYMBOL_GPL(toi_running);
- __nosavedata int toi_in_hibernate;
- EXPORT_SYMBOL_GPL(toi_in_hibernate);
- __nosavedata struct pbe *restore_highmem_pblist;
- EXPORT_SYMBOL_GPL(restore_highmem_pblist);
- int toi_trace_allocs;
- EXPORT_SYMBOL_GPL(toi_trace_allocs);
- void toi_read_lock_tasklist(void)
- {
- read_lock(&tasklist_lock);
- }
- EXPORT_SYMBOL_GPL(toi_read_lock_tasklist);
- void toi_read_unlock_tasklist(void)
- {
- read_unlock(&tasklist_lock);
- }
- EXPORT_SYMBOL_GPL(toi_read_unlock_tasklist);
- #ifdef CONFIG_TOI_ZRAM_SUPPORT
- int (*toi_flag_zram_disks)(void);
- int toi_do_flag_zram_disks(void)
- {
- return toi_flag_zram_disks ? (*toi_flag_zram_disks) () : 0;
- }
- EXPORT_SYMBOL_GPL(toi_do_flag_zram_disks);
- #endif
- static int __init toi_wait_setup(char *str)
- {
- int value;
- if (sscanf(str, "=%d", &value) == 1) {
- if (value < -1 || value > 255)
- pr_warn("TuxOnIce_wait outside range -1 to " "255.\n");
- else
- toi_wait = value;
- }
- return 1;
- }
- __setup("toi_wait", toi_wait_setup);
- static int __init toi_translate_retry_setup(char *str)
- {
- toi_translate_err_default = 0;
- return 1;
- }
- __setup("toi_translate_retry", toi_translate_retry_setup);
- static int __init toi_debug_setup(char *str)
- {
- toi_bkd.toi_action |= (1 << TOI_LOGALL);
- toi_bootflags_mask |= (1 << TOI_LOGALL);
- toi_bkd.toi_debug_state = 255;
- toi_bkd.toi_default_console_level = 7;
- return 1;
- }
- __setup("toi_debug_setup", toi_debug_setup);
- static int __init toi_pause_setup(char *str)
- {
- toi_bkd.toi_action |= (1 << TOI_PAUSE);
- toi_bootflags_mask |= (1 << TOI_PAUSE);
- return 1;
- }
- __setup("toi_pause", toi_pause_setup);
- #ifdef CONFIG_PM_DEBUG
- static int __init toi_trace_allocs_setup(char *str)
- {
- int value;
- if (sscanf(str, "=%d", &value) == 1)
- toi_trace_allocs = value;
- return 1;
- }
- __setup("toi_trace_allocs", toi_trace_allocs_setup);
- #endif
- static int __init toi_ignore_late_initcall_setup(char *str)
- {
- int value;
- if (sscanf(str, "=%d", &value) == 1)
- ignore_late_initcall = value;
- return 1;
- }
- __setup("toi_initramfs_resume_only", toi_ignore_late_initcall_setup);
- static int __init toi_force_no_multithreaded_setup(char *str)
- {
- int value;
- toi_bkd.toi_action &= ~(1 << TOI_NO_MULTITHREADED_IO);
- toi_bootflags_mask |= (1 << TOI_NO_MULTITHREADED_IO);
- if (sscanf(str, "=%d", &value) == 1 && value)
- toi_bkd.toi_action |= (1 << TOI_NO_MULTITHREADED_IO);
- return 1;
- }
- __setup("toi_no_multithreaded", toi_force_no_multithreaded_setup);
- #ifdef CONFIG_KGDB
- static int __init toi_post_resume_breakpoint_setup(char *str)
- {
- int value;
- toi_bkd.toi_action &= ~(1 << TOI_POST_RESUME_BREAKPOINT);
- toi_bootflags_mask |= (1 << TOI_POST_RESUME_BREAKPOINT);
- if (sscanf(str, "=%d", &value) == 1 && value)
- toi_bkd.toi_action |= (1 << TOI_POST_RESUME_BREAKPOINT);
- return 1;
- }
- __setup("toi_post_resume_break", toi_post_resume_breakpoint_setup);
- #endif
- static int __init toi_disable_readahead_setup(char *str)
- {
- int value;
- toi_bkd.toi_action &= ~(1 << TOI_NO_READAHEAD);
- toi_bootflags_mask |= (1 << TOI_NO_READAHEAD);
- if (sscanf(str, "=%d", &value) == 1 && value)
- toi_bkd.toi_action |= (1 << TOI_NO_READAHEAD);
- return 1;
- }
- __setup("toi_no_readahead", toi_disable_readahead_setup);
|