|
@@ -31,6 +31,9 @@
|
|
* Alex Badea <vampire@go.ro>:
|
|
* Alex Badea <vampire@go.ro>:
|
|
* Fixed runaway init
|
|
* Fixed runaway init
|
|
*
|
|
*
|
|
|
|
+ * Andreas Steinmetz <ast@domdv.de>:
|
|
|
|
+ * Added encrypted suspend option
|
|
|
|
+ *
|
|
* More state savers are welcome. Especially for the scsi layer...
|
|
* More state savers are welcome. Especially for the scsi layer...
|
|
*
|
|
*
|
|
* For TODOs,FIXMEs also look in Documentation/power/swsusp.txt
|
|
* For TODOs,FIXMEs also look in Documentation/power/swsusp.txt
|
|
@@ -71,8 +74,16 @@
|
|
#include <asm/tlbflush.h>
|
|
#include <asm/tlbflush.h>
|
|
#include <asm/io.h>
|
|
#include <asm/io.h>
|
|
|
|
|
|
|
|
+#include <linux/random.h>
|
|
|
|
+#include <linux/crypto.h>
|
|
|
|
+#include <asm/scatterlist.h>
|
|
|
|
+
|
|
#include "power.h"
|
|
#include "power.h"
|
|
|
|
|
|
|
|
+#define CIPHER "aes"
|
|
|
|
+#define MAXKEY 32
|
|
|
|
+#define MAXIV 32
|
|
|
|
+
|
|
/* References to section boundaries */
|
|
/* References to section boundaries */
|
|
extern const void __nosave_begin, __nosave_end;
|
|
extern const void __nosave_begin, __nosave_end;
|
|
|
|
|
|
@@ -103,7 +114,8 @@ static suspend_pagedir_t *pagedir_save;
|
|
#define SWSUSP_SIG "S1SUSPEND"
|
|
#define SWSUSP_SIG "S1SUSPEND"
|
|
|
|
|
|
static struct swsusp_header {
|
|
static struct swsusp_header {
|
|
- char reserved[PAGE_SIZE - 20 - sizeof(swp_entry_t)];
|
|
|
|
|
|
+ char reserved[PAGE_SIZE - 20 - MAXKEY - MAXIV - sizeof(swp_entry_t)];
|
|
|
|
+ u8 key_iv[MAXKEY+MAXIV];
|
|
swp_entry_t swsusp_info;
|
|
swp_entry_t swsusp_info;
|
|
char orig_sig[10];
|
|
char orig_sig[10];
|
|
char sig[10];
|
|
char sig[10];
|
|
@@ -129,6 +141,131 @@ static struct swsusp_info swsusp_info;
|
|
static unsigned short swapfile_used[MAX_SWAPFILES];
|
|
static unsigned short swapfile_used[MAX_SWAPFILES];
|
|
static unsigned short root_swap;
|
|
static unsigned short root_swap;
|
|
|
|
|
|
|
|
+static int write_page(unsigned long addr, swp_entry_t * loc);
|
|
|
|
+static int bio_read_page(pgoff_t page_off, void * page);
|
|
|
|
+
|
|
|
|
+static u8 key_iv[MAXKEY+MAXIV];
|
|
|
|
+
|
|
|
|
+#ifdef CONFIG_SWSUSP_ENCRYPT
|
|
|
|
+
|
|
|
|
+static int crypto_init(int mode, void **mem)
|
|
|
|
+{
|
|
|
|
+ int error = 0;
|
|
|
|
+ int len;
|
|
|
|
+ char *modemsg;
|
|
|
|
+ struct crypto_tfm *tfm;
|
|
|
|
+
|
|
|
|
+ modemsg = mode ? "suspend not possible" : "resume not possible";
|
|
|
|
+
|
|
|
|
+ tfm = crypto_alloc_tfm(CIPHER, CRYPTO_TFM_MODE_CBC);
|
|
|
|
+ if(!tfm) {
|
|
|
|
+ printk(KERN_ERR "swsusp: no tfm, %s\n", modemsg);
|
|
|
|
+ error = -EINVAL;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if(MAXKEY < crypto_tfm_alg_min_keysize(tfm)) {
|
|
|
|
+ printk(KERN_ERR "swsusp: key buffer too small, %s\n", modemsg);
|
|
|
|
+ error = -ENOKEY;
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (mode)
|
|
|
|
+ get_random_bytes(key_iv, MAXKEY+MAXIV);
|
|
|
|
+
|
|
|
|
+ len = crypto_tfm_alg_max_keysize(tfm);
|
|
|
|
+ if (len > MAXKEY)
|
|
|
|
+ len = MAXKEY;
|
|
|
|
+
|
|
|
|
+ if (crypto_cipher_setkey(tfm, key_iv, len)) {
|
|
|
|
+ printk(KERN_ERR "swsusp: key setup failure, %s\n", modemsg);
|
|
|
|
+ error = -EKEYREJECTED;
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ len = crypto_tfm_alg_ivsize(tfm);
|
|
|
|
+
|
|
|
|
+ if (MAXIV < len) {
|
|
|
|
+ printk(KERN_ERR "swsusp: iv buffer too small, %s\n", modemsg);
|
|
|
|
+ error = -EOVERFLOW;
|
|
|
|
+ goto fail;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ crypto_cipher_set_iv(tfm, key_iv+MAXKEY, len);
|
|
|
|
+
|
|
|
|
+ *mem=(void *)tfm;
|
|
|
|
+
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+fail: crypto_free_tfm(tfm);
|
|
|
|
+out: return error;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static __inline__ void crypto_exit(void *mem)
|
|
|
|
+{
|
|
|
|
+ crypto_free_tfm((struct crypto_tfm *)mem);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static __inline__ int crypto_write(struct pbe *p, void *mem)
|
|
|
|
+{
|
|
|
|
+ int error = 0;
|
|
|
|
+ struct scatterlist src, dst;
|
|
|
|
+
|
|
|
|
+ src.page = virt_to_page(p->address);
|
|
|
|
+ src.offset = 0;
|
|
|
|
+ src.length = PAGE_SIZE;
|
|
|
|
+ dst.page = virt_to_page((void *)&swsusp_header);
|
|
|
|
+ dst.offset = 0;
|
|
|
|
+ dst.length = PAGE_SIZE;
|
|
|
|
+
|
|
|
|
+ error = crypto_cipher_encrypt((struct crypto_tfm *)mem, &dst, &src,
|
|
|
|
+ PAGE_SIZE);
|
|
|
|
+
|
|
|
|
+ if (!error)
|
|
|
|
+ error = write_page((unsigned long)&swsusp_header,
|
|
|
|
+ &(p->swap_address));
|
|
|
|
+ return error;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static __inline__ int crypto_read(struct pbe *p, void *mem)
|
|
|
|
+{
|
|
|
|
+ int error = 0;
|
|
|
|
+ struct scatterlist src, dst;
|
|
|
|
+
|
|
|
|
+ error = bio_read_page(swp_offset(p->swap_address), (void *)p->address);
|
|
|
|
+ if (!error) {
|
|
|
|
+ src.offset = 0;
|
|
|
|
+ src.length = PAGE_SIZE;
|
|
|
|
+ dst.offset = 0;
|
|
|
|
+ dst.length = PAGE_SIZE;
|
|
|
|
+ src.page = dst.page = virt_to_page((void *)p->address);
|
|
|
|
+
|
|
|
|
+ error = crypto_cipher_decrypt((struct crypto_tfm *)mem, &dst,
|
|
|
|
+ &src, PAGE_SIZE);
|
|
|
|
+ }
|
|
|
|
+ return error;
|
|
|
|
+}
|
|
|
|
+#else
|
|
|
|
+static __inline__ int crypto_init(int mode, void *mem)
|
|
|
|
+{
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static __inline__ void crypto_exit(void *mem)
|
|
|
|
+{
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static __inline__ int crypto_write(struct pbe *p, void *mem)
|
|
|
|
+{
|
|
|
|
+ return write_page(p->address, &(p->swap_address));
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static __inline__ int crypto_read(struct pbe *p, void *mem)
|
|
|
|
+{
|
|
|
|
+ return bio_read_page(swp_offset(p->swap_address), (void *)p->address);
|
|
|
|
+}
|
|
|
|
+#endif
|
|
|
|
+
|
|
static int mark_swapfiles(swp_entry_t prev)
|
|
static int mark_swapfiles(swp_entry_t prev)
|
|
{
|
|
{
|
|
int error;
|
|
int error;
|
|
@@ -140,6 +277,7 @@ static int mark_swapfiles(swp_entry_t prev)
|
|
!memcmp("SWAPSPACE2",swsusp_header.sig, 10)) {
|
|
!memcmp("SWAPSPACE2",swsusp_header.sig, 10)) {
|
|
memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10);
|
|
memcpy(swsusp_header.orig_sig,swsusp_header.sig, 10);
|
|
memcpy(swsusp_header.sig,SWSUSP_SIG, 10);
|
|
memcpy(swsusp_header.sig,SWSUSP_SIG, 10);
|
|
|
|
+ memcpy(swsusp_header.key_iv, key_iv, MAXKEY+MAXIV);
|
|
swsusp_header.swsusp_info = prev;
|
|
swsusp_header.swsusp_info = prev;
|
|
error = rw_swap_page_sync(WRITE,
|
|
error = rw_swap_page_sync(WRITE,
|
|
swp_entry(root_swap, 0),
|
|
swp_entry(root_swap, 0),
|
|
@@ -286,6 +424,10 @@ static int data_write(void)
|
|
int error = 0, i = 0;
|
|
int error = 0, i = 0;
|
|
unsigned int mod = nr_copy_pages / 100;
|
|
unsigned int mod = nr_copy_pages / 100;
|
|
struct pbe *p;
|
|
struct pbe *p;
|
|
|
|
+ void *tfm;
|
|
|
|
+
|
|
|
|
+ if ((error = crypto_init(1, &tfm)))
|
|
|
|
+ return error;
|
|
|
|
|
|
if (!mod)
|
|
if (!mod)
|
|
mod = 1;
|
|
mod = 1;
|
|
@@ -294,11 +436,14 @@ static int data_write(void)
|
|
for_each_pbe (p, pagedir_nosave) {
|
|
for_each_pbe (p, pagedir_nosave) {
|
|
if (!(i%mod))
|
|
if (!(i%mod))
|
|
printk( "\b\b\b\b%3d%%", i / mod );
|
|
printk( "\b\b\b\b%3d%%", i / mod );
|
|
- if ((error = write_page(p->address, &(p->swap_address))))
|
|
|
|
|
|
+ if ((error = crypto_write(p, tfm))) {
|
|
|
|
+ crypto_exit(tfm);
|
|
return error;
|
|
return error;
|
|
|
|
+ }
|
|
i++;
|
|
i++;
|
|
}
|
|
}
|
|
printk("\b\b\b\bdone\n");
|
|
printk("\b\b\b\bdone\n");
|
|
|
|
+ crypto_exit(tfm);
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -400,6 +545,7 @@ static int write_suspend_image(void)
|
|
if ((error = close_swap()))
|
|
if ((error = close_swap()))
|
|
goto FreePagedir;
|
|
goto FreePagedir;
|
|
Done:
|
|
Done:
|
|
|
|
+ memset(key_iv, 0, MAXKEY+MAXIV);
|
|
return error;
|
|
return error;
|
|
FreePagedir:
|
|
FreePagedir:
|
|
free_pagedir_entries();
|
|
free_pagedir_entries();
|
|
@@ -1212,6 +1358,8 @@ static int check_sig(void)
|
|
return error;
|
|
return error;
|
|
if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) {
|
|
if (!memcmp(SWSUSP_SIG, swsusp_header.sig, 10)) {
|
|
memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10);
|
|
memcpy(swsusp_header.sig, swsusp_header.orig_sig, 10);
|
|
|
|
+ memcpy(key_iv, swsusp_header.key_iv, MAXKEY+MAXIV);
|
|
|
|
+ memset(swsusp_header.key_iv, 0, MAXKEY+MAXIV);
|
|
|
|
|
|
/*
|
|
/*
|
|
* Reset swap signature now.
|
|
* Reset swap signature now.
|
|
@@ -1239,6 +1387,10 @@ static int data_read(struct pbe *pblist)
|
|
int error = 0;
|
|
int error = 0;
|
|
int i = 0;
|
|
int i = 0;
|
|
int mod = swsusp_info.image_pages / 100;
|
|
int mod = swsusp_info.image_pages / 100;
|
|
|
|
+ void *tfm;
|
|
|
|
+
|
|
|
|
+ if ((error = crypto_init(0, &tfm)))
|
|
|
|
+ return error;
|
|
|
|
|
|
if (!mod)
|
|
if (!mod)
|
|
mod = 1;
|
|
mod = 1;
|
|
@@ -1250,14 +1402,15 @@ static int data_read(struct pbe *pblist)
|
|
if (!(i % mod))
|
|
if (!(i % mod))
|
|
printk("\b\b\b\b%3d%%", i / mod);
|
|
printk("\b\b\b\b%3d%%", i / mod);
|
|
|
|
|
|
- error = bio_read_page(swp_offset(p->swap_address),
|
|
|
|
- (void *)p->address);
|
|
|
|
- if (error)
|
|
|
|
|
|
+ if ((error = crypto_read(p, tfm))) {
|
|
|
|
+ crypto_exit(tfm);
|
|
return error;
|
|
return error;
|
|
|
|
+ }
|
|
|
|
|
|
i++;
|
|
i++;
|
|
}
|
|
}
|
|
printk("\b\b\b\bdone\n");
|
|
printk("\b\b\b\bdone\n");
|
|
|
|
+ crypto_exit(tfm);
|
|
return error;
|
|
return error;
|
|
}
|
|
}
|
|
|
|
|
|
@@ -1385,6 +1538,7 @@ int swsusp_read(void)
|
|
|
|
|
|
error = read_suspend_image();
|
|
error = read_suspend_image();
|
|
blkdev_put(resume_bdev);
|
|
blkdev_put(resume_bdev);
|
|
|
|
+ memset(key_iv, 0, MAXKEY+MAXIV);
|
|
|
|
|
|
if (!error)
|
|
if (!error)
|
|
pr_debug("swsusp: Reading resume file was successful\n");
|
|
pr_debug("swsusp: Reading resume file was successful\n");
|