Linux Kernel
3.7.1
Main Page
Related Pages
Modules
Namespaces
Data Structures
Files
File List
Globals
All
Data Structures
Namespaces
Files
Functions
Variables
Typedefs
Enumerations
Enumerator
Macros
Groups
Pages
arch
x86
power
hibernate_64.c
Go to the documentation of this file.
1
/*
2
* Hibernation support for x86-64
3
*
4
* Distribute under GPLv2
5
*
6
* Copyright (c) 2007 Rafael J. Wysocki <
[email protected]
>
7
* Copyright (c) 2002 Pavel Machek <
[email protected]
>
8
* Copyright (c) 2001 Patrick Mochel <
[email protected]
>
9
*/
10
11
#include <
linux/gfp.h
>
12
#include <
linux/smp.h
>
13
#include <
linux/suspend.h
>
14
#include <
asm/proto.h
>
15
#include <asm/page.h>
16
#include <asm/pgtable.h>
17
#include <
asm/mtrr.h
>
18
#include <asm/suspend.h>
19
20
/* References to section boundaries */
21
extern
const
void
__nosave_begin
,
__nosave_end
;
22
23
/* Defined in hibernate_asm_64.S */
24
extern
int
restore_image
(
void
);
25
26
/*
27
* Address to jump to in the last phase of restore in order to get to the image
28
* kernel's text (this value is passed in the image header).
29
*/
30
unsigned
long
restore_jump_address
;
31
32
/*
33
* Value of the cr3 register from before the hibernation (this value is passed
34
* in the image header).
35
*/
36
unsigned
long
restore_cr3
;
37
38
pgd_t
*
temp_level4_pgt
;
39
40
void
*
relocated_restore_code
;
41
42
static
int
res_phys_pud_init(
pud_t
*pud,
unsigned
long
address
,
unsigned
long
end
)
43
{
44
long
i
,
j
;
45
46
i =
pud_index
(address);
47
pud = pud +
i
;
48
for
(; i <
PTRS_PER_PUD
; pud++, i++) {
49
unsigned
long
paddr
;
50
pmd_t
*
pmd
;
51
52
paddr = address + i*
PUD_SIZE
;
53
if
(paddr >= end)
54
break
;
55
56
pmd = (
pmd_t
*)
get_safe_page
(
GFP_ATOMIC
);
57
if
(!pmd)
58
return
-
ENOMEM
;
59
set_pud
(pud,
__pud
(
__pa
(pmd) |
_KERNPG_TABLE
));
60
for
(j = 0; j <
PTRS_PER_PMD
; pmd++, j++, paddr +=
PMD_SIZE
) {
61
unsigned
long
pe;
62
63
if
(paddr >= end)
64
break
;
65
pe =
__PAGE_KERNEL_LARGE_EXEC
|
paddr
;
66
pe &=
__supported_pte_mask
;
67
set_pmd
(pmd,
__pmd
(pe));
68
}
69
}
70
return
0;
71
}
72
73
static
int
set_up_temporary_mappings(
void
)
74
{
75
unsigned
long
start
,
end
,
next
;
76
int
error
;
77
78
temp_level4_pgt = (
pgd_t
*)
get_safe_page
(
GFP_ATOMIC
);
79
if
(!temp_level4_pgt)
80
return
-
ENOMEM
;
81
82
/* It is safe to reuse the original kernel mapping */
83
set_pgd
(temp_level4_pgt +
pgd_index
(
__START_KERNEL_map
),
84
init_level4_pgt[
pgd_index
(
__START_KERNEL_map
)]);
85
86
/* Set up the direct mapping from scratch */
87
start = (
unsigned
long
)
pfn_to_kaddr
(0);
88
end = (
unsigned
long
)
pfn_to_kaddr
(
max_pfn
);
89
90
for
(; start <
end
; start =
next
) {
91
pud_t
*pud = (
pud_t
*)
get_safe_page
(
GFP_ATOMIC
);
92
if
(!pud)
93
return
-
ENOMEM
;
94
next = start +
PGDIR_SIZE
;
95
if
(next > end)
96
next =
end
;
97
if
((error = res_phys_pud_init(pud,
__pa
(start),
__pa
(next))))
98
return
error;
99
set_pgd
(temp_level4_pgt +
pgd_index
(start),
100
mk_kernel_pgd
(
__pa
(pud)));
101
}
102
return
0;
103
}
104
105
int
swsusp_arch_resume
(
void
)
106
{
107
int
error
;
108
109
/* We have got enough memory and from now on we cannot recover */
110
if
((error = set_up_temporary_mappings()))
111
return
error
;
112
113
relocated_restore_code
= (
void
*)
get_safe_page
(
GFP_ATOMIC
);
114
if
(!
relocated_restore_code
)
115
return
-
ENOMEM
;
116
memcpy
(
relocated_restore_code
, &
core_restore_code
,
117
&
restore_registers
- &
core_restore_code
);
118
119
restore_image
();
120
return
0;
121
}
122
123
/*
124
* pfn_is_nosave - check if given pfn is in the 'nosave' section
125
*/
126
127
int
pfn_is_nosave
(
unsigned
long
pfn)
128
{
129
unsigned
long
nosave_begin_pfn =
__pa_symbol
(&
__nosave_begin
) >>
PAGE_SHIFT
;
130
unsigned
long
nosave_end_pfn =
PAGE_ALIGN
(
__pa_symbol
(&
__nosave_end
)) >>
PAGE_SHIFT
;
131
return
(pfn >= nosave_begin_pfn) && (pfn < nosave_end_pfn);
132
}
133
134
struct
restore_data_record
{
135
unsigned
long
jump_address
;
136
unsigned
long
cr3
;
137
unsigned
long
magic
;
138
};
139
140
#define RESTORE_MAGIC 0x0123456789ABCDEFUL
141
147
int
arch_hibernation_header_save
(
void
*
addr
,
unsigned
int
max_size
)
148
{
149
struct
restore_data_record
*rdr =
addr
;
150
151
if
(max_size <
sizeof
(
struct
restore_data_record
))
152
return
-
EOVERFLOW
;
153
rdr->
jump_address
=
restore_jump_address
;
154
rdr->
cr3
=
restore_cr3
;
155
rdr->
magic
=
RESTORE_MAGIC
;
156
return
0;
157
}
158
164
int
arch_hibernation_header_restore
(
void
*
addr
)
165
{
166
struct
restore_data_record
*rdr =
addr
;
167
168
restore_jump_address
= rdr->
jump_address
;
169
restore_cr3
= rdr->
cr3
;
170
return
(rdr->
magic
==
RESTORE_MAGIC
) ? 0 : -
EINVAL
;
171
}
Generated on Thu Jan 10 2013 13:21:50 for Linux Kernel by
1.8.2