Import QEMU upstream snapshot d2e570c

Upstream: https://gitlab.com/qemu-project/qemu.git

Upstream-Commit: d2e570cc0f97b936902a5b1b86b73c0f5998b475
This commit is contained in:
2026-08-31 02:15:30 +02:00
commit cf256aa081
11315 changed files with 3598369 additions and 0 deletions
+22
View File
@@ -0,0 +1,22 @@
config DIMM
bool
select MEM_DEVICE
config MEM_DEVICE
bool
config NVDIMM
bool
select MEM_DEVICE
config SPARSE_MEM
bool
config CXL_MEM_DEVICE
bool
default y if CXL
select MEM_DEVICE
config SP_MEM
bool
select MEM_DEVICE
+2499
View File
File diff suppressed because it is too large Load Diff
+129
View File
@@ -0,0 +1,129 @@
/*
* CXL Type 3 (memory expander) device QMP stubs
*
* Copyright(C) 2020 Intel Corporation.
*
* This work is licensed under the terms of the GNU GPL, version 2. See the
* COPYING file in the top-level directory.
*
* SPDX-License-Identifier: GPL-v2-only
*/
#include "qemu/osdep.h"
#include "qapi/error.h"
#include "qapi/qapi-commands-cxl.h"
void qmp_cxl_inject_general_media_event(const char *path, CxlEventLog log,
uint32_t flags, bool has_maint_op_class,
uint8_t maint_op_class,
bool has_maint_op_subclass,
uint8_t maint_op_subclass,
bool has_ld_id, uint16_t ld_id,
bool has_head_id, uint8_t head_id,
uint64_t dpa,
uint8_t descriptor, uint8_t type,
uint8_t transaction_type,
bool has_channel, uint8_t channel,
bool has_rank, uint8_t rank,
bool has_device, uint32_t device,
const char *component_id,
bool has_comp_id_pldm,
bool is_comp_id_pldm,
bool has_cme_ev_flags,
uint8_t cme_ev_flags,
bool has_cme_count, uint32_t cme_count,
uint8_t sub_type,
Error **errp) {}
void qmp_cxl_inject_dram_event(const char *path, CxlEventLog log,
uint32_t flags,
bool has_maint_op_class, uint8_t maint_op_class,
bool has_maint_op_subclass,
uint8_t maint_op_subclass,
bool has_ld_id, uint16_t ld_id,
bool has_head_id, uint8_t head_id,
uint64_t dpa, uint8_t descriptor,
uint8_t type, uint8_t transaction_type,
bool has_channel, uint8_t channel,
bool has_rank, uint8_t rank,
bool has_nibble_mask, uint32_t nibble_mask,
bool has_bank_group, uint8_t bank_group,
bool has_bank, uint8_t bank,
bool has_row, uint32_t row,
bool has_column, uint16_t column,
bool has_correction_mask,
uint64List *correction_mask,
const char *component_id,
bool has_comp_id_pldm,
bool is_comp_id_pldm,
bool has_sub_channel, uint8_t sub_channel,
bool has_cme_ev_flags, uint8_t cme_ev_flags,
bool has_cvme_count, uint32_t cvme_count,
uint8_t sub_type,
Error **errp) {}
void qmp_cxl_inject_memory_module_event(const char *path, CxlEventLog log,
uint32_t flags, bool has_maint_op_class,
uint8_t maint_op_class,
bool has_maint_op_subclass,
uint8_t maint_op_subclass,
bool has_ld_id, uint16_t ld_id,
bool has_head_id, uint8_t head_id,
uint8_t type,
uint8_t health_status,
uint8_t media_status,
uint8_t additional_status,
uint8_t life_used,
int16_t temperature,
uint32_t dirty_shutdown_count,
uint32_t corrected_volatile_error_count,
uint32_t corrected_persist_error_count,
const char *component_id,
bool has_comp_id_pldm,
bool is_comp_id_pldm,
uint8_t sub_type,
Error **errp) {}
void qmp_cxl_inject_poison(const char *path, uint64_t start, uint64_t length,
Error **errp)
{
error_setg(errp, "CXL Type 3 support is not compiled in");
}
void qmp_cxl_inject_uncorrectable_errors(const char *path,
CXLUncorErrorRecordList *errors,
Error **errp)
{
error_setg(errp, "CXL Type 3 support is not compiled in");
}
void qmp_cxl_inject_correctable_error(const char *path, CxlCorErrorType type,
Error **errp)
{
error_setg(errp, "CXL Type 3 support is not compiled in");
}
void qmp_cxl_add_dynamic_capacity(const char *path,
uint16_t host_id,
CxlExtentSelectionPolicy sel_policy,
uint8_t region,
const char *tag,
CxlDynamicCapacityExtentList *extents,
Error **errp)
{
error_setg(errp, "CXL Type 3 support is not compiled in");
}
void qmp_cxl_release_dynamic_capacity(const char *path, uint16_t host_id,
CxlExtentRemovalPolicy removal_policy,
bool has_forced_removal,
bool forced_removal,
bool has_sanitize_on_release,
bool sanitize_on_release,
uint8_t region,
const char *tag,
CxlDynamicCapacityExtentList *extents,
Error **errp)
{
error_setg(errp, "CXL Type 3 support is not compiled in");
}
+22
View File
@@ -0,0 +1,22 @@
#include "qemu/osdep.h"
#include "hw/mem/memory-device.h"
MemoryDeviceInfoList *qmp_memory_device_list(void)
{
return NULL;
}
uint64_t get_plugged_memory_size(void)
{
return (uint64_t)-1;
}
unsigned int memory_devices_get_reserved_memslots(void)
{
return 0;
}
bool memory_devices_memslot_auto_decision_active(void)
{
return false;
}
+561
View File
@@ -0,0 +1,561 @@
/*
* Memory Device Interface
*
* Copyright ProfitBricks GmbH 2012
* Copyright (C) 2014 Red Hat Inc
* Copyright (c) 2018 Red Hat Inc
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "hw/mem/memory-device.h"
#include "qapi/error.h"
#include "hw/core/boards.h"
#include "qemu/range.h"
#include "hw/virtio/vhost.h"
#include "system/kvm.h"
#include "system/address-spaces.h"
#include "trace.h"
static bool memory_device_is_empty(const MemoryDeviceState *md)
{
const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md);
Error *local_err = NULL;
MemoryRegion *mr;
/* dropping const here is fine as we don't touch the memory region */
mr = mdc->get_memory_region((MemoryDeviceState *)md, &local_err);
if (local_err) {
/* Not empty, we'll report errors later when containing the MR again. */
error_free(local_err);
return false;
}
return !mr;
}
static gint memory_device_addr_sort(gconstpointer a, gconstpointer b)
{
const MemoryDeviceState *md_a = MEMORY_DEVICE(a);
const MemoryDeviceState *md_b = MEMORY_DEVICE(b);
const MemoryDeviceClass *mdc_a = MEMORY_DEVICE_GET_CLASS(a);
const MemoryDeviceClass *mdc_b = MEMORY_DEVICE_GET_CLASS(b);
const uint64_t addr_a = mdc_a->get_addr(md_a);
const uint64_t addr_b = mdc_b->get_addr(md_b);
if (addr_a > addr_b) {
return 1;
} else if (addr_a < addr_b) {
return -1;
}
return 0;
}
static int memory_device_build_list(Object *obj, void *opaque)
{
GSList **list = opaque;
if (object_dynamic_cast(obj, TYPE_MEMORY_DEVICE)) {
DeviceState *dev = DEVICE(obj);
if (qdev_is_realized(dev)) {
*list = g_slist_insert_sorted(*list, dev, memory_device_addr_sort);
}
}
object_child_foreach(obj, memory_device_build_list, opaque);
return 0;
}
static unsigned int memory_device_get_memslots(MemoryDeviceState *md)
{
const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md);
if (mdc->get_memslots) {
return mdc->get_memslots(md);
}
return 1;
}
/*
* Memslots that are reserved by memory devices (required but still reported
* as free from KVM / vhost).
*/
static unsigned int get_reserved_memslots(MachineState *ms)
{
if (ms->device_memory->used_memslots >
ms->device_memory->required_memslots) {
/* This is unexpected, and we warned already in the memory notifier. */
return 0;
}
return ms->device_memory->required_memslots -
ms->device_memory->used_memslots;
}
unsigned int memory_devices_get_reserved_memslots(void)
{
if (!current_machine->device_memory) {
return 0;
}
return get_reserved_memslots(current_machine);
}
bool memory_devices_memslot_auto_decision_active(void)
{
if (!current_machine->device_memory) {
return false;
}
return current_machine->device_memory->memslot_auto_decision_active;
}
static unsigned int memory_device_memslot_decision_limit(MachineState *ms,
MemoryRegion *mr)
{
const unsigned int reserved = get_reserved_memslots(ms);
const uint64_t size = memory_region_size(mr);
unsigned int max = vhost_get_max_memslots();
unsigned int free = vhost_get_free_memslots();
uint64_t available_space;
unsigned int memslots;
if (kvm_enabled()) {
max = MIN(max, kvm_get_max_memslots());
free = MIN(free, kvm_get_free_memslots());
}
/*
* If we only have less overall memslots than what we consider reasonable,
* just keep it to a minimum.
*/
if (max < MEMORY_DEVICES_SAFE_MAX_MEMSLOTS) {
return 1;
}
/*
* Consider our soft-limit across all memory devices. We don't really
* expect to exceed this limit in reasonable configurations.
*/
if (MEMORY_DEVICES_SOFT_MEMSLOT_LIMIT <=
ms->device_memory->required_memslots) {
return 1;
}
memslots = MEMORY_DEVICES_SOFT_MEMSLOT_LIMIT -
ms->device_memory->required_memslots;
/*
* Consider the actually still free memslots. This is only relevant if
* other memslot consumers would consume *significantly* more memslots than
* what we prepared for (> 253). Unlikely, but let's just handle it
* cleanly.
*/
memslots = MIN(memslots, free - reserved);
if (memslots < 1 || unlikely(free < reserved)) {
return 1;
}
/* We cannot have any other memory devices? So give all to this device. */
if (size == ms->maxram_size - ms->ram_size) {
return memslots;
}
/*
* Simple heuristic: equally distribute the memslots over the space
* still available for memory devices.
*/
available_space = ms->maxram_size - ms->ram_size -
ms->device_memory->used_region_size;
memslots = (double)memslots * size / available_space;
return memslots < 1 ? 1 : memslots;
}
static void memory_device_check_addable(MachineState *ms, MemoryDeviceState *md,
MemoryRegion *mr, Error **errp)
{
const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md);
const uint64_t used_region_size = ms->device_memory->used_region_size;
const uint64_t size = memory_region_size(mr);
const unsigned int reserved_memslots = get_reserved_memslots(ms);
unsigned int required_memslots, memslot_limit;
/*
* Instruct the device to decide how many memslots to use, if applicable,
* before we query the number of required memslots the first time.
*/
if (mdc->decide_memslots) {
memslot_limit = memory_device_memslot_decision_limit(ms, mr);
mdc->decide_memslots(md, memslot_limit);
}
required_memslots = memory_device_get_memslots(md);
/* we will need memory slots for kvm and vhost */
if (kvm_enabled() &&
kvm_get_free_memslots() < required_memslots + reserved_memslots) {
error_setg(errp, "hypervisor has not enough free memory slots left");
return;
}
if (vhost_get_free_memslots() < required_memslots + reserved_memslots) {
error_setg(errp, "a used vhost backend has not enough free memory slots left");
return;
}
/* will we exceed the total amount of memory specified */
if (used_region_size + size < used_region_size ||
used_region_size + size > ms->maxram_size - ms->ram_size) {
error_setg(errp, "not enough space, currently 0x%" PRIx64
" in use of total space for memory devices 0x" RAM_ADDR_FMT,
used_region_size, ms->maxram_size - ms->ram_size);
return;
}
}
static uint64_t memory_device_get_free_addr(MachineState *ms,
const uint64_t *hint,
uint64_t align, uint64_t size,
Error **errp)
{
GSList *list = NULL, *item;
Range as, new = range_empty;
range_init_nofail(&as, ms->device_memory->base,
memory_region_size(&ms->device_memory->mr));
/* start of address space indicates the maximum alignment we expect */
if (!QEMU_IS_ALIGNED(range_lob(&as), align)) {
warn_report("the alignment (0x%" PRIx64 ") exceeds the expected"
" maximum alignment, memory will get fragmented and not"
" all 'maxmem' might be usable for memory devices.",
align);
}
if (hint && !QEMU_IS_ALIGNED(*hint, align)) {
error_setg(errp, "address must be aligned to 0x%" PRIx64 " bytes",
align);
return 0;
}
if (hint) {
if (range_init(&new, *hint, size) || !range_contains_range(&as, &new)) {
error_setg(errp, "can't add memory device [0x%" PRIx64 ":0x%" PRIx64
"], usable range for memory devices [0x%" PRIx64 ":0x%"
PRIx64 "]", *hint, size, range_lob(&as),
range_size(&as));
return 0;
}
} else {
if (range_init(&new, QEMU_ALIGN_UP(range_lob(&as), align), size)) {
error_setg(errp, "can't add memory device, device too big");
return 0;
}
}
/* find address range that will fit new memory device */
object_child_foreach(OBJECT(ms), memory_device_build_list, &list);
for (item = list; item; item = g_slist_next(item)) {
const MemoryDeviceState *md = item->data;
const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(OBJECT(md));
uint64_t next_addr;
Range tmp;
if (memory_device_is_empty(md)) {
continue;
}
range_init_nofail(&tmp, mdc->get_addr(md),
memory_device_get_region_size(md, &error_abort));
if (range_overlaps_range(&tmp, &new)) {
if (hint) {
const DeviceState *d = DEVICE(md);
error_setg(errp, "address range conflicts with memory device"
" id='%s'", d->id ? d->id : "(unnamed)");
goto out;
}
next_addr = QEMU_ALIGN_UP(range_upb(&tmp) + 1, align);
if (!next_addr || range_init(&new, next_addr, range_size(&new))) {
range_make_empty(&new);
break;
}
} else if (range_lob(&tmp) > range_upb(&new)) {
break;
}
}
if (!range_contains_range(&as, &new)) {
error_setg(errp, "could not find position in guest address space for "
"memory device - memory fragmented due to alignments");
}
out:
g_slist_free(list);
return range_lob(&new);
}
MemoryDeviceInfoList *qmp_memory_device_list(void)
{
GSList *devices = NULL, *item;
MemoryDeviceInfoList *list = NULL, **tail = &list;
object_child_foreach(qdev_get_machine(), memory_device_build_list,
&devices);
for (item = devices; item; item = g_slist_next(item)) {
const MemoryDeviceState *md = MEMORY_DEVICE(item->data);
const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(item->data);
MemoryDeviceInfo *info = g_new0(MemoryDeviceInfo, 1);
/* Let's query infotmation even for empty memory devices. */
mdc->fill_device_info(md, info);
QAPI_LIST_APPEND(tail, info);
}
g_slist_free(devices);
return list;
}
static int memory_device_plugged_size(Object *obj, void *opaque)
{
uint64_t *size = opaque;
if (object_dynamic_cast(obj, TYPE_MEMORY_DEVICE)) {
const DeviceState *dev = DEVICE(obj);
const MemoryDeviceState *md = MEMORY_DEVICE(obj);
const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(obj);
if (qdev_is_realized(dev) && !memory_device_is_empty(md)) {
*size += mdc->get_plugged_size(md, &error_abort);
}
}
object_child_foreach(obj, memory_device_plugged_size, opaque);
return 0;
}
uint64_t get_plugged_memory_size(void)
{
uint64_t size = 0;
memory_device_plugged_size(qdev_get_machine(), &size);
return size;
}
void memory_device_pre_plug(MemoryDeviceState *md, MachineState *ms,
Error **errp)
{
const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md);
Error *local_err = NULL;
uint64_t addr, align = 0;
MemoryRegion *mr;
/* We support empty memory devices even without device memory. */
if (memory_device_is_empty(md)) {
return;
}
if (!ms->device_memory) {
error_setg(errp, "the configuration is not prepared for memory devices"
" (e.g., for memory hotplug), consider specifying the"
" maxmem option");
return;
}
mr = mdc->get_memory_region(md, &local_err);
if (local_err) {
goto out;
}
memory_device_check_addable(ms, md, mr, &local_err);
if (local_err) {
goto out;
}
/*
* We always want the memory region size to be multiples of the memory
* region alignment: for example, DIMMs with 1G+1byte size don't make
* any sense. Note that we don't check that the size is multiples
* of any additional alignment requirements the memory device might
* have when it comes to the address in physical address space.
*/
if (!QEMU_IS_ALIGNED(memory_region_size(mr),
memory_region_get_alignment(mr))) {
error_setg(errp, "backend memory size must be multiple of 0x%"
PRIx64, memory_region_get_alignment(mr));
return;
}
if (mdc->get_min_alignment) {
align = mdc->get_min_alignment(md);
}
align = MAX(align, memory_region_get_alignment(mr));
addr = mdc->get_addr(md);
addr = memory_device_get_free_addr(ms, !addr ? NULL : &addr, align,
memory_region_size(mr), &local_err);
if (local_err) {
goto out;
}
mdc->set_addr(md, addr, &local_err);
if (!local_err) {
trace_memory_device_pre_plug(DEVICE(md)->id ? DEVICE(md)->id : "",
addr);
}
out:
error_propagate(errp, local_err);
}
void memory_device_plug(MemoryDeviceState *md, MachineState *ms)
{
const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md);
unsigned int memslots;
uint64_t addr;
MemoryRegion *mr;
if (memory_device_is_empty(md)) {
return;
}
memslots = memory_device_get_memslots(md);
addr = mdc->get_addr(md);
/*
* We expect that a previous call to memory_device_pre_plug() succeeded, so
* it can't fail at this point.
*/
mr = mdc->get_memory_region(md, &error_abort);
g_assert(ms->device_memory);
ms->device_memory->used_region_size += memory_region_size(mr);
ms->device_memory->required_memslots += memslots;
if (mdc->decide_memslots && memslots > 1) {
ms->device_memory->memslot_auto_decision_active++;
}
memory_region_add_subregion(&ms->device_memory->mr,
addr - ms->device_memory->base, mr);
trace_memory_device_plug(DEVICE(md)->id ? DEVICE(md)->id : "", addr);
}
void memory_device_unplug(MemoryDeviceState *md, MachineState *ms)
{
const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md);
const unsigned int memslots = memory_device_get_memslots(md);
MemoryRegion *mr;
if (memory_device_is_empty(md)) {
return;
}
/*
* We expect that a previous call to memory_device_pre_plug() succeeded, so
* it can't fail at this point.
*/
mr = mdc->get_memory_region(md, &error_abort);
g_assert(ms->device_memory);
memory_region_del_subregion(&ms->device_memory->mr, mr);
if (mdc->decide_memslots && memslots > 1) {
ms->device_memory->memslot_auto_decision_active--;
}
ms->device_memory->used_region_size -= memory_region_size(mr);
ms->device_memory->required_memslots -= memslots;
trace_memory_device_unplug(DEVICE(md)->id ? DEVICE(md)->id : "",
mdc->get_addr(md));
}
uint64_t memory_device_get_region_size(const MemoryDeviceState *md,
Error **errp)
{
const MemoryDeviceClass *mdc = MEMORY_DEVICE_GET_CLASS(md);
MemoryRegion *mr;
/* dropping const here is fine as we don't touch the memory region */
mr = mdc->get_memory_region((MemoryDeviceState *)md, errp);
if (!mr) {
return 0;
}
return memory_region_size(mr);
}
static void memory_devices_region_mod(MemoryListener *listener,
MemoryRegionSection *mrs, bool add)
{
DeviceMemoryState *dms = container_of(listener, DeviceMemoryState,
listener);
if (!memory_region_is_ram(mrs->mr)) {
warn_report("Unexpected memory region mapped into device memory region.");
return;
}
/*
* The expectation is that each distinct RAM memory region section in
* our region for memory devices consumes exactly one memslot in KVM
* and in vhost. For vhost, this is true, except:
* * ROM memory regions don't consume a memslot. These get used very
* rarely for memory devices (R/O NVDIMMs).
* * Memslots without a fd (memory-backend-ram) don't necessarily
* consume a memslot. Such setups are quite rare and possibly bogus:
* the memory would be inaccessible by such vhost devices.
*
* So for vhost, in corner cases we might over-estimate the number of
* memslots that are currently used or that might still be reserved
* (required - used).
*/
dms->used_memslots += add ? 1 : -1;
if (dms->used_memslots > dms->required_memslots) {
warn_report("Memory devices use more memory slots than indicated as required.");
}
}
static void memory_devices_region_add(MemoryListener *listener,
MemoryRegionSection *mrs)
{
return memory_devices_region_mod(listener, mrs, true);
}
static void memory_devices_region_del(MemoryListener *listener,
MemoryRegionSection *mrs)
{
return memory_devices_region_mod(listener, mrs, false);
}
void machine_memory_devices_init(MachineState *ms, hwaddr base, uint64_t size)
{
g_assert(size);
g_assert(!ms->device_memory);
ms->device_memory = g_new0(DeviceMemoryState, 1);
ms->device_memory->base = base;
memory_region_init(&ms->device_memory->mr, OBJECT(ms), "device-memory",
size);
address_space_init(&ms->device_memory->as, &ms->device_memory->mr,
"device-memory");
memory_region_add_subregion(get_system_memory(), ms->device_memory->base,
&ms->device_memory->mr);
/* Track the number of memslots used by memory devices. */
ms->device_memory->listener.region_add = memory_devices_region_add;
ms->device_memory->listener.region_del = memory_devices_region_del;
memory_listener_register(&ms->device_memory->listener,
&ms->device_memory->as);
}
static const TypeInfo memory_device_info = {
.name = TYPE_MEMORY_DEVICE,
.parent = TYPE_INTERFACE,
.class_size = sizeof(MemoryDeviceClass),
};
static void memory_device_register_types(void)
{
type_register_static(&memory_device_info);
}
type_init(memory_device_register_types)
+13
View File
@@ -0,0 +1,13 @@
mem_ss = ss.source_set()
mem_ss.add(files('memory-device.c'))
mem_ss.add(when: 'CONFIG_DIMM', if_true: files('pc-dimm.c'))
mem_ss.add(when: 'CONFIG_NPCM7XX', if_true: files('npcm7xx_mc.c'))
mem_ss.add(when: 'CONFIG_NVDIMM', if_true: files('nvdimm.c'))
mem_ss.add(when: 'CONFIG_CXL_MEM_DEVICE', if_true: files('cxl_type3.c'))
mem_ss.add(when: 'CONFIG_SP_MEM', if_true: files('sp-mem.c'))
stub_ss.add(files('cxl_type3_stubs.c'))
stub_ss.add(files('memory-device-stubs.c'))
system_ss.add_all(when: 'CONFIG_MEM_DEVICE', if_true: mem_ss)
system_ss.add(when: 'CONFIG_SPARSE_MEM', if_true: files('sparse-mem.c'))
+84
View File
@@ -0,0 +1,84 @@
/*
* Nuvoton NPCM7xx Memory Controller stub
*
* Copyright 2020 Google LLC
*
* 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/mem/npcm7xx_mc.h"
#include "qapi/error.h"
#include "qemu/log.h"
#include "qemu/module.h"
#include "qemu/units.h"
#define NPCM7XX_MC_REGS_SIZE (4 * KiB)
static uint64_t npcm7xx_mc_read(void *opaque, hwaddr addr, unsigned int size)
{
/*
* If bits 8..11 @ offset 0 are not zero, the boot block thinks the memory
* controller has already been initialized and will skip DDR training.
*/
if (addr == 0) {
return 0x100;
}
qemu_log_mask(LOG_UNIMP, "%s: mostly unimplemented\n", __func__);
return 0;
}
static void npcm7xx_mc_write(void *opaque, hwaddr addr, uint64_t v,
unsigned int size)
{
qemu_log_mask(LOG_UNIMP, "%s: mostly unimplemented\n", __func__);
}
static const MemoryRegionOps npcm7xx_mc_ops = {
.read = npcm7xx_mc_read,
.write = npcm7xx_mc_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 4,
.max_access_size = 4,
.unaligned = false,
},
};
static void npcm7xx_mc_realize(DeviceState *dev, Error **errp)
{
NPCM7xxMCState *s = NPCM7XX_MC(dev);
memory_region_init_io(&s->mmio, OBJECT(s), &npcm7xx_mc_ops, s, "regs",
NPCM7XX_MC_REGS_SIZE);
sysbus_init_mmio(SYS_BUS_DEVICE(s), &s->mmio);
}
static void npcm7xx_mc_class_init(ObjectClass *klass, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
dc->desc = "NPCM7xx Memory Controller stub";
dc->realize = npcm7xx_mc_realize;
}
static const TypeInfo npcm7xx_mc_types[] = {
{
.name = TYPE_NPCM7XX_MC,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(NPCM7xxMCState),
.class_init = npcm7xx_mc_class_init,
},
};
DEFINE_TYPES(npcm7xx_mc_types);
+285
View File
@@ -0,0 +1,285 @@
/*
* Non-Volatile Dual In-line Memory Module Virtualization Implementation
*
* Copyright(C) 2015 Intel Corporation.
*
* Author:
* Xiao Guangrong <[email protected]>
*
* Currently, it only supports PMEM Virtualization.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*/
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qemu/pmem.h"
#include "qapi/error.h"
#include "qapi/visitor.h"
#include "hw/mem/nvdimm.h"
#include "hw/core/qdev-properties.h"
#include "hw/mem/memory-device.h"
#include "system/hostmem.h"
static void nvdimm_get_label_size(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
NVDIMMDevice *nvdimm = NVDIMM(obj);
uint64_t value = nvdimm->label_size;
visit_type_size(v, name, &value, errp);
}
static void nvdimm_set_label_size(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
NVDIMMDevice *nvdimm = NVDIMM(obj);
uint64_t value;
if (nvdimm->nvdimm_mr) {
error_setg(errp, "cannot change property value");
return;
}
if (!visit_type_size(v, name, &value, errp)) {
return;
}
if (value < MIN_NAMESPACE_LABEL_SIZE) {
error_setg(errp, "Property '%s.%s' (0x%" PRIx64 ") is required"
" at least 0x%lx", object_get_typename(obj), name, value,
MIN_NAMESPACE_LABEL_SIZE);
return;
}
nvdimm->label_size = value;
}
static void nvdimm_get_uuid(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
NVDIMMDevice *nvdimm = NVDIMM(obj);
char *value = NULL;
value = qemu_uuid_unparse_strdup(&nvdimm->uuid);
visit_type_str(v, name, &value, errp);
g_free(value);
}
static void nvdimm_set_uuid(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
NVDIMMDevice *nvdimm = NVDIMM(obj);
char *value;
if (!visit_type_str(v, name, &value, errp)) {
return;
}
if (qemu_uuid_parse(value, &nvdimm->uuid) != 0) {
error_setg(errp, "Property '%s.%s' has invalid value",
object_get_typename(obj), name);
}
g_free(value);
}
static void nvdimm_init(Object *obj)
{
object_property_add(obj, NVDIMM_LABEL_SIZE_PROP, "size",
nvdimm_get_label_size, nvdimm_set_label_size, NULL,
NULL);
object_property_add(obj, NVDIMM_UUID_PROP, "QemuUUID", nvdimm_get_uuid,
nvdimm_set_uuid, NULL, NULL);
}
static void nvdimm_finalize(Object *obj)
{
NVDIMMDevice *nvdimm = NVDIMM(obj);
g_free(nvdimm->nvdimm_mr);
}
static void nvdimm_prepare_memory_region(NVDIMMDevice *nvdimm, Error **errp)
{
PCDIMMDevice *dimm = PC_DIMM(nvdimm);
uint64_t align, pmem_size, size;
MemoryRegion *mr;
g_assert(!nvdimm->nvdimm_mr);
if (!dimm->hostmem) {
error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property must be set");
return;
}
mr = host_memory_backend_get_memory(dimm->hostmem);
align = memory_region_get_alignment(mr);
size = memory_region_size(mr);
pmem_size = size - nvdimm->label_size;
nvdimm->label_data = memory_region_get_ram_ptr(mr) + pmem_size;
pmem_size = QEMU_ALIGN_DOWN(pmem_size, align);
if (size <= nvdimm->label_size || !pmem_size) {
HostMemoryBackend *hostmem = dimm->hostmem;
error_setg(errp, "the size of memdev %s (0x%" PRIx64 ") is too "
"small to contain nvdimm label (0x%" PRIx64 ") and "
"aligned PMEM (0x%" PRIx64 ")",
object_get_canonical_path_component(OBJECT(hostmem)),
memory_region_size(mr), nvdimm->label_size, align);
return;
}
if (!nvdimm->unarmed && memory_region_is_rom(mr)) {
HostMemoryBackend *hostmem = dimm->hostmem;
error_setg(errp, "'unarmed' property must be 'on' since memdev %s "
"is read-only",
object_get_canonical_path_component(OBJECT(hostmem)));
return;
}
if (memory_region_is_rom(mr)) {
nvdimm->readonly = true;
}
nvdimm->nvdimm_mr = g_new(MemoryRegion, 1);
memory_region_init_alias(nvdimm->nvdimm_mr, OBJECT(dimm),
"nvdimm-memory", mr, 0, pmem_size);
memory_region_set_nonvolatile(nvdimm->nvdimm_mr, true);
nvdimm->nvdimm_mr->align = align;
}
static MemoryRegion *nvdimm_md_get_memory_region(MemoryDeviceState *md,
Error **errp)
{
NVDIMMDevice *nvdimm = NVDIMM(md);
Error *local_err = NULL;
if (!nvdimm->nvdimm_mr) {
nvdimm_prepare_memory_region(nvdimm, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return NULL;
}
}
return nvdimm->nvdimm_mr;
}
static void nvdimm_realize(PCDIMMDevice *dimm, Error **errp)
{
NVDIMMDevice *nvdimm = NVDIMM(dimm);
NVDIMMClass *ndc = NVDIMM_GET_CLASS(nvdimm);
if (!nvdimm->nvdimm_mr) {
nvdimm_prepare_memory_region(nvdimm, errp);
}
if (ndc->realize) {
ndc->realize(nvdimm, errp);
}
}
static void nvdimm_unrealize(PCDIMMDevice *dimm)
{
NVDIMMDevice *nvdimm = NVDIMM(dimm);
NVDIMMClass *ndc = NVDIMM_GET_CLASS(nvdimm);
if (ndc->unrealize) {
ndc->unrealize(nvdimm);
}
}
/*
* the caller should check the input parameters before calling
* label read/write functions.
*/
static void nvdimm_validate_rw_label_data(NVDIMMDevice *nvdimm, uint64_t size,
uint64_t offset, bool is_write)
{
assert((nvdimm->label_size >= size + offset) && (offset + size > offset));
assert(!is_write || !nvdimm->readonly);
}
static void nvdimm_read_label_data(NVDIMMDevice *nvdimm, void *buf,
uint64_t size, uint64_t offset)
{
nvdimm_validate_rw_label_data(nvdimm, size, offset, false);
memcpy(buf, nvdimm->label_data + offset, size);
}
static void nvdimm_write_label_data(NVDIMMDevice *nvdimm, const void *buf,
uint64_t size, uint64_t offset)
{
MemoryRegion *mr;
PCDIMMDevice *dimm = PC_DIMM(nvdimm);
bool is_pmem = object_property_get_bool(OBJECT(dimm->hostmem),
"pmem", NULL);
uint64_t backend_offset;
nvdimm_validate_rw_label_data(nvdimm, size, offset, true);
if (!is_pmem) {
memcpy(nvdimm->label_data + offset, buf, size);
} else {
pmem_memcpy_persist(nvdimm->label_data + offset, buf, size);
}
mr = host_memory_backend_get_memory(dimm->hostmem);
backend_offset = memory_region_size(mr) - nvdimm->label_size + offset;
memory_region_set_dirty(mr, backend_offset, size);
}
static const Property nvdimm_properties[] = {
DEFINE_PROP_BOOL(NVDIMM_UNARMED_PROP, NVDIMMDevice, unarmed, false),
};
static void nvdimm_class_init(ObjectClass *oc, const void *data)
{
PCDIMMDeviceClass *ddc = PC_DIMM_CLASS(oc);
MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc);
NVDIMMClass *nvc = NVDIMM_CLASS(oc);
DeviceClass *dc = DEVICE_CLASS(oc);
ddc->realize = nvdimm_realize;
ddc->unrealize = nvdimm_unrealize;
mdc->get_memory_region = nvdimm_md_get_memory_region;
device_class_set_props(dc, nvdimm_properties);
nvc->read_label_data = nvdimm_read_label_data;
nvc->write_label_data = nvdimm_write_label_data;
set_bit(DEVICE_CATEGORY_STORAGE, dc->categories);
}
static const TypeInfo nvdimm_info = {
.name = TYPE_NVDIMM,
.parent = TYPE_PC_DIMM,
.class_size = sizeof(NVDIMMClass),
.class_init = nvdimm_class_init,
.instance_size = sizeof(NVDIMMDevice),
.instance_init = nvdimm_init,
.instance_finalize = nvdimm_finalize,
};
static void nvdimm_register_types(void)
{
type_register_static(&nvdimm_info);
}
type_init(nvdimm_register_types)
+315
View File
@@ -0,0 +1,315 @@
/*
* Dimm device for Memory Hotplug
*
* Copyright ProfitBricks GmbH 2012
* Copyright (C) 2014 Red Hat Inc
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>
*/
#include "qemu/osdep.h"
#include "hw/core/boards.h"
#include "hw/mem/pc-dimm.h"
#include "hw/core/qdev-properties.h"
#include "migration/vmstate.h"
#include "hw/mem/nvdimm.h"
#include "hw/mem/memory-device.h"
#include "qapi/error.h"
#include "qapi/visitor.h"
#include "qemu/module.h"
#include "system/hostmem.h"
#include "system/numa.h"
#include "trace.h"
static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp);
static MemoryRegion *pc_dimm_get_memory_region(PCDIMMDevice *dimm, Error **errp)
{
if (!dimm->hostmem) {
error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property must be set");
return NULL;
}
return host_memory_backend_get_memory(dimm->hostmem);
}
void pc_dimm_pre_plug(PCDIMMDevice *dimm, MachineState *machine, Error **errp)
{
Error *local_err = NULL;
int slot;
slot = object_property_get_int(OBJECT(dimm), PC_DIMM_SLOT_PROP,
&error_abort);
if ((slot < 0 || slot >= machine->ram_slots) &&
slot != PC_DIMM_UNASSIGNED_SLOT) {
error_setg(errp,
"invalid slot number %d, valid range is [0-%" PRIu64 "]",
slot, machine->ram_slots - 1);
return;
}
slot = pc_dimm_get_free_slot(slot == PC_DIMM_UNASSIGNED_SLOT ? NULL : &slot,
machine->ram_slots, &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
object_property_set_int(OBJECT(dimm), PC_DIMM_SLOT_PROP, slot,
&error_abort);
trace_mhp_pc_dimm_assigned_slot(slot);
memory_device_pre_plug(MEMORY_DEVICE(dimm), machine, errp);
}
void pc_dimm_plug(PCDIMMDevice *dimm, MachineState *machine)
{
MemoryRegion *vmstate_mr = pc_dimm_get_memory_region(dimm,
&error_abort);
memory_device_plug(MEMORY_DEVICE(dimm), machine);
vmstate_register_ram(vmstate_mr, DEVICE(dimm));
/* count only "real" DIMMs, not NVDIMMs */
if (!object_dynamic_cast(OBJECT(dimm), TYPE_NVDIMM)) {
machine->device_memory->dimm_size += memory_region_size(vmstate_mr);
}
}
void pc_dimm_unplug(PCDIMMDevice *dimm, MachineState *machine)
{
MemoryRegion *vmstate_mr = pc_dimm_get_memory_region(dimm,
&error_abort);
memory_device_unplug(MEMORY_DEVICE(dimm), machine);
vmstate_unregister_ram(vmstate_mr, DEVICE(dimm));
if (!object_dynamic_cast(OBJECT(dimm), TYPE_NVDIMM)) {
machine->device_memory->dimm_size -= memory_region_size(vmstate_mr);
}
}
static int pc_dimm_slot2bitmap(Object *obj, void *opaque)
{
unsigned long *bitmap = opaque;
if (object_dynamic_cast(obj, TYPE_PC_DIMM)) {
DeviceState *dev = DEVICE(obj);
if (qdev_is_realized(dev)) { /* count only realized DIMMs */
PCDIMMDevice *d = PC_DIMM(obj);
set_bit(d->slot, bitmap);
}
}
object_child_foreach(obj, pc_dimm_slot2bitmap, opaque);
return 0;
}
static int pc_dimm_get_free_slot(const int *hint, int max_slots, Error **errp)
{
unsigned long *bitmap;
int slot = 0;
if (max_slots <= 0) {
error_setg(errp, "no slots where allocated, please specify "
"the 'slots' option");
return slot;
}
bitmap = bitmap_new(max_slots);
object_child_foreach(qdev_get_machine(), pc_dimm_slot2bitmap, bitmap);
/* check if requested slot is not occupied */
if (hint) {
if (*hint >= max_slots) {
error_setg(errp, "invalid slot# %d, should be less than %d",
*hint, max_slots);
} else if (!test_bit(*hint, bitmap)) {
slot = *hint;
} else {
error_setg(errp, "slot %d is busy", *hint);
}
goto out;
}
/* search for free slot */
slot = find_first_zero_bit(bitmap, max_slots);
if (slot == max_slots) {
error_setg(errp, "no free slots available");
}
out:
g_free(bitmap);
return slot;
}
static const Property pc_dimm_properties[] = {
DEFINE_PROP_UINT64(PC_DIMM_ADDR_PROP, PCDIMMDevice, addr, 0),
DEFINE_PROP_UINT32(PC_DIMM_NODE_PROP, PCDIMMDevice, node, 0),
DEFINE_PROP_INT32(PC_DIMM_SLOT_PROP, PCDIMMDevice, slot,
PC_DIMM_UNASSIGNED_SLOT),
DEFINE_PROP_LINK(PC_DIMM_MEMDEV_PROP, PCDIMMDevice, hostmem,
TYPE_MEMORY_BACKEND, HostMemoryBackend *),
};
static void pc_dimm_get_size(Object *obj, Visitor *v, const char *name,
void *opaque, Error **errp)
{
Error *local_err = NULL;
uint64_t value;
value = memory_device_get_region_size(MEMORY_DEVICE(obj), &local_err);
if (local_err) {
error_propagate(errp, local_err);
return;
}
visit_type_uint64(v, name, &value, errp);
}
static void pc_dimm_init(Object *obj)
{
object_property_add(obj, PC_DIMM_SIZE_PROP, "uint64", pc_dimm_get_size,
NULL, NULL, NULL);
}
static void pc_dimm_realize(DeviceState *dev, Error **errp)
{
PCDIMMDevice *dimm = PC_DIMM(dev);
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
MachineState *ms = MACHINE(qdev_get_machine());
if (ms->numa_state) {
int nb_numa_nodes = ms->numa_state->num_nodes;
if (((nb_numa_nodes > 0) && (dimm->node >= nb_numa_nodes)) ||
(!nb_numa_nodes && dimm->node)) {
error_setg(errp, "'DIMM property " PC_DIMM_NODE_PROP " has value %"
PRIu32 "' which exceeds the number of numa nodes: %d",
dimm->node, nb_numa_nodes ? nb_numa_nodes : 1);
return;
}
} else if (dimm->node > 0) {
error_setg(errp, "machine doesn't support NUMA");
return;
}
if (!dimm->hostmem) {
error_setg(errp, "'" PC_DIMM_MEMDEV_PROP "' property is not set");
return;
} else if (host_memory_backend_is_mapped(dimm->hostmem)) {
error_setg(errp, "can't use already busy memdev: %s",
object_get_canonical_path_component(OBJECT(dimm->hostmem)));
return;
}
if (ddc->realize) {
ddc->realize(dimm, errp);
}
host_memory_backend_set_mapped(dimm->hostmem, true);
}
static void pc_dimm_unrealize(DeviceState *dev)
{
PCDIMMDevice *dimm = PC_DIMM(dev);
PCDIMMDeviceClass *ddc = PC_DIMM_GET_CLASS(dimm);
if (ddc->unrealize) {
ddc->unrealize(dimm);
}
host_memory_backend_set_mapped(dimm->hostmem, false);
}
static uint64_t pc_dimm_md_get_addr(const MemoryDeviceState *md)
{
return object_property_get_uint(OBJECT(md), PC_DIMM_ADDR_PROP,
&error_abort);
}
static void pc_dimm_md_set_addr(MemoryDeviceState *md, uint64_t addr,
Error **errp)
{
object_property_set_uint(OBJECT(md), PC_DIMM_ADDR_PROP, addr, errp);
}
static MemoryRegion *pc_dimm_md_get_memory_region(MemoryDeviceState *md,
Error **errp)
{
return pc_dimm_get_memory_region(PC_DIMM(md), errp);
}
static void pc_dimm_md_fill_device_info(const MemoryDeviceState *md,
MemoryDeviceInfo *info)
{
PCDIMMDeviceInfo *di = g_new0(PCDIMMDeviceInfo, 1);
const DeviceClass *dc = DEVICE_GET_CLASS(md);
const PCDIMMDevice *dimm = PC_DIMM(md);
const DeviceState *dev = DEVICE(md);
if (dev->id) {
di->id = g_strdup(dev->id);
}
di->hotplugged = dev->hotplugged;
di->hotpluggable = dc->hotpluggable;
di->addr = dimm->addr;
di->slot = dimm->slot;
di->node = dimm->node;
di->size = object_property_get_uint(OBJECT(dimm), PC_DIMM_SIZE_PROP,
NULL);
di->memdev = object_get_canonical_path(OBJECT(dimm->hostmem));
if (object_dynamic_cast(OBJECT(dev), TYPE_NVDIMM)) {
info->u.nvdimm.data = di;
info->type = MEMORY_DEVICE_INFO_KIND_NVDIMM;
} else {
info->u.dimm.data = di;
info->type = MEMORY_DEVICE_INFO_KIND_DIMM;
}
}
static void pc_dimm_class_init(ObjectClass *oc, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc);
dc->realize = pc_dimm_realize;
dc->unrealize = pc_dimm_unrealize;
device_class_set_props(dc, pc_dimm_properties);
dc->desc = "DIMM memory module";
mdc->get_addr = pc_dimm_md_get_addr;
mdc->set_addr = pc_dimm_md_set_addr;
/* for a dimm plugged_size == region_size */
mdc->get_plugged_size = memory_device_get_region_size;
mdc->get_memory_region = pc_dimm_md_get_memory_region;
mdc->fill_device_info = pc_dimm_md_fill_device_info;
}
static const TypeInfo pc_dimm_info = {
.name = TYPE_PC_DIMM,
.parent = TYPE_DEVICE,
.instance_size = sizeof(PCDIMMDevice),
.instance_init = pc_dimm_init,
.class_init = pc_dimm_class_init,
.class_size = sizeof(PCDIMMDeviceClass),
.interfaces = (const InterfaceInfo[]) {
{ TYPE_MEMORY_DEVICE },
{ }
},
};
static void pc_dimm_register_types(void)
{
type_register_static(&pc_dimm_info);
}
type_init(pc_dimm_register_types)
+128
View File
@@ -0,0 +1,128 @@
/*
* Specific Purpose Memory (SPM) device
*
* Copyright (c) 2026 Advanced Micro Devices, Inc.
*
* Authors:
* FangSheng Huang <[email protected]>
*
* SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "qemu/osdep.h"
#include "qemu/module.h"
#include "qapi/error.h"
#include "hw/core/qdev-properties.h"
#include "hw/core/qdev.h"
#include "hw/mem/sp-mem.h"
#include "hw/mem/memory-device.h"
#include "system/hostmem.h"
#define SP_MEM_MEMDEV_PROP "memdev"
#define SP_MEM_NODE_PROP "node"
#define SP_MEM_ADDR_PROP "addr"
static const Property sp_mem_properties[] = {
DEFINE_PROP_LINK(SP_MEM_MEMDEV_PROP, SpMemDevice, hostmem,
TYPE_MEMORY_BACKEND, HostMemoryBackend *),
DEFINE_PROP_UINT32(SP_MEM_NODE_PROP, SpMemDevice, node, 0),
DEFINE_PROP_UINT64(SP_MEM_ADDR_PROP, SpMemDevice, addr, 0),
};
static uint64_t sp_mem_get_addr(const MemoryDeviceState *md)
{
return object_property_get_uint(OBJECT(md), SP_MEM_ADDR_PROP,
&error_abort);
}
static void sp_mem_set_addr(MemoryDeviceState *md, uint64_t addr,
Error **errp)
{
object_property_set_uint(OBJECT(md), SP_MEM_ADDR_PROP, addr, errp);
}
static MemoryRegion *sp_mem_get_memory_region(MemoryDeviceState *md,
Error **errp)
{
SpMemDevice *spm = SP_MEM(md);
if (!spm->hostmem) {
error_setg(errp, "'%s' property must be set", SP_MEM_MEMDEV_PROP);
return NULL;
}
return host_memory_backend_get_memory(spm->hostmem);
}
static void sp_mem_fill_device_info(const MemoryDeviceState *md,
MemoryDeviceInfo *info)
{
SpMemDeviceInfo *di = g_new0(SpMemDeviceInfo, 1);
SpMemDevice *spm = SP_MEM(md);
DeviceState *dev = DEVICE(md);
di->id = dev->id ? g_strdup(dev->id) : NULL;
di->addr = spm->addr;
di->size = memory_region_size(
host_memory_backend_get_memory(spm->hostmem));
di->node = spm->node;
di->memdev = object_get_canonical_path(OBJECT(spm->hostmem));
info->u.sp_mem.data = di;
info->type = MEMORY_DEVICE_INFO_KIND_SP_MEM;
}
static void sp_mem_realize(DeviceState *dev, Error **errp)
{
SpMemDevice *spm = SP_MEM(dev);
if (!spm->hostmem) {
error_setg(errp, "'%s' property is required", SP_MEM_MEMDEV_PROP);
return;
}
if (host_memory_backend_is_mapped(spm->hostmem)) {
error_setg(errp, "memory backend '%s' is already in use",
object_get_canonical_path_component(OBJECT(spm->hostmem)));
return;
}
host_memory_backend_set_mapped(spm->hostmem, true);
}
static void sp_mem_unrealize(DeviceState *dev)
{
SpMemDevice *spm = SP_MEM(dev);
host_memory_backend_set_mapped(spm->hostmem, false);
}
static void sp_mem_class_init(ObjectClass *oc, const void *data)
{
DeviceClass *dc = DEVICE_CLASS(oc);
MemoryDeviceClass *mdc = MEMORY_DEVICE_CLASS(oc);
dc->desc = "SPM (Specific Purpose Memory) device";
dc->hotpluggable = false;
dc->realize = sp_mem_realize;
dc->unrealize = sp_mem_unrealize;
device_class_set_props(dc, sp_mem_properties);
mdc->get_addr = sp_mem_get_addr;
mdc->set_addr = sp_mem_set_addr;
mdc->get_memory_region = sp_mem_get_memory_region;
mdc->get_plugged_size = memory_device_get_region_size;
mdc->fill_device_info = sp_mem_fill_device_info;
}
static const TypeInfo sp_mem_types[] = {
{
.name = TYPE_SP_MEM,
.parent = TYPE_DEVICE,
.class_init = sp_mem_class_init,
.instance_size = sizeof(SpMemDevice),
.interfaces = (InterfaceInfo[]) {
{ TYPE_MEMORY_DEVICE },
{ }
},
},
};
DEFINE_TYPES(sp_mem_types)
+160
View File
@@ -0,0 +1,160 @@
/*
* A sparse memory device. Useful for fuzzing
*
* Copyright Red Hat Inc., 2021
*
* Authors:
* Alexander Bulekov <[email protected]>
*
* This work is licensed under the terms of the GNU GPL, version 2 or later.
* See the COPYING file in the top-level directory.
*/
#include "qemu/osdep.h"
#include "qemu/error-report.h"
#include "hw/core/qdev-properties.h"
#include "hw/core/sysbus.h"
#include "qapi/error.h"
#include "qemu/units.h"
#include "system/qtest.h"
#include "hw/mem/sparse-mem.h"
#define SPARSE_MEM(obj) OBJECT_CHECK(SparseMemState, (obj), TYPE_SPARSE_MEM)
#define SPARSE_BLOCK_SIZE 0x1000
typedef struct SparseMemState {
SysBusDevice parent_obj;
MemoryRegion mmio;
uint64_t baseaddr;
uint64_t length;
uint64_t size_used;
uint64_t maxsize;
GHashTable *mapped;
} SparseMemState;
typedef struct sparse_mem_block {
uint8_t data[SPARSE_BLOCK_SIZE];
} sparse_mem_block;
static uint64_t sparse_mem_read(void *opaque, hwaddr addr, unsigned int size)
{
SparseMemState *s = opaque;
uint64_t ret = 0;
size_t pfn = addr / SPARSE_BLOCK_SIZE;
size_t offset = addr % SPARSE_BLOCK_SIZE;
sparse_mem_block *block;
block = g_hash_table_lookup(s->mapped, (void *)pfn);
if (block) {
assert(offset + size <= sizeof(block->data));
memcpy(&ret, block->data + offset, size);
}
return ret;
}
static void sparse_mem_write(void *opaque, hwaddr addr, uint64_t v,
unsigned int size)
{
SparseMemState *s = opaque;
size_t pfn = addr / SPARSE_BLOCK_SIZE;
size_t offset = addr % SPARSE_BLOCK_SIZE;
sparse_mem_block *block;
if (!g_hash_table_lookup(s->mapped, (void *)pfn) &&
s->size_used + SPARSE_BLOCK_SIZE < s->maxsize && v) {
g_hash_table_insert(s->mapped, (void *)pfn,
g_new0(sparse_mem_block, 1));
s->size_used += sizeof(block->data);
}
block = g_hash_table_lookup(s->mapped, (void *)pfn);
if (!block) {
return;
}
assert(offset + size <= sizeof(block->data));
memcpy(block->data + offset, &v, size);
}
static void sparse_mem_enter_reset(Object *obj, ResetType type)
{
SparseMemState *s = SPARSE_MEM(obj);
g_hash_table_remove_all(s->mapped);
}
static const MemoryRegionOps sparse_mem_ops = {
.read = sparse_mem_read,
.write = sparse_mem_write,
.endianness = DEVICE_LITTLE_ENDIAN,
.valid = {
.min_access_size = 1,
.max_access_size = 8,
.unaligned = false,
},
};
static const Property sparse_mem_properties[] = {
/* The base address of the memory */
DEFINE_PROP_UINT64("baseaddr", SparseMemState, baseaddr, 0x0),
/* The length of the sparse memory region */
DEFINE_PROP_UINT64("length", SparseMemState, length, UINT64_MAX),
/* Max amount of actual memory that can be used to back the sparse memory */
DEFINE_PROP_UINT64("maxsize", SparseMemState, maxsize, 10 * MiB),
};
MemoryRegion *sparse_mem_init(uint64_t addr, uint64_t length)
{
DeviceState *dev;
dev = qdev_new(TYPE_SPARSE_MEM);
qdev_prop_set_uint64(dev, "baseaddr", addr);
qdev_prop_set_uint64(dev, "length", length);
sysbus_realize_and_unref(SYS_BUS_DEVICE(dev), &error_fatal);
sysbus_mmio_map_overlap(SYS_BUS_DEVICE(dev), 0, addr, -10000);
return &SPARSE_MEM(dev)->mmio;
}
static void sparse_mem_realize(DeviceState *dev, Error **errp)
{
SparseMemState *s = SPARSE_MEM(dev);
SysBusDevice *sbd = SYS_BUS_DEVICE(dev);
if (!qtest_enabled()) {
error_setg(errp, "sparse_mem device should only be used "
"for testing with QTest");
return;
}
assert(s->baseaddr + s->length > s->baseaddr);
s->mapped = g_hash_table_new_full(NULL, NULL, NULL,
(GDestroyNotify)g_free);
memory_region_init_io(&s->mmio, OBJECT(s), &sparse_mem_ops, s,
"sparse-mem", s->length);
sysbus_init_mmio(sbd, &s->mmio);
}
static void sparse_mem_class_init(ObjectClass *klass, const void *data)
{
ResettableClass *rc = RESETTABLE_CLASS(klass);
DeviceClass *dc = DEVICE_CLASS(klass);
device_class_set_props(dc, sparse_mem_properties);
dc->desc = "Sparse Memory Device";
dc->realize = sparse_mem_realize;
rc->phases.enter = sparse_mem_enter_reset;
}
static const TypeInfo sparse_mem_types[] = {
{
.name = TYPE_SPARSE_MEM,
.parent = TYPE_SYS_BUS_DEVICE,
.instance_size = sizeof(SparseMemState),
.class_init = sparse_mem_class_init,
},
};
DEFINE_TYPES(sparse_mem_types);
+8
View File
@@ -0,0 +1,8 @@
# See docs/devel/tracing.rst for syntax documentation.
# pc-dimm.c
mhp_pc_dimm_assigned_slot(int slot) "%d"
# memory-device.c
memory_device_pre_plug(const char *id, uint64_t addr) "id=%s addr=0x%"PRIx64
memory_device_plug(const char *id, uint64_t addr) "id=%s addr=0x%"PRIx64
memory_device_unplug(const char *id, uint64_t addr) "id=%s addr=0x%"PRIx64
+1
View File
@@ -0,0 +1 @@
#include "trace/trace-hw_mem.h"