Skip to content

Commit

Permalink
hw_detection: Initial implementation
Browse files Browse the repository at this point in the history
Impact: Abort resume if a dmi mismatch is detected

Some situations are not resume safe, for instance when the hard drive is
put in another machine. In that case, we shouldn't try to resume but
rather invalidate the image and do a full boot.

To perform this detection, we rely on the DMI_PRODUCT_UUID that is
stored in the hibernate image.

Testing Done: hibernated on a T500. Put the drive on a DELL E6400.
Tried to resume. The kernel printed the mismatch message and did a full
boot. Hibernated on the DELL. Put the drive back in the T500. Tried to
resume. Same scenario: The kernel printed the mismatch message and did a
full boot.

Signed-off-by: Pierre-Alexandre Meyer <pierre@mouraf.org>
  • Loading branch information
pierre committed Feb 28, 2009
1 parent 9ecad8a commit 9705b9e
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 2 deletions.
46 changes: 45 additions & 1 deletion kernel/power/tuxonice_builtin.c
Expand Up @@ -17,6 +17,8 @@
#include <linux/crypto.h>
#include <linux/cpu.h>
#include <linux/ctype.h>
#include <linux/dmi.h>

#include "tuxonice_io.h"
#include "tuxonice.h"
#include "tuxonice_extent.h"
Expand Down Expand Up @@ -223,6 +225,46 @@ static int ignore_late_initcall = 1;
static int ignore_late_initcall;
#endif

/**
* toi_try_resume -- resume path entry point
*
* All core devices should be up and running by now.
* Before trying to resume, we need to check if the hardware has
* fundamentally changed (and would require a full boot).
*
* How can the hardware change?
* The use cases are:
* o Memory: Linux, as well as TuxOnIce, support memory hotplugging.
* o PCI devices (Wifi cards, docking stations, ...). Most should be
* hotpluggables.
* o USB devices: hotpluggable.
* o SATA devices (cdroms, ...): XXX
* o Hard Drive put in another board (common for laptops).
*
* All of the hotpluggable devices can be handled by post resume hooks:
* we can force reprobing for complicated ones (Docking Stations,
* Memory, ...) when resuming.
* The difficult use case is when a disk is put in another board. In
* that case, it's a whole new world. A full boot is needed.
* We could use for_each_pci_dev() and hash the system (hash /sys
* basically) but this would be too complicated and dangerous.
*
* To detect such a scenario, we use the dmi product uuid. The value
* when hibernating is written to the hibernated image.
*
* See Section 3.3.2 of the DMI spec for DMI_PRODUCT_UUID:
* http://www.dmtf.org/standards/published_documents/DSP0134_2.6.0.pdf
*
* Corner cases TODO:
* o If the value is FFh, the DMI has not been set and can be
* rewritten. What to do? Use vendor?
* o If the value is 00h, the DMI has not been set. What to do? Use
* vendor?
*
* Side Effects:
* hw_uuid_detected is set.
**/
char const *hw_uuid_detected;
void toi_try_resume(void)
{
/* Don't let it wrap around eventually */
Expand All @@ -234,8 +276,10 @@ void toi_try_resume(void)
return;
}

if (toi_core_fns)
if (toi_core_fns) {
hw_uuid_detected = dmi_get_system_info(DMI_PRODUCT_UUID);
toi_core_fns->try_resume();
}
else
printk(KERN_INFO "TuxOnIce core not loaded yet.\n");
}
Expand Down
33 changes: 33 additions & 0 deletions kernel/power/tuxonice_file.c
Expand Up @@ -77,11 +77,25 @@ struct toi_file_header {
int resumed_before;
unsigned long first_header_block;
int have_image;
char hw_uuid[16];
};

/* Header Page Information */
static int header_pages_reserved;

/*
* The hw uuid helps probing the hardware to check if it has changed.
* Most changes can be handled via hotplug on resume. But if the disk is
* placed in another machine, it is a whole new world - don't try to
* resume in that case. A full boot is wiser.
*/

/* Hardware uuid detected on the machine */
extern char const *hw_uuid_detected;

/* Hardware uuid found in the image */
static char const *hw_uuid_found;

/* Main Storage Pages */
static int main_pages_allocated, main_pages_requested;

Expand Down Expand Up @@ -465,6 +479,8 @@ static int parse_signature(struct toi_file_header *header)
int binary_sig = !memcmp(tuxonice_signature, header->sig,
sizeof(tuxonice_signature));

hw_uuid_found = header->hw_uuid;

if (no_image_header || (binary_sig && !header->have_image))
return 0;

Expand Down Expand Up @@ -493,6 +509,7 @@ static int prepare_signature(struct toi_file_header *current_header,
current_header->resumed_before = 0;
current_header->first_header_block = first_header_block;
current_header->have_image = 1;
strncpy(current_header->hw_uuid, hw_uuid_detected, 16);
return 0;
}

Expand Down Expand Up @@ -730,6 +747,9 @@ static int toi_file_read_header_cleanup(void)
* UNMARK_RESUME_ATTEMPTED.
* If the signature is changed, an I/O operation is performed.
* The signature exists iff toi_file_signature_op(GET_IMAGE_EXISTS)>-1.
*
* When op is GET_IMAGE_EXISTS, returns 0 if there is a mismatch in the
* hardware uuid.
**/
static int toi_file_signature_op(int op)
{
Expand Down Expand Up @@ -781,6 +801,19 @@ static int toi_file_signature_op(int op)
changed = 1;
}
break;
case GET_IMAGE_EXISTS:
/* Check hardware uuid */
if (result == 1) {
if (strncmp(hw_uuid_found, hw_uuid_detected, 16)) {
printk(KERN_NOTICE "TuxOnIce: hw uuid mismatch, "
"full boot needed.\n");
toi_file_signature_op(INVALIDATE);
result = 0;
} else
printk(KERN_INFO "TuxOnIce: hw uuid match, "
"continue resuming.\n");
}
break;
}

if (changed) {
Expand Down
31 changes: 30 additions & 1 deletion kernel/power/tuxonice_swap.c
Expand Up @@ -35,6 +35,7 @@ struct sig_data {
unsigned long sector;
int resume_attempted;
int orig_sig_type;
char hw_uuid[16];
};

union diskpage {
Expand Down Expand Up @@ -82,6 +83,19 @@ static int toi_swapon_status;
/* Header Page Information */
static long header_pages_reserved;

/*
* The hw uuid helps probing the hardware to check if it has changed.
* Most changes can be handled via hotplug on resume. But if the disk is
* placed in another machine, it is a whole new world - don't try to
* resume in that case. A full boot is wiser.
*/

/* Hardware uuid detected on the machine */
extern char const *hw_uuid_detected;

/* Hardware uuid found in the image */
static char const *hw_uuid_found;

/* Swap Pages */
static long swap_pages_allocated;

Expand Down Expand Up @@ -341,6 +355,8 @@ static int parse_signature(void)
sig = (struct sig_data *) current_signature_page;
swap_header = swap_header_page.pointer->swh.magic.magic;

hw_uuid_found = swap_header_page.pointer->sig_data.hw_uuid;

for (type = 0; type < 5; type++)
if (!memcmp(sigs[type], swap_header, strlen(sigs[type])))
return type;
Expand Down Expand Up @@ -410,6 +426,8 @@ static int write_modified_signature(int modification)
swap_header_page.pointer->sig_data.resume_attempted = 0;
swap_header_page.pointer->sig_data.orig_sig_type =
parse_signature();
strncpy(swap_header_page.pointer->sig_data.hw_uuid,
hw_uuid_detected, 16);

memcpy(swap_header_page.pointer->swh.magic.magic,
tuxonice_signature, sizeof(tuxonice_signature));
Expand Down Expand Up @@ -968,6 +986,7 @@ static int toi_swap_storage_needed(void)
* Image_exists
*
* Returns -1 if don't know, otherwise 0 (no) or 1 (yes).
* Returns 0 if there is a mismatch in the hardware uuid.
*/
static int toi_swap_image_exists(int quiet)
{
Expand Down Expand Up @@ -1015,7 +1034,17 @@ static int toi_swap_image_exists(int quiet)
if (!quiet)
printk(KERN_INFO "TuxOnIce: Detected TuxOnIce binary "
"signature.\n");
return 1;

if (strncmp(hw_uuid_found, hw_uuid_detected, 16)) {
printk(KERN_NOTICE "TuxOnIce: hw uuid mismatch, "
"full boot needed.\n");
write_modified_signature(NO_IMAGE_SIGNATURE);
return 0;
} else {
printk(KERN_INFO "TuxOnIce: hw uuid match, "
"continue resuming.\n");
return 1;
}
}

printk("Unrecognised parse_signature result (%d).\n", signature_found);
Expand Down

0 comments on commit 9705b9e

Please sign in to comment.