2 * Non-Volatile Dual In-line Memory Module Virtualization Implementation
4 * Copyright(C) 2015 Intel Corporation.
7 * Xiao Guangrong <guangrong.xiao@linux.intel.com>
9 * Currently, it only supports PMEM Virtualization.
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, see <http://www.gnu.org/licenses/>
25 #include "qemu/osdep.h"
26 #include "qemu/module.h"
27 #include "qemu/pmem.h"
28 #include "qapi/error.h"
29 #include "qapi/visitor.h"
30 #include "hw/mem/nvdimm.h"
31 #include "hw/qdev-properties.h"
32 #include "hw/mem/memory-device.h"
33 #include "sysemu/hostmem.h"
35 static void nvdimm_get_label_size(Object
*obj
, Visitor
*v
, const char *name
,
36 void *opaque
, Error
**errp
)
38 NVDIMMDevice
*nvdimm
= NVDIMM(obj
);
39 uint64_t value
= nvdimm
->label_size
;
41 visit_type_size(v
, name
, &value
, errp
);
44 static void nvdimm_set_label_size(Object
*obj
, Visitor
*v
, const char *name
,
45 void *opaque
, Error
**errp
)
47 NVDIMMDevice
*nvdimm
= NVDIMM(obj
);
50 if (nvdimm
->nvdimm_mr
) {
51 error_setg(errp
, "cannot change property value");
55 if (!visit_type_size(v
, name
, &value
, errp
)) {
58 if (value
< MIN_NAMESPACE_LABEL_SIZE
) {
59 error_setg(errp
, "Property '%s.%s' (0x%" PRIx64
") is required"
60 " at least 0x%lx", object_get_typename(obj
), name
, value
,
61 MIN_NAMESPACE_LABEL_SIZE
);
65 nvdimm
->label_size
= value
;
68 static void nvdimm_get_uuid(Object
*obj
, Visitor
*v
, const char *name
,
69 void *opaque
, Error
**errp
)
71 NVDIMMDevice
*nvdimm
= NVDIMM(obj
);
74 value
= qemu_uuid_unparse_strdup(&nvdimm
->uuid
);
76 visit_type_str(v
, name
, &value
, errp
);
81 static void nvdimm_set_uuid(Object
*obj
, Visitor
*v
, const char *name
,
82 void *opaque
, Error
**errp
)
84 NVDIMMDevice
*nvdimm
= NVDIMM(obj
);
87 if (!visit_type_str(v
, name
, &value
, errp
)) {
91 if (qemu_uuid_parse(value
, &nvdimm
->uuid
) != 0) {
92 error_setg(errp
, "Property '%s.%s' has invalid value",
93 object_get_typename(obj
), name
);
100 static void nvdimm_init(Object
*obj
)
102 object_property_add(obj
, NVDIMM_LABEL_SIZE_PROP
, "int",
103 nvdimm_get_label_size
, nvdimm_set_label_size
, NULL
,
106 object_property_add(obj
, NVDIMM_UUID_PROP
, "QemuUUID", nvdimm_get_uuid
,
107 nvdimm_set_uuid
, NULL
, NULL
);
110 static void nvdimm_finalize(Object
*obj
)
112 NVDIMMDevice
*nvdimm
= NVDIMM(obj
);
114 g_free(nvdimm
->nvdimm_mr
);
117 static void nvdimm_prepare_memory_region(NVDIMMDevice
*nvdimm
, Error
**errp
)
119 PCDIMMDevice
*dimm
= PC_DIMM(nvdimm
);
120 uint64_t align
, pmem_size
, size
;
123 g_assert(!nvdimm
->nvdimm_mr
);
125 if (!dimm
->hostmem
) {
126 error_setg(errp
, "'" PC_DIMM_MEMDEV_PROP
"' property must be set");
130 mr
= host_memory_backend_get_memory(dimm
->hostmem
);
131 align
= memory_region_get_alignment(mr
);
132 size
= memory_region_size(mr
);
134 pmem_size
= size
- nvdimm
->label_size
;
135 nvdimm
->label_data
= memory_region_get_ram_ptr(mr
) + pmem_size
;
136 pmem_size
= QEMU_ALIGN_DOWN(pmem_size
, align
);
138 if (size
<= nvdimm
->label_size
|| !pmem_size
) {
139 HostMemoryBackend
*hostmem
= dimm
->hostmem
;
141 error_setg(errp
, "the size of memdev %s (0x%" PRIx64
") is too "
142 "small to contain nvdimm label (0x%" PRIx64
") and "
143 "aligned PMEM (0x%" PRIx64
")",
144 object_get_canonical_path_component(OBJECT(hostmem
)),
145 memory_region_size(mr
), nvdimm
->label_size
, align
);
149 if (!nvdimm
->unarmed
&& memory_region_is_rom(mr
)) {
150 HostMemoryBackend
*hostmem
= dimm
->hostmem
;
152 error_setg(errp
, "'unarmed' property must be 'on' since memdev %s "
154 object_get_canonical_path_component(OBJECT(hostmem
)));
157 if (memory_region_is_rom(mr
)) {
158 nvdimm
->readonly
= true;
161 nvdimm
->nvdimm_mr
= g_new(MemoryRegion
, 1);
162 memory_region_init_alias(nvdimm
->nvdimm_mr
, OBJECT(dimm
),
163 "nvdimm-memory", mr
, 0, pmem_size
);
164 memory_region_set_nonvolatile(nvdimm
->nvdimm_mr
, true);
165 nvdimm
->nvdimm_mr
->align
= align
;
168 static MemoryRegion
*nvdimm_md_get_memory_region(MemoryDeviceState
*md
,
171 NVDIMMDevice
*nvdimm
= NVDIMM(md
);
172 Error
*local_err
= NULL
;
174 if (!nvdimm
->nvdimm_mr
) {
175 nvdimm_prepare_memory_region(nvdimm
, &local_err
);
177 error_propagate(errp
, local_err
);
181 return nvdimm
->nvdimm_mr
;
184 static void nvdimm_realize(PCDIMMDevice
*dimm
, Error
**errp
)
186 NVDIMMDevice
*nvdimm
= NVDIMM(dimm
);
187 NVDIMMClass
*ndc
= NVDIMM_GET_CLASS(nvdimm
);
189 if (!nvdimm
->nvdimm_mr
) {
190 nvdimm_prepare_memory_region(nvdimm
, errp
);
194 ndc
->realize(nvdimm
, errp
);
198 static void nvdimm_unrealize(PCDIMMDevice
*dimm
)
200 NVDIMMDevice
*nvdimm
= NVDIMM(dimm
);
201 NVDIMMClass
*ndc
= NVDIMM_GET_CLASS(nvdimm
);
203 if (ndc
->unrealize
) {
204 ndc
->unrealize(nvdimm
);
209 * the caller should check the input parameters before calling
210 * label read/write functions.
212 static void nvdimm_validate_rw_label_data(NVDIMMDevice
*nvdimm
, uint64_t size
,
213 uint64_t offset
, bool is_write
)
215 assert((nvdimm
->label_size
>= size
+ offset
) && (offset
+ size
> offset
));
216 assert(!is_write
|| !nvdimm
->readonly
);
219 static void nvdimm_read_label_data(NVDIMMDevice
*nvdimm
, void *buf
,
220 uint64_t size
, uint64_t offset
)
222 nvdimm_validate_rw_label_data(nvdimm
, size
, offset
, false);
224 memcpy(buf
, nvdimm
->label_data
+ offset
, size
);
227 static void nvdimm_write_label_data(NVDIMMDevice
*nvdimm
, const void *buf
,
228 uint64_t size
, uint64_t offset
)
231 PCDIMMDevice
*dimm
= PC_DIMM(nvdimm
);
232 bool is_pmem
= object_property_get_bool(OBJECT(dimm
->hostmem
),
234 uint64_t backend_offset
;
236 nvdimm_validate_rw_label_data(nvdimm
, size
, offset
, true);
239 memcpy(nvdimm
->label_data
+ offset
, buf
, size
);
241 pmem_memcpy_persist(nvdimm
->label_data
+ offset
, buf
, size
);
244 mr
= host_memory_backend_get_memory(dimm
->hostmem
);
245 backend_offset
= memory_region_size(mr
) - nvdimm
->label_size
+ offset
;
246 memory_region_set_dirty(mr
, backend_offset
, size
);
249 static Property nvdimm_properties
[] = {
250 DEFINE_PROP_BOOL(NVDIMM_UNARMED_PROP
, NVDIMMDevice
, unarmed
, false),
251 DEFINE_PROP_END_OF_LIST(),
254 static void nvdimm_class_init(ObjectClass
*oc
, void *data
)
256 PCDIMMDeviceClass
*ddc
= PC_DIMM_CLASS(oc
);
257 MemoryDeviceClass
*mdc
= MEMORY_DEVICE_CLASS(oc
);
258 NVDIMMClass
*nvc
= NVDIMM_CLASS(oc
);
259 DeviceClass
*dc
= DEVICE_CLASS(oc
);
261 ddc
->realize
= nvdimm_realize
;
262 ddc
->unrealize
= nvdimm_unrealize
;
263 mdc
->get_memory_region
= nvdimm_md_get_memory_region
;
264 device_class_set_props(dc
, nvdimm_properties
);
266 nvc
->read_label_data
= nvdimm_read_label_data
;
267 nvc
->write_label_data
= nvdimm_write_label_data
;
268 set_bit(DEVICE_CATEGORY_STORAGE
, dc
->categories
);
271 static const TypeInfo nvdimm_info
= {
273 .parent
= TYPE_PC_DIMM
,
274 .class_size
= sizeof(NVDIMMClass
),
275 .class_init
= nvdimm_class_init
,
276 .instance_size
= sizeof(NVDIMMDevice
),
277 .instance_init
= nvdimm_init
,
278 .instance_finalize
= nvdimm_finalize
,
281 static void nvdimm_register_types(void)
283 type_register_static(&nvdimm_info
);
286 type_init(nvdimm_register_types
)