backends/hostmem-file.c | 40 +++++++++++++++++++++++++++++++++++++++- include/exec/memory.h | 2 ++ include/exec/ram_addr.h | 3 ++- qapi/qom.json | 1 + qemu-options.hx | 6 +++++- softmmu/memory.c | 3 ++- softmmu/physmem.c | 5 +++-- 7 files changed, 54 insertions(+), 6 deletions(-)
Add an option for hostmem-file to start the memory object at an offset
into the target file. This is useful if multiple memory objects reside
inside the same target file, such as a device node.
In particular, it's useful to map guest memory directly into /dev/mem
for experimentation.
Signed-off-by: Alexander Graf <graf@amazon.com>
---
backends/hostmem-file.c | 40 +++++++++++++++++++++++++++++++++++++++-
include/exec/memory.h | 2 ++
include/exec/ram_addr.h | 3 ++-
qapi/qom.json | 1 +
qemu-options.hx | 6 +++++-
softmmu/memory.c | 3 ++-
softmmu/physmem.c | 5 +++--
7 files changed, 54 insertions(+), 6 deletions(-)
diff --git a/backends/hostmem-file.c b/backends/hostmem-file.c
index 25141283c4..38ea65bec5 100644
--- a/backends/hostmem-file.c
+++ b/backends/hostmem-file.c
@@ -27,6 +27,7 @@ struct HostMemoryBackendFile {
char *mem_path;
uint64_t align;
+ uint64_t offset;
bool discard_data;
bool is_pmem;
bool readonly;
@@ -58,7 +59,8 @@ file_backend_memory_alloc(HostMemoryBackend *backend, Error **errp)
ram_flags |= fb->is_pmem ? RAM_PMEM : 0;
memory_region_init_ram_from_file(&backend->mr, OBJECT(backend), name,
backend->size, fb->align, ram_flags,
- fb->mem_path, fb->readonly, errp);
+ fb->mem_path, fb->offset, fb->readonly,
+ errp);
g_free(name);
#endif
}
@@ -125,6 +127,36 @@ static void file_memory_backend_set_align(Object *o, Visitor *v,
fb->align = val;
}
+static void file_memory_backend_get_offset(Object *o, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
+ uint64_t val = fb->offset;
+
+ visit_type_size(v, name, &val, errp);
+}
+
+static void file_memory_backend_set_offset(Object *o, Visitor *v,
+ const char *name, void *opaque,
+ Error **errp)
+{
+ HostMemoryBackend *backend = MEMORY_BACKEND(o);
+ HostMemoryBackendFile *fb = MEMORY_BACKEND_FILE(o);
+ uint64_t val;
+
+ if (host_memory_backend_mr_inited(backend)) {
+ error_setg(errp, "cannot change property '%s' of %s", name,
+ object_get_typename(o));
+ return;
+ }
+
+ if (!visit_type_size(v, name, &val, errp)) {
+ return;
+ }
+ fb->offset = val;
+}
+
#ifdef CONFIG_LIBPMEM
static bool file_memory_backend_get_pmem(Object *o, Error **errp)
{
@@ -197,6 +229,12 @@ file_backend_class_init(ObjectClass *oc, void *data)
file_memory_backend_get_align,
file_memory_backend_set_align,
NULL, NULL);
+ object_class_property_add(oc, "offset", "int",
+ file_memory_backend_get_offset,
+ file_memory_backend_set_offset,
+ NULL, NULL);
+ object_class_property_set_description(oc, "offset",
+ "Offset into the target file (ex: 1G)");
#ifdef CONFIG_LIBPMEM
object_class_property_add_bool(oc, "pmem",
file_memory_backend_get_pmem, file_memory_backend_set_pmem);
diff --git a/include/exec/memory.h b/include/exec/memory.h
index 2e602a2fad..bd67198111 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -1318,6 +1318,7 @@ void memory_region_init_resizeable_ram(MemoryRegion *mr,
* @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM,
* RAM_NORESERVE,
* @path: the path in which to allocate the RAM.
+ * @offset: offset within the file referenced by path
* @readonly: true to open @path for reading, false for read/write.
* @errp: pointer to Error*, to store an error if it happens.
*
@@ -1331,6 +1332,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
uint64_t align,
uint32_t ram_flags,
const char *path,
+ ram_addr_t offset,
bool readonly,
Error **errp);
diff --git a/include/exec/ram_addr.h b/include/exec/ram_addr.h
index f4fb6a2111..90a8269290 100644
--- a/include/exec/ram_addr.h
+++ b/include/exec/ram_addr.h
@@ -110,6 +110,7 @@ long qemu_maxrampagesize(void);
* @ram_flags: RamBlock flags. Supported flags: RAM_SHARED, RAM_PMEM,
* RAM_NORESERVE.
* @mem_path or @fd: specify the backing file or device
+ * @offset: Offset into target file
* @readonly: true to open @path for reading, false for read/write.
* @errp: pointer to Error*, to store an error if it happens
*
@@ -119,7 +120,7 @@ long qemu_maxrampagesize(void);
*/
RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
uint32_t ram_flags, const char *mem_path,
- bool readonly, Error **errp);
+ off_t offset, bool readonly, Error **errp);
RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr,
uint32_t ram_flags, int fd, off_t offset,
bool readonly, Error **errp);
diff --git a/qapi/qom.json b/qapi/qom.json
index 30e76653ad..d4e16c57a3 100644
--- a/qapi/qom.json
+++ b/qapi/qom.json
@@ -649,6 +649,7 @@
{ 'struct': 'MemoryBackendFileProperties',
'base': 'MemoryBackendProperties',
'data': { '*align': 'size',
+ '*offset': 'size',
'*discard-data': 'bool',
'mem-path': 'str',
'*pmem': { 'type': 'bool', 'if': 'CONFIG_LIBPMEM' },
diff --git a/qemu-options.hx b/qemu-options.hx
index d59d19704b..2f12a9299e 100644
--- a/qemu-options.hx
+++ b/qemu-options.hx
@@ -4875,7 +4875,7 @@ SRST
they are specified. Note that the 'id' property must be set. These
objects are placed in the '/objects' path.
- ``-object memory-backend-file,id=id,size=size,mem-path=dir,share=on|off,discard-data=on|off,merge=on|off,dump=on|off,prealloc=on|off,host-nodes=host-nodes,policy=default|preferred|bind|interleave,align=align,readonly=on|off``
+ ``-object memory-backend-file,id=id,size=size,mem-path=dir,share=on|off,discard-data=on|off,merge=on|off,dump=on|off,prealloc=on|off,host-nodes=host-nodes,policy=default|preferred|bind|interleave,align=align,offset=offset,readonly=on|off``
Creates a memory file backend object, which can be used to back
the guest RAM with huge pages.
@@ -4945,6 +4945,10 @@ SRST
such cases, users can specify the required alignment via this
option.
+ The ``offset`` option specifies the offset into the target file
+ that the region starts at. You can use this parameter to overload
+ multiple regions into a single file.
+
The ``pmem`` option specifies whether the backing file specified
by ``mem-path`` is in host persistent memory that can be
accessed using the SNIA NVM programming model (e.g. Intel
diff --git a/softmmu/memory.c b/softmmu/memory.c
index 9d64efca26..c9bdebcdf4 100644
--- a/softmmu/memory.c
+++ b/softmmu/memory.c
@@ -1601,6 +1601,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
uint64_t align,
uint32_t ram_flags,
const char *path,
+ ram_addr_t offset,
bool readonly,
Error **errp)
{
@@ -1612,7 +1613,7 @@ void memory_region_init_ram_from_file(MemoryRegion *mr,
mr->destructor = memory_region_destructor_ram;
mr->align = align;
mr->ram_block = qemu_ram_alloc_from_file(size, mr, ram_flags, path,
- readonly, &err);
+ offset, readonly, &err);
if (err) {
mr->size = int128_zero();
object_unparent(OBJECT(mr));
diff --git a/softmmu/physmem.c b/softmmu/physmem.c
index cb998cdf23..d5a6a0553f 100644
--- a/softmmu/physmem.c
+++ b/softmmu/physmem.c
@@ -2114,7 +2114,7 @@ RAMBlock *qemu_ram_alloc_from_fd(ram_addr_t size, MemoryRegion *mr,
RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
uint32_t ram_flags, const char *mem_path,
- bool readonly, Error **errp)
+ off_t offset, bool readonly, Error **errp)
{
int fd;
bool created;
@@ -2126,7 +2126,8 @@ RAMBlock *qemu_ram_alloc_from_file(ram_addr_t size, MemoryRegion *mr,
return NULL;
}
- block = qemu_ram_alloc_from_fd(size, mr, ram_flags, fd, 0, readonly, errp);
+ block = qemu_ram_alloc_from_fd(size, mr, ram_flags, fd, offset, readonly,
+ errp);
if (!block) {
if (created) {
unlink(mem_path);
--
2.38.1
Amazon Development Center Germany GmbH
Krausenstr. 38
10117 Berlin
Geschaeftsfuehrung: Christian Schlaeger, Jonathan Weiss
Eingetragen am Amtsgericht Charlottenburg unter HRB 149173 B
Sitz: Berlin
Ust-ID: DE 289 237 879
On 02.02.23 23:56, Alexander Graf wrote: > Add an option for hostmem-file to start the memory object at an offset > into the target file. This is useful if multiple memory objects reside > inside the same target file, such as a device node. > > In particular, it's useful to map guest memory directly into /dev/mem > for experimentation. > > Signed-off-by: Alexander Graf <graf@amazon.com> > --- [...] > > - block = qemu_ram_alloc_from_fd(size, mr, ram_flags, fd, 0, readonly, errp); > + block = qemu_ram_alloc_from_fd(size, mr, ram_flags, fd, offset, readonly, > + errp); IIUC, the existing offset implementation is a bit broken: (1) qemu_ram_alloc_from_fd() does not handle the offset parameter correctly: The "file_size > 0 && file_size < size" calculation does not seem to consider the offset when checking for file sizes. (2) file_ram_alloc() most probably does not handle ftruncate() correctly. Especially on a yet empty file (where we set truncate=true), that might be insufficient. (3) file_ram_alloc() and/or qemu_ram_alloc_from_fd() do not verify that the offset is properly aligned to block->page_size. -- Thanks, David / dhildenb
Alexander Graf <graf@amazon.com> writes: > Add an option for hostmem-file to start the memory object at an offset > into the target file. This is useful if multiple memory objects reside > inside the same target file, such as a device node. > > In particular, it's useful to map guest memory directly into /dev/mem > for experimentation. > > Signed-off-by: Alexander Graf <graf@amazon.com> > --- [...] > diff --git a/qapi/qom.json b/qapi/qom.json > index 30e76653ad..d4e16c57a3 100644 > --- a/qapi/qom.json > +++ b/qapi/qom.json > @@ -649,6 +649,7 @@ > { 'struct': 'MemoryBackendFileProperties', > 'base': 'MemoryBackendProperties', > 'data': { '*align': 'size', > + '*offset': 'size', > '*discard-data': 'bool', > 'mem-path': 'str', > '*pmem': { 'type': 'bool', 'if': 'CONFIG_LIBPMEM' }, Looks like you forgot to document the new member. You may want to ... > diff --git a/qemu-options.hx b/qemu-options.hx > index d59d19704b..2f12a9299e 100644 > --- a/qemu-options.hx > +++ b/qemu-options.hx > @@ -4875,7 +4875,7 @@ SRST > they are specified. Note that the 'id' property must be set. These > objects are placed in the '/objects' path. > > - ``-object memory-backend-file,id=id,size=size,mem-path=dir,share=on|off,discard-data=on|off,merge=on|off,dump=on|off,prealloc=on|off,host-nodes=host-nodes,policy=default|preferred|bind|interleave,align=align,readonly=on|off`` > + ``-object memory-backend-file,id=id,size=size,mem-path=dir,share=on|off,discard-data=on|off,merge=on|off,dump=on|off,prealloc=on|off,host-nodes=host-nodes,policy=default|preferred|bind|interleave,align=align,offset=offset,readonly=on|off`` > Creates a memory file backend object, which can be used to back > the guest RAM with huge pages. > > @@ -4945,6 +4945,10 @@ SRST > such cases, users can specify the required alignment via this > option. > > + The ``offset`` option specifies the offset into the target file > + that the region starts at. You can use this parameter to overload > + multiple regions into a single file. > + > The ``pmem`` option specifies whether the backing file specified > by ``mem-path`` is in host persistent memory that can be > accessed using the SNIA NVM programming model (e.g. Intel ... adapt the CLI documentation for it. [...]
© 2016 - 2023 Red Hat, Inc.