/* * Utils for keeping track of which areas of RAM change during suspend code. * * Copyright (C) 2012 Google, Inc. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. */ #include #include #include #include static LIST_HEAD(volatile_chunks); static DEFINE_SPINLOCK(volatile_chunks_lock); /** * Register a range of memory as volatile. * * @chunk: The chunk to register; memory for this structure must remain * allocated until the chunk is unregistered. */ void pm_register_suspend_volatile(struct pm_suspend_volatile_chunk *chunk) { unsigned long flags; spin_lock_irqsave(&volatile_chunks_lock, flags); list_add_tail(&chunk->list, &volatile_chunks); spin_unlock_irqrestore(&volatile_chunks_lock, flags); } /** * Unregister a range of memory as volatile. * * @chunk: The chunk to unregister. Should be exact pointer passed to register. */ void pm_unregister_suspend_volatile(struct pm_suspend_volatile_chunk *chunk) { unsigned long flags; spin_lock_irqsave(&volatile_chunks_lock, flags); list_del(&chunk->list); spin_unlock_irqrestore(&volatile_chunks_lock, flags); } /** * Return true if the chunk overlaps memory that is touched by suspend process. * * Checks both memory that was registered with pm_register_suspend_volatile() * and memory that was tagged with __suspend_volatile. * * As the list is not proected with locks, this code must only be called * from an uninterruptible context. * * @start: Physical start address of chunk to check. * @num_bytes: Size of the chunk to check in bytes. */ bool pm_does_overlap_suspend_volatile(phys_addr_t start, size_t num_bytes) { struct pm_suspend_volatile_chunk *chunk; bool is_volatile = false; /* Bits marked at compile time as suspend volatile */ if (phys_addrs_overlap(start, num_bytes, virt_to_phys(__start_suspend_volatile_bss), __stop_suspend_volatile_bss - __start_suspend_volatile_bss)) return true; /* Bits marked at compile time as suspend volatile */ if (phys_addrs_overlap(start, num_bytes, virt_to_phys(__start_suspend_volatile_data), __stop_suspend_volatile_data - __start_suspend_volatile_data)) return true; /* * Bits registered dynamically * * Normally a call to spin_lock_irqsave(&volatile_chunks_lock, flags); * would go here. However this code is only called with scheduling * disabled so there is no need to lock the list. It is also called * often enough that the overhead involved is signficant (~100 ms) */ list_for_each_entry(chunk, &volatile_chunks, list) { if (phys_addrs_overlap(start, num_bytes, chunk->start, chunk->num_bytes)) { is_volatile = true; break; } } return is_volatile; }