blob: 78ff8809732b514129f7cf47742ac74151ce3e62 [file] [log] [blame]
/* Copyright (C) 2017 The Android Open Source Project
**
** 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 "qemu/osdep.h"
#include "qemu/log.h"
#include "qapi/error.h"
#include "hw/hw.h"
#include "hw/sysbus.h"
#include "hw/misc/goldfish_pstore.h"
#include "exec/address-spaces.h"
#include "io/channel-file.h"
#include "migration/qemu-file.h"
#include "migration/qemu-file-channel.h"
#define GOLDFISH_PSTORE(obj) OBJECT_CHECK(goldfish_pstore, (obj), TYPE_GOLDFISH_PSTORE)
/**
A goldfish pstore is currently an area of reservered memory that survives reboots. The memory
is persisted to disk if the filename parameter has been given. We persist the memory region
when the device is unrealized.
*/
typedef struct goldfish_pstore {
DeviceState parent;
MemoryRegion memory;
uint64_t size; /* Size of the region in bytes, keep in mind the larger this
gets, the longer writes on exit take */
hwaddr addr; /* Physical guest address where this memory shows up */
char* fname; /* File where we will (re)store the bytes on entry/exit */
} goldfish_pstore;
static Property goldfish_pstore_properties[] = {
DEFINE_PROP_UINT64(GOLDFISH_PSTORE_ADDR_PROP, goldfish_pstore, addr, 0),
DEFINE_PROP_SIZE(GOLDFISH_PSTORE_SIZE_PROP, goldfish_pstore, size, 0),
DEFINE_PROP_STRING(GOLDFISH_PSTORE_FILE_PROP, goldfish_pstore, fname),
DEFINE_PROP_END_OF_LIST(),
};
/* Construct a QEMUFile object from the given fd. */
static QEMUFile* qemu_file_from_fd(int fd, bool write)
{
QIOChannel *ioc;
QEMUFile *f;
assert(fd > 0);
lseek(fd, 0, SEEK_SET);
ioc = QIO_CHANNEL(qio_channel_file_new_fd(fd));
if (write) {
f = qemu_fopen_channel_output(ioc);
} else {
f = qemu_fopen_channel_input(ioc);
}
object_unref(OBJECT(ioc));
return f;
}
static void goldfish_pstore_save_restore(goldfish_pstore *store, bool write) {
Error *local_err = NULL;
QEMUFile *file = NULL;
int fd = -1;
if (!store->fname) {
error_setg(&local_err, "No storage file has been specified");
goto Error;
}
// Let's get the file handle
if (write) {
fd = OPEN_WRITE(store->fname);
} else {
fd = OPEN_READ(store->fname);
}
if (fd == -1) {
if (write) {
error_setg_errno(&local_err, errno, "Unable to open %s", store->fname);
}
goto Error;
}
// And just read/write the memory contents.
file = qemu_file_from_fd(fd, write);
if (write) {
qemu_put_buffer(file, (uint8_t *)memory_region_get_ram_ptr(&store->memory),
memory_region_size(&store->memory));
} else {
size_t read = qemu_get_buffer(
file, (uint8_t *)memory_region_get_ram_ptr(&store->memory),
memory_region_size(&store->memory));
}
if (qemu_file_get_error(file)) {
const char *flag_msg = write ? "write" : "read";
error_setg_errno(&local_err, errno, "Unable to %s memory using file: %s",
flag_msg, store->fname);
goto Error;
}
Error:
if (local_err && qemu_loglevel_mask(LOG_TRACE)) {
// pstore is best effort, users frequently see warnings which is confusing.
// pstore support is usually only needed for those that do low-level development
// and need the reporting upon reboot. Those developers should enable tracing to
// see the error messages
error_report_err(local_err);
}
if (file) {
qemu_fclose(file);
}
#ifndef _MSC_VER
close(fd);
#endif
}
static void goldfish_pstore_realize(DeviceState *dev, Error **errp) {
goldfish_pstore *s = GOLDFISH_PSTORE(dev);
// Reserve a slot of memory that we will use for our pstore.
memory_region_init_ram_nomigrate(&s->memory, OBJECT(s), GOLDFISH_PSTORE_DEV_ID, s->size,
errp);
// Ok, now we just need to move it to the right physical address.
memory_region_add_subregion(get_system_memory(), s->addr, &s->memory);
// And restore the contents from disk
goldfish_pstore_save_restore(s, false);
}
static void goldfish_pstore_unrealize(DeviceState *dev, Error **errp) {
goldfish_pstore *s = GOLDFISH_PSTORE(dev);
goldfish_pstore_save_restore(s, true);
}
static void goldfish_dev_init(Object *obj) {
// ID registration needs to happen before realization, otherwise
// the device will show up with some unknown name in the dev tree
// or with a name that has been handed through command line parameters.
// We don't want any of that, so we set the device-id right here.
goldfish_pstore *s = GOLDFISH_PSTORE(obj);
s->parent.id = GOLDFISH_PSTORE_DEV_ID;
}
static void goldfish_pstore_class_init(ObjectClass *klass, void *data) {
DeviceClass *dc = DEVICE_CLASS(klass);
dc->realize = goldfish_pstore_realize;
dc->unrealize = goldfish_pstore_unrealize;
dc->desc = "goldfish emulated persistent storage ram";
dc->props = goldfish_pstore_properties;
}
static const TypeInfo goldfish_pstore_info = {
.name = TYPE_GOLDFISH_PSTORE,
.parent = TYPE_DEVICE,
.instance_size = sizeof(goldfish_pstore),
.instance_init = goldfish_dev_init,
.class_init = goldfish_pstore_class_init,
};
static void goldfish_pstore_register(void) {
type_register_static(&goldfish_pstore_info);
}
type_init(goldfish_pstore_register);