Skip to content

Commit a2c2e67

Browse files
soleenwilldeacon
authored andcommitted
arm64: hibernate: add trans_pgd public functions
trans_pgd_create_copy() and trans_pgd_map_page() are going to be the basis for new shared code that handles page tables for cases which are between kernels: kexec, and hibernate. Note: Eventually, get_safe_page() will be moved into a function pointer passed via argument, but for now keep it as is. Signed-off-by: Pavel Tatashin <[email protected]> Reviewed-by: James Morse <[email protected]> [will: Keep these functions static until kexec needs them] Signed-off-by: Will Deacon <[email protected]>
1 parent 7ea4088 commit a2c2e67

File tree

1 file changed

+60
-33
lines changed

1 file changed

+60
-33
lines changed

arch/arm64/kernel/hibernate.c

Lines changed: 60 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -182,39 +182,15 @@ int arch_hibernation_header_restore(void *addr)
182182
}
183183
EXPORT_SYMBOL(arch_hibernation_header_restore);
184184

185-
/*
186-
* Copies length bytes, starting at src_start into an new page,
187-
* perform cache maintentance, then maps it at the specified address low
188-
* address as executable.
189-
*
190-
* This is used by hibernate to copy the code it needs to execute when
191-
* overwriting the kernel text. This function generates a new set of page
192-
* tables, which it loads into ttbr0.
193-
*
194-
* Length is provided as we probably only want 4K of data, even on a 64K
195-
* page system.
196-
*/
197-
static int create_safe_exec_page(void *src_start, size_t length,
198-
unsigned long dst_addr,
199-
phys_addr_t *phys_dst_addr)
185+
static int trans_pgd_map_page(pgd_t *trans_pgd, void *page,
186+
unsigned long dst_addr,
187+
pgprot_t pgprot)
200188
{
201-
void *page = (void *)get_safe_page(GFP_ATOMIC);
202-
pgd_t *trans_pgd;
203189
pgd_t *pgdp;
204190
pud_t *pudp;
205191
pmd_t *pmdp;
206192
pte_t *ptep;
207193

208-
if (!page)
209-
return -ENOMEM;
210-
211-
memcpy(page, src_start, length);
212-
__flush_icache_range((unsigned long)page, (unsigned long)page + length);
213-
214-
trans_pgd = (void *)get_safe_page(GFP_ATOMIC);
215-
if (!trans_pgd)
216-
return -ENOMEM;
217-
218194
pgdp = pgd_offset_raw(trans_pgd, dst_addr);
219195
if (pgd_none(READ_ONCE(*pgdp))) {
220196
pudp = (void *)get_safe_page(GFP_ATOMIC);
@@ -242,6 +218,44 @@ static int create_safe_exec_page(void *src_start, size_t length,
242218
ptep = pte_offset_kernel(pmdp, dst_addr);
243219
set_pte(ptep, pfn_pte(virt_to_pfn(page), PAGE_KERNEL_EXEC));
244220

221+
return 0;
222+
}
223+
224+
/*
225+
* Copies length bytes, starting at src_start into an new page,
226+
* perform cache maintenance, then maps it at the specified address low
227+
* address as executable.
228+
*
229+
* This is used by hibernate to copy the code it needs to execute when
230+
* overwriting the kernel text. This function generates a new set of page
231+
* tables, which it loads into ttbr0.
232+
*
233+
* Length is provided as we probably only want 4K of data, even on a 64K
234+
* page system.
235+
*/
236+
static int create_safe_exec_page(void *src_start, size_t length,
237+
unsigned long dst_addr,
238+
phys_addr_t *phys_dst_addr)
239+
{
240+
void *page = (void *)get_safe_page(GFP_ATOMIC);
241+
pgd_t *trans_pgd;
242+
int rc;
243+
244+
if (!page)
245+
return -ENOMEM;
246+
247+
memcpy(page, src_start, length);
248+
__flush_icache_range((unsigned long)page, (unsigned long)page + length);
249+
250+
trans_pgd = (void *)get_safe_page(GFP_ATOMIC);
251+
if (!trans_pgd)
252+
return -ENOMEM;
253+
254+
rc = trans_pgd_map_page(trans_pgd, page, dst_addr,
255+
PAGE_KERNEL_EXEC);
256+
if (rc)
257+
return rc;
258+
245259
/*
246260
* Load our new page tables. A strict BBM approach requires that we
247261
* ensure that TLBs are free of any entries that may overlap with the
@@ -462,6 +476,24 @@ static int copy_page_tables(pgd_t *dst_pgdp, unsigned long start,
462476
return 0;
463477
}
464478

479+
static int trans_pgd_create_copy(pgd_t **dst_pgdp, unsigned long start,
480+
unsigned long end)
481+
{
482+
int rc;
483+
pgd_t *trans_pgd = (pgd_t *)get_safe_page(GFP_ATOMIC);
484+
485+
if (!trans_pgd) {
486+
pr_err("Failed to allocate memory for temporary page tables.\n");
487+
return -ENOMEM;
488+
}
489+
490+
rc = copy_page_tables(trans_pgd, start, end);
491+
if (!rc)
492+
*dst_pgdp = trans_pgd;
493+
494+
return rc;
495+
}
496+
465497
/*
466498
* Setup then Resume from the hibernate image using swsusp_arch_suspend_exit().
467499
*
@@ -483,12 +515,7 @@ int swsusp_arch_resume(void)
483515
* Create a second copy of just the linear map, and use this when
484516
* restoring.
485517
*/
486-
tmp_pg_dir = (pgd_t *)get_safe_page(GFP_ATOMIC);
487-
if (!tmp_pg_dir) {
488-
pr_err("Failed to allocate memory for temporary page tables.\n");
489-
return -ENOMEM;
490-
}
491-
rc = copy_page_tables(tmp_pg_dir, PAGE_OFFSET, PAGE_END);
518+
rc = trans_pgd_create_copy(&tmp_pg_dir, PAGE_OFFSET, PAGE_END);
492519
if (rc)
493520
return rc;
494521

0 commit comments

Comments
 (0)