/* * Register Definition API * * Copyright (c) 2016 Xilinx Inc. * Copyright (c) 2013 Peter Crosthwaite * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the * Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * 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 "hw/register.h" #include "qemu/log.h" #include "qemu/module.h" static inline void register_write_val(RegisterInfo *reg, uint64_t val) { g_assert(reg->data); switch (reg->data_size) { case 1: *(uint8_t *)reg->data = val; break; case 2: *(uint16_t *)reg->data = val; break; case 4: *(uint32_t *)reg->data = val; break; case 8: *(uint64_t *)reg->data = val; break; default: g_assert_not_reached(); } } static inline uint64_t register_read_val(RegisterInfo *reg) { switch (reg->data_size) { case 1: return *(uint8_t *)reg->data; case 2: return *(uint16_t *)reg->data; case 4: return *(uint32_t *)reg->data; case 8: return *(uint64_t *)reg->data; default: g_assert_not_reached(); } return 0; /* unreachable */ } static inline uint64_t register_enabled_mask(int data_size, unsigned size) { if (data_size < size) { size = data_size; } return MAKE_64BIT_MASK(0, size * 8); } void register_write(RegisterInfo *reg, uint64_t val, uint64_t we, const char *prefix, bool debug) { uint64_t old_val, new_val, test, no_w_mask; const RegisterAccessInfo *ac; assert(reg); ac = reg->access; if (!ac || !ac->name) { qemu_log_mask(LOG_GUEST_ERROR, "%s: write to undefined device state " "(written value: 0x%" PRIx64 ")\n", prefix, val); return; } old_val = reg->data ? register_read_val(reg) : ac->reset; test = (old_val ^ val) & ac->rsvd; if (test) { qemu_log_mask(LOG_GUEST_ERROR, "%s: change of value in reserved bit" "fields: 0x%" PRIx64 ")\n", prefix, test); } test = val & ac->unimp; if (test) { qemu_log_mask(LOG_UNIMP, "%s:%s writing 0x%" PRIx64 " to unimplemented bits:" \ " 0x%" PRIx64 "\n", prefix, reg->access->name, val, ac->unimp); } /* Create the no write mask based on the read only, write to clear and * reserved bit masks. */ no_w_mask = ac->ro | ac->w1c | ac->rsvd | ~we; new_val = (val & ~no_w_mask) | (old_val & no_w_mask); new_val &= ~(val & ac->w1c); if (ac->pre_write) { new_val = ac->pre_write(reg, new_val); } if (debug) { qemu_log("%s:%s: write of value 0x%" PRIx64 "\n", prefix, ac->name, new_val); } register_write_val(reg, new_val); if (ac->post_write) { ac->post_write(reg, new_val); } } uint64_t register_read(RegisterInfo *reg, uint64_t re, const char* prefix, bool debug) { uint64_t ret; const RegisterAccessInfo *ac; assert(reg); ac = reg->access; if (!ac || !ac->name) { qemu_log_mask(LOG_GUEST_ERROR, "%s: read from undefined device state\n", prefix); return 0; } ret = reg->data ? register_read_val(reg) : ac->reset; register_write_val(reg, ret & ~(ac->cor & re)); /* Mask based on the read enable size */ ret &= re; if (ac->post_read) { ret = ac->post_read(reg, ret); } if (debug) { qemu_log("%s:%s: read of value 0x%" PRIx64 "\n", prefix, ac->name, ret); } return ret; } void register_reset(RegisterInfo *reg) { const RegisterAccessInfo *ac; g_assert(reg); if (!reg->data || !reg->access) { return; } ac = reg->access; register_write_val(reg, reg->access->reset); if (ac->post_write) { ac->post_write(reg, reg->access->reset); } } void register_write_memory(void *opaque, hwaddr addr, uint64_t value, unsigned size) { RegisterInfoArray *reg_array = opaque; RegisterInfo *reg = NULL; uint64_t we; int i; for (i = 0; i < reg_array->num_elements; i++) { if (reg_array->r[i]->access->addr == addr) { reg = reg_array->r[i]; break; } } if (!reg) { qemu_log_mask(LOG_GUEST_ERROR, "%s: write to unimplemented register " \ "at address: 0x%" PRIx64 "\n", reg_array->prefix, addr); return; } /* Generate appropriate write enable mask */ we = register_enabled_mask(reg->data_size, size); register_write(reg, value, we, reg_array->prefix, reg_array->debug); } uint64_t register_read_memory(void *opaque, hwaddr addr, unsigned size) { RegisterInfoArray *reg_array = opaque; RegisterInfo *reg = NULL; uint64_t read_val; uint64_t re; int i; for (i = 0; i < reg_array->num_elements; i++) { if (reg_array->r[i]->access->addr == addr) { reg = reg_array->r[i]; break; } } if (!reg) { qemu_log_mask(LOG_GUEST_ERROR, "%s: read to unimplemented register " \ "at address: 0x%" PRIx64 "\n", reg_array->prefix, addr); return 0; } /* Generate appropriate read enable mask */ re = register_enabled_mask(reg->data_size, size); read_val = register_read(reg, re, reg_array->prefix, reg_array->debug); return extract64(read_val, 0, size * 8); } static RegisterInfoArray *register_init_block(DeviceState *owner, const RegisterAccessInfo *rae, int num, RegisterInfo *ri, void *data, const MemoryRegionOps *ops, bool debug_enabled, uint64_t memory_size, size_t data_size_bits) { const char *device_prefix = object_get_typename(OBJECT(owner)); RegisterInfoArray *r_array = g_new0(RegisterInfoArray, 1); int data_size = data_size_bits >> 3; int i; r_array->r = g_new0(RegisterInfo *, num); r_array->num_elements = num; r_array->debug = debug_enabled; r_array->prefix = device_prefix; for (i = 0; i < num; i++) { int index = rae[i].addr / data_size; RegisterInfo *r = &ri[index]; /* Init the register, this will zero it. */ object_initialize((void *)r, sizeof(*r), TYPE_REGISTER); /* Set the properties of the register */ r->data = data + data_size * index; r->data_size = data_size; r->access = &rae[i]; r->opaque = owner; r_array->r[i] = r; } memory_region_init_io(&r_array->mem, OBJECT(owner), ops, r_array, device_prefix, memory_size); return r_array; } RegisterInfoArray *register_init_block8(DeviceState *owner, const RegisterAccessInfo *rae, int num, RegisterInfo *ri, uint8_t *data, const MemoryRegionOps *ops, bool debug_enabled, uint64_t memory_size) { return register_init_block(owner, rae, num, ri, (void *) data, ops, debug_enabled, memory_size, 8); } RegisterInfoArray *register_init_block32(DeviceState *owner, const RegisterAccessInfo *rae, int num, RegisterInfo *ri, uint32_t *data, const MemoryRegionOps *ops, bool debug_enabled, uint64_t memory_size) { return register_init_block(owner, rae, num, ri, (void *) data, ops, debug_enabled, memory_size, 32); } void register_finalize_block(RegisterInfoArray *r_array) { object_unparent(OBJECT(&r_array->mem)); g_free(r_array->r); g_free(r_array); } static void register_class_init(ObjectClass *oc, void *data) { DeviceClass *dc = DEVICE_CLASS(oc); /* Reason: needs to be wired up to work */ dc->user_creatable = false; } static const TypeInfo register_info = { .name = TYPE_REGISTER, .parent = TYPE_DEVICE, .class_init = register_class_init, .instance_size = sizeof(RegisterInfo), }; static void register_register_types(void) { type_register_static(®ister_info); } type_init(register_register_types)