linux/arch/s390/kernel/suspend.c
<<
>>
Prefs
   1/*
   2 * Suspend support specific for s390.
   3 *
   4 * Copyright IBM Corp. 2009
   5 *
   6 * Author(s): Hans-Joachim Picht <hans@linux.vnet.ibm.com>
   7 */
   8
   9#include <linux/pfn.h>
  10#include <linux/suspend.h>
  11#include <linux/mm.h>
  12#include <asm/ctl_reg.h>
  13
  14/*
  15 * References to section boundaries
  16 */
  17extern const void __nosave_begin, __nosave_end;
  18
  19/*
  20 * The restore of the saved pages in an hibernation image will set
  21 * the change and referenced bits in the storage key for each page.
  22 * Overindication of the referenced bits after an hibernation cycle
  23 * does not cause any harm but the overindication of the change bits
  24 * would cause trouble.
  25 * Use the ARCH_SAVE_PAGE_KEYS hooks to save the storage key of each
  26 * page to the most significant byte of the associated page frame
  27 * number in the hibernation image.
  28 */
  29
  30/*
  31 * Key storage is allocated as a linked list of pages.
  32 * The size of the keys array is (PAGE_SIZE - sizeof(long))
  33 */
  34struct page_key_data {
  35        struct page_key_data *next;
  36        unsigned char data[];
  37};
  38
  39#define PAGE_KEY_DATA_SIZE      (PAGE_SIZE - sizeof(struct page_key_data *))
  40
  41static struct page_key_data *page_key_data;
  42static struct page_key_data *page_key_rp, *page_key_wp;
  43static unsigned long page_key_rx, page_key_wx;
  44
  45/*
  46 * For each page in the hibernation image one additional byte is
  47 * stored in the most significant byte of the page frame number.
  48 * On suspend no additional memory is required but on resume the
  49 * keys need to be memorized until the page data has been restored.
  50 * Only then can the storage keys be set to their old state.
  51 */
  52unsigned long page_key_additional_pages(unsigned long pages)
  53{
  54        return DIV_ROUND_UP(pages, PAGE_KEY_DATA_SIZE);
  55}
  56
  57/*
  58 * Free page_key_data list of arrays.
  59 */
  60void page_key_free(void)
  61{
  62        struct page_key_data *pkd;
  63
  64        while (page_key_data) {
  65                pkd = page_key_data;
  66                page_key_data = pkd->next;
  67                free_page((unsigned long) pkd);
  68        }
  69}
  70
  71/*
  72 * Allocate page_key_data list of arrays with enough room to store
  73 * one byte for each page in the hibernation image.
  74 */
  75int page_key_alloc(unsigned long pages)
  76{
  77        struct page_key_data *pk;
  78        unsigned long size;
  79
  80        size = DIV_ROUND_UP(pages, PAGE_KEY_DATA_SIZE);
  81        while (size--) {
  82                pk = (struct page_key_data *) get_zeroed_page(GFP_KERNEL);
  83                if (!pk) {
  84                        page_key_free();
  85                        return -ENOMEM;
  86                }
  87                pk->next = page_key_data;
  88                page_key_data = pk;
  89        }
  90        page_key_rp = page_key_wp = page_key_data;
  91        page_key_rx = page_key_wx = 0;
  92        return 0;
  93}
  94
  95/*
  96 * Save the storage key into the upper 8 bits of the page frame number.
  97 */
  98void page_key_read(unsigned long *pfn)
  99{
 100        unsigned long addr;
 101
 102        addr = (unsigned long) page_address(pfn_to_page(*pfn));
 103        *(unsigned char *) pfn = (unsigned char) page_get_storage_key(addr);
 104}
 105
 106/*
 107 * Extract the storage key from the upper 8 bits of the page frame number
 108 * and store it in the page_key_data list of arrays.
 109 */
 110void page_key_memorize(unsigned long *pfn)
 111{
 112        page_key_wp->data[page_key_wx] = *(unsigned char *) pfn;
 113        *(unsigned char *) pfn = 0;
 114        if (++page_key_wx < PAGE_KEY_DATA_SIZE)
 115                return;
 116        page_key_wp = page_key_wp->next;
 117        page_key_wx = 0;
 118}
 119
 120/*
 121 * Get the next key from the page_key_data list of arrays and set the
 122 * storage key of the page referred by @address. If @address refers to
 123 * a "safe" page the swsusp_arch_resume code will transfer the storage
 124 * key from the buffer page to the original page.
 125 */
 126void page_key_write(void *address)
 127{
 128        page_set_storage_key((unsigned long) address,
 129                             page_key_rp->data[page_key_rx], 0);
 130        if (++page_key_rx >= PAGE_KEY_DATA_SIZE)
 131                return;
 132        page_key_rp = page_key_rp->next;
 133        page_key_rx = 0;
 134}
 135
 136int pfn_is_nosave(unsigned long pfn)
 137{
 138        unsigned long nosave_begin_pfn = PFN_DOWN(__pa(&__nosave_begin));
 139        unsigned long nosave_end_pfn = PFN_DOWN(__pa(&__nosave_end));
 140
 141        /* Always save lowcore pages (LC protection might be enabled). */
 142        if (pfn <= LC_PAGES)
 143                return 0;
 144        if (pfn >= nosave_begin_pfn && pfn < nosave_end_pfn)
 145                return 1;
 146        /* Skip memory holes and read-only pages (NSS, DCSS, ...). */
 147        if (tprot(PFN_PHYS(pfn)))
 148                return 1;
 149        return 0;
 150}
 151
 152void save_processor_state(void)
 153{
 154        /* swsusp_arch_suspend() actually saves all cpu register contents.
 155         * Machine checks must be disabled since swsusp_arch_suspend() stores
 156         * register contents to their lowcore save areas. That's the same
 157         * place where register contents on machine checks would be saved.
 158         * To avoid register corruption disable machine checks.
 159         * We must also disable machine checks in the new psw mask for
 160         * program checks, since swsusp_arch_suspend() may generate program
 161         * checks. Disabling machine checks for all other new psw masks is
 162         * just paranoia.
 163         */
 164        local_mcck_disable();
 165        /* Disable lowcore protection */
 166        __ctl_clear_bit(0,28);
 167        S390_lowcore.external_new_psw.mask &= ~PSW_MASK_MCHECK;
 168        S390_lowcore.svc_new_psw.mask &= ~PSW_MASK_MCHECK;
 169        S390_lowcore.io_new_psw.mask &= ~PSW_MASK_MCHECK;
 170        S390_lowcore.program_new_psw.mask &= ~PSW_MASK_MCHECK;
 171}
 172
 173void restore_processor_state(void)
 174{
 175        S390_lowcore.external_new_psw.mask |= PSW_MASK_MCHECK;
 176        S390_lowcore.svc_new_psw.mask |= PSW_MASK_MCHECK;
 177        S390_lowcore.io_new_psw.mask |= PSW_MASK_MCHECK;
 178        S390_lowcore.program_new_psw.mask |= PSW_MASK_MCHECK;
 179        /* Enable lowcore protection */
 180        __ctl_set_bit(0,28);
 181        local_mcck_enable();
 182}
 183