/* * QEMU SMBus Generic Storage Device * * Copyright (c) 2020 Mike Davis * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #include "qemu/osdep.h" #include "hw/hw.h" #include "hw/qdev-properties.h" #include "hw/i2c/i2c.h" #include "hw/i2c/smbus_slave.h" #include "smbus.h" #include "qapi/error.h" #include "hw/loader.h" #include "migration/vmstate.h" #define TYPE_SMBUS_STORAGE "smbus-storage" #define SMBUS_STORAGE(obj) OBJECT_CHECK(SMBusStorageDevice, (obj), \ TYPE_SMBUS_STORAGE) //#define DEBUG #ifdef DEBUG # define DPRINTF(format, ...) printf(format, ## __VA_ARGS__) #else # define DPRINTF(format, ...) do { } while (0) #endif typedef struct SMBusStorageDevice { SMBusDevice smbusdev; char* file; uint8_t *data; uint32_t size; uint8_t addr; uint32_t offset; bool persist; } SMBusStorageDevice; static void smbus_storage_realize(DeviceState *dev, Error **errp) { SMBusStorageDevice *s = SMBUS_STORAGE(dev); qdev_prop_set_uint8(dev, "address", s->addr); s->data = g_malloc0(s->size); s->offset = 0; if (s->file) { int size = get_image_size(s->file); if (size != s->size) { error_setg(errp, "%s: file '%s' size of %d, expected %d\n", __func__, s->file, size, s->size); return; } int fd = qemu_open(s->file, O_RDONLY | O_BINARY, NULL); if (fd < 0) { error_setg(errp, "%s: file '%s' could not be opened\n", __func__, s->file); return; } int rc = read(fd, s->data, s->size); if (rc != s->size) { error_setg(errp, "%s: file '%s' read failure\n", __func__, s->file); close(fd); return; } close(fd); } else { error_setg(errp, "%s: file unspecified\n", __func__); } } static int smbus_storage_write_data(SMBusDevice *dev, uint8_t *buf, uint8_t len) { SMBusStorageDevice *s = SMBUS_STORAGE(dev); DPRINTF("%s: addr=0x%02x cmd=0x%02x val=0x%02x\n", __func__, s->addr, buf[0], buf[1]); /* len is guaranteed to be > 0 */ s->offset = buf[0]; buf++; len--; bool changed = false; for (; len > 0; len--) { changed = true; s->data[s->offset] = *buf++; DPRINTF("%s: addr=0x%02x off=0x%02x, data=0x%02x\n", __func__, s->addr, s->offset, s->data[s->offset]); s->offset = (s->offset + 1) % s->size; } if (changed && s->file && s->persist) { int fd = qemu_open(s->file, O_WRONLY | O_BINARY, NULL); if (fd < 0) { DPRINTF("%s: file '%s' could not be opened\n", __func__, s->file); return -1; } int wc = write(fd, s->data, s->size); if (wc != s->size) { DPRINTF( "%s: file '%s' write failure\n", __func__, s->file); close(fd); return -1; } close(fd); } return 0; } static uint8_t smbus_storage_receive_byte(SMBusDevice *dev) { SMBusStorageDevice *s = SMBUS_STORAGE(dev); uint8_t val = s->data[s->offset]; DPRINTF("%s: addr=0x%02x off=0x%02x val=0x%02x\n", __func__, s->addr, s->offset, val); s->offset = (s->offset + 1) % s->size; return val; } static const VMStateDescription vmstate_smbus_storage = { .name = "smbus-storage", .version_id = 1, .minimum_version_id = 1, .fields = (VMStateField[]) { VMSTATE_SMBUS_DEVICE(smbusdev, SMBusStorageDevice), VMSTATE_VBUFFER_UINT32(data, SMBusStorageDevice, 1, NULL, size), VMSTATE_UINT32(offset, SMBusStorageDevice), VMSTATE_END_OF_LIST() } }; // default xbox eeprom with persistence static Property smbus_storage_props[] = { DEFINE_PROP_UINT8("addr", SMBusStorageDevice, addr, 0x54), DEFINE_PROP_UINT32("size", SMBusStorageDevice, size, 256), DEFINE_PROP_BOOL("persist", SMBusStorageDevice, persist, true), DEFINE_PROP_STRING("file", SMBusStorageDevice, file), DEFINE_PROP_END_OF_LIST() }; static void smbus_storage_class_initfn(ObjectClass *klass, void *data) { DeviceClass *dc = DEVICE_CLASS(klass); SMBusDeviceClass *sc = SMBUS_DEVICE_CLASS(klass); dc->vmsd = &vmstate_smbus_storage; dc->realize = smbus_storage_realize; sc->receive_byte = smbus_storage_receive_byte; sc->write_data = smbus_storage_write_data; device_class_set_props(dc, smbus_storage_props); } static TypeInfo smbus_storage_info = { .name = TYPE_SMBUS_STORAGE, .parent = TYPE_SMBUS_DEVICE, .instance_size = sizeof(SMBusStorageDevice), .class_init = smbus_storage_class_initfn }; static void smbus_storage_register_devices(void) { type_register_static(&smbus_storage_info); } type_init(smbus_storage_register_devices)