nvs.c (4669B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* 3 * nvs.c - Routines for saving and restoring ACPI NVS memory region 4 * 5 * Copyright (C) 2008-2011 Rafael J. Wysocki <rjw@sisk.pl>, Novell Inc. 6 */ 7 8#define pr_fmt(fmt) "ACPI: PM: " fmt 9 10#include <linux/io.h> 11#include <linux/kernel.h> 12#include <linux/list.h> 13#include <linux/mm.h> 14#include <linux/slab.h> 15#include <linux/acpi.h> 16 17#include "internal.h" 18 19/* ACPI NVS regions, APEI may use it */ 20 21struct nvs_region { 22 __u64 phys_start; 23 __u64 size; 24 struct list_head node; 25}; 26 27static LIST_HEAD(nvs_region_list); 28 29#ifdef CONFIG_ACPI_SLEEP 30static int suspend_nvs_register(unsigned long start, unsigned long size); 31#else 32static inline int suspend_nvs_register(unsigned long a, unsigned long b) 33{ 34 return 0; 35} 36#endif 37 38int acpi_nvs_register(__u64 start, __u64 size) 39{ 40 struct nvs_region *region; 41 42 region = kmalloc(sizeof(*region), GFP_KERNEL); 43 if (!region) 44 return -ENOMEM; 45 region->phys_start = start; 46 region->size = size; 47 list_add_tail(®ion->node, &nvs_region_list); 48 49 return suspend_nvs_register(start, size); 50} 51 52int acpi_nvs_for_each_region(int (*func)(__u64 start, __u64 size, void *data), 53 void *data) 54{ 55 int rc; 56 struct nvs_region *region; 57 58 list_for_each_entry(region, &nvs_region_list, node) { 59 rc = func(region->phys_start, region->size, data); 60 if (rc) 61 return rc; 62 } 63 64 return 0; 65} 66 67 68#ifdef CONFIG_ACPI_SLEEP 69/* 70 * Platforms, like ACPI, may want us to save some memory used by them during 71 * suspend and to restore the contents of this memory during the subsequent 72 * resume. The code below implements a mechanism allowing us to do that. 73 */ 74 75struct nvs_page { 76 unsigned long phys_start; 77 unsigned int size; 78 void *kaddr; 79 void *data; 80 bool unmap; 81 struct list_head node; 82}; 83 84static LIST_HEAD(nvs_list); 85 86/** 87 * suspend_nvs_register - register platform NVS memory region to save 88 * @start: Physical address of the region. 89 * @size: Size of the region. 90 * 91 * The NVS region need not be page-aligned (both ends) and we arrange 92 * things so that the data from page-aligned addresses in this region will 93 * be copied into separate RAM pages. 94 */ 95static int suspend_nvs_register(unsigned long start, unsigned long size) 96{ 97 struct nvs_page *entry, *next; 98 99 pr_info("Registering ACPI NVS region [mem %#010lx-%#010lx] (%ld bytes)\n", 100 start, start + size - 1, size); 101 102 while (size > 0) { 103 unsigned int nr_bytes; 104 105 entry = kzalloc(sizeof(struct nvs_page), GFP_KERNEL); 106 if (!entry) 107 goto Error; 108 109 list_add_tail(&entry->node, &nvs_list); 110 entry->phys_start = start; 111 nr_bytes = PAGE_SIZE - (start & ~PAGE_MASK); 112 entry->size = (size < nr_bytes) ? size : nr_bytes; 113 114 start += entry->size; 115 size -= entry->size; 116 } 117 return 0; 118 119 Error: 120 list_for_each_entry_safe(entry, next, &nvs_list, node) { 121 list_del(&entry->node); 122 kfree(entry); 123 } 124 return -ENOMEM; 125} 126 127/** 128 * suspend_nvs_free - free data pages allocated for saving NVS regions 129 */ 130void suspend_nvs_free(void) 131{ 132 struct nvs_page *entry; 133 134 list_for_each_entry(entry, &nvs_list, node) 135 if (entry->data) { 136 free_page((unsigned long)entry->data); 137 entry->data = NULL; 138 if (entry->kaddr) { 139 if (entry->unmap) { 140 iounmap(entry->kaddr); 141 entry->unmap = false; 142 } else { 143 acpi_os_unmap_iomem(entry->kaddr, 144 entry->size); 145 } 146 entry->kaddr = NULL; 147 } 148 } 149} 150 151/** 152 * suspend_nvs_alloc - allocate memory necessary for saving NVS regions 153 */ 154int suspend_nvs_alloc(void) 155{ 156 struct nvs_page *entry; 157 158 list_for_each_entry(entry, &nvs_list, node) { 159 entry->data = (void *)__get_free_page(GFP_KERNEL); 160 if (!entry->data) { 161 suspend_nvs_free(); 162 return -ENOMEM; 163 } 164 } 165 return 0; 166} 167 168/** 169 * suspend_nvs_save - save NVS memory regions 170 */ 171int suspend_nvs_save(void) 172{ 173 struct nvs_page *entry; 174 175 pr_info("Saving platform NVS memory\n"); 176 177 list_for_each_entry(entry, &nvs_list, node) 178 if (entry->data) { 179 unsigned long phys = entry->phys_start; 180 unsigned int size = entry->size; 181 182 entry->kaddr = acpi_os_get_iomem(phys, size); 183 if (!entry->kaddr) { 184 entry->kaddr = acpi_os_ioremap(phys, size); 185 entry->unmap = !!entry->kaddr; 186 } 187 if (!entry->kaddr) { 188 suspend_nvs_free(); 189 return -ENOMEM; 190 } 191 memcpy(entry->data, entry->kaddr, entry->size); 192 } 193 194 return 0; 195} 196 197/** 198 * suspend_nvs_restore - restore NVS memory regions 199 * 200 * This function is going to be called with interrupts disabled, so it 201 * cannot iounmap the virtual addresses used to access the NVS region. 202 */ 203void suspend_nvs_restore(void) 204{ 205 struct nvs_page *entry; 206 207 pr_info("Restoring platform NVS memory\n"); 208 209 list_for_each_entry(entry, &nvs_list, node) 210 if (entry->data) 211 memcpy(entry->kaddr, entry->data, entry->size); 212} 213#endif