Add a generic loader
v13 cover Add a generic loader
This work is based on the original work by Li Guang with extra features added by Peter C and myself.
The idea of this loader is to allow the user to load multiple images or values into QEMU at startup.
Memory values can be loaded like this: -device loader,addr=0xfd1a0104,data=0x8000000e,data-len=4
Images can be loaded like this: -device loader,file=./images/u-boot.elf,cpu=0
This can be useful and we use it a lot in Xilinx to load multiple images into a machine at creation (ATF, Kernel and DTB for example).
Tested with the latest Xilinx ZynqMP machine, if I enable EL3 and EL2 I can boot ATF through to u-boot using the loader to load the images.
It can also be used to set registers.
This patch series makes the load_elf() function more generic by not requiring an architecture. It also adds new functions load_elf_as(), load_uimage_as and load_image_targphys_as which allows custom AddressSpaces when loading images.
V12:
- All patches have been reviewed
- Most patches have been merged
- The commit message of the actual device patch has been updated to justify why it is a device.
Alistair Francis (2):
generic-loader: Add a generic loader
docs: Add a generic loader explanation document
MAINTAINERS | 6 ++ docs/generic-loader.txt | 84 ++++++++++++++++ hw/core/Makefile.objs | 2 + hw/core/generic-loader.c | 211 +++++++++++++++++++++++++++++++++++++++ include/hw/core/generic-loader.h | 46 +++++++++ 5 files changed, 349 insertions(+) create mode 100644 docs/generic-loader.txt create mode 100644 hw/core/generic-loader.c create mode 100644 include/hw/core/generic-loader.h
Signature · 1 line
2.7.4
PATCH v13 1/2 generic-loader: Add a generic loader
Add a generic loader to QEMU which can be used to load images or set memory values.
Internally inside QEMU this is a device. It is a strange device that provides no hardware interface but allows QEMU to monkey patch memory specified when it is created. To be able to do this it has a reset callback that does the memory operations.
This device allows the user to monkey patch memory. To be able to do this it needs a backend to manage the datas, the same as other memory-related devices. In this case as the backend is so trivial we have merged it with the frontend instead of creating and maintaining a seperate backend.
---
V13:
- Include the commit message in the c file.
V12:
- Update commit message to justify using -device
V11:
- Small corrections
- Don't check for !data as writing a value of 0 is valid.
V10:
- Split out the PC setting and data loading
V9:
- Fix error messages
- Updated some incorrect logic
- Add address space loading support for all image types
- Explain why the reset is manually registered
V8:
- Code corrections
- Rebase
V7:
- Rebase
V6:
- Add error checking
V5:
- Rebase
V4:
- Allow the loader to work with every architecture
- Move the file to hw/core
- Increase the maximum number of CPUs
- Make the CPU operations conditional
- Convert the cpu option to cpu-num
- Require the user to specify endianess
V3:
- Pass the ram_size to load_image_targphys()
V2:
- Add maintainers entry
- Perform bounds checking
- Register and unregister the reset in the realise/unrealise
Changes since RFC:
- Add BE support
MAINTAINERS | 6 ++ hw/core/Makefile.objs | 2 + hw/core/generic-loader.c | 211 +++++++++++++++++++++++++++++++++++++++ include/hw/core/generic-loader.h | 46 +++++++++ 4 files changed, 265 insertions(+) create mode 100644 hw/core/generic-loader.c create mode 100644 include/hw/core/generic-loader.h
MAINTAINERS +6 -0
@@ -1013,6 +1013,12 @@ M: Dmitry Fleytman <[email protected]> S: Maintained F: hw/net/e1000e* +Generic Loader +M: Alistair Francis <[email protected]> +S: Maintained +F: hw/core/generic-loader.c +F: include/hw/core/generic-loader.h + Subsystems ---------- Audio
hw/core/Makefile.objs +2 -0
@@ -17,3 +17,5 @@ common-obj-$(CONFIG_SOFTMMU) += loader.o common-obj-$(CONFIG_SOFTMMU) += qdev-properties-system.o common-obj-$(CONFIG_SOFTMMU) += register.o common-obj-$(CONFIG_PLATFORM_BUS) += platform-bus.o + +obj-$(CONFIG_SOFTMMU) += generic-loader.o
hw/core/generic-loader.c new file +211 -0
211 lines not on this page · raw message
include/hw/core/generic-loader.h new file +46 -0
46 lines not on this page · raw message
Signature · 1 line
2.7.4
Re: [PATCH v13 1/2] generic-loader: Add a generic loader
Hi,
[very old patch merged as commit e481a1f63c93]
Quoted 101 lines · Alistair Francis
Add a generic loader to QEMU which can be used to load images or set memory values.
Internally inside QEMU this is a device. It is a strange device that provides no hardware interface but allows QEMU to monkey patch memory specified when it is created. To be able to do this it has a reset callback that does the memory operations.
This device allows the user to monkey patch memory. To be able to do this it needs a backend to manage the datas, the same as other memory-related devices. In this case as the backend is so trivial we have merged it with the frontend instead of creating and maintaining a seperate backend.
Signed-off-by Alistair Francis <[email protected]>Reviewed-by Peter Maydell <[email protected]>---
hw/core/generic-loader.c new file +76
-0@@ -0,0 +1,211 @@ +/* +* Generic Loader +* +* Copyright (C) 2014 Li Guang +* Copyright (C) 2016 Xilinx Inc. +* Written by Li Guang <[email protected]> +* +* 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. +* +*/ + +/* +* Internally inside QEMU this is a device. It is a strange device that +* provides no hardware interface but allows QEMU to monkey patch memory +* specified when it is created. To be able to do this it has a reset +* callback that does the memory operations. + +* This device allows the user to monkey patch memory. To be able to do +* this it needs a backend to manage the datas, the same as other +* memory-related devices. In this case as the backend is so trivial we +* have merged it with the frontend instead of creating and maintaining a +* seperate backend. +*/ + +#include "qemu/osdep.h" +#include "qom/cpu.h" +#include "hw/sysbus.h" +#include "sysemu/dma.h" +#include "hw/loader.h" +#include "qapi/error.h" +#include "hw/core/generic-loader.h" + +#define CPU_NONE 0xFFFFFFFF + +static void generic_loader_reset (void *opaque) +{ +GenericLoaderState *s =GENERIC_LOADER (opaque); + +if (s->set_pc) { +CPUClass *cc =CPU_GET_CLASS (s->cpu); +cpu_reset (s->cpu); +if (cc) { + cc->set_pc (s->cpu, s->addr); + } + } + +if (s->data_len) { +assert (s->data_len <sizeof (s->data)); +dma_memory_write (s->cpu->as, s->addr, &s->data, s->data_len); + } +} + +static void generic_loader_realize (DeviceState *dev,Error **errp) +{ +GenericLoaderState *s =GENERIC_LOADER (dev); +hwaddr entry; +int big_endian; +int size =0 ; + + s->set_pc = false; + +/* Perform some error checking on the user's options */ +if (s->data || s->data_len || s->data_be) { +/* User is loading memory values */ +if (s->file) { +error_setg (errp,"Specifying a file is not supported when loading " +"memory values" ); +return ;
hw/core/generic-loader.c line 86
Quoted 10 lines
hw/core/generic-loader.c new file +10
-0+ }else if (s->force_raw) { +error_setg (errp,"Specifying force-raw is not supported when " +"loading memory values" ); +return ; + }else if (!s->data_len) { +/* We cant' check for !data here as a value of 0 is still valid. */ +error_setg (errp,"Both data and data-len must be specified" ); +return ; + }else if (s->data_len >8 ) { +error_setg (errp,"data-len cannot be greater then 8 bytes" );
If s->data_len < 8 (like 4 or 2) ...
Quoted 67 lines
hw/core/generic-loader.c new file +67
-0+return ; + } + }else if (s->file || s->force_raw) { +/* User is loading an image */ +if (s->data || s->data_len || s->data_be) { +error_setg (errp,"data can not be specified when loading an " +"image" ); +return ; + } + s->set_pc = true; + }else if (s->addr) { +/* User is setting the PC */ +if (s->data || s->data_len || s->data_be) { +error_setg (errp,"data can not be specified when setting a " +"program counter" ); +return ; + }else if (!s->cpu_num) { +error_setg (errp,"cpu_num must be specified when setting a " +"program counter" ); +return ; + } + s->set_pc = true; + }else { +/* Did the user specify anything? */ +error_setg (errp,"please include valid arguments" ); +return ; + } + +qemu_register_reset (generic_loader_reset, dev); + +if (s->cpu_num !=CPU_NONE ) { + s->cpu =qemu_get_cpu (s->cpu_num); +if (!s->cpu) { +error_setg (errp,"Specified boot CPU#%d is nonexistent" , + s->cpu_num); +return ; + } + }else { + s->cpu = first_cpu; + } + +#ifdef TARGET_WORDS_BIGENDIAN + big_endian =1 ; +#else + big_endian =0 ; +#endif + +if (s->file) { +if (!s->force_raw) { + size =load_elf_as (s->file,NULL ,NULL , &entry,NULL ,NULL , + big_endian,0 ,0 ,0 , s->cpu->as); + +if (size <0 ) { + size =load_uimage_as (s->file, &entry,NULL ,NULL ,NULL ,NULL , + s->cpu->as); + } + } + +if (size <0 || s->force_raw) { +/* Default to the maximum size being the machine's ram size */ + size =load_image_targphys_as (s->file, s->addr, ram_size, + s->cpu->as); + }else { + s->addr = entry; + } + +if (size <0 ) {
hw/core/generic-loader.c line 163
Quoted 10 lines
hw/core/generic-loader.c new file +10
-0+error_setg (errp,"Cannot load specified image %s" , s->file); +return ; + } + } + +/* Convert the data endiannes */ +if (s->data_be) { + s->data =cpu_to_be64 (s->data); + }else { + s->data =cpu_to_le64 (s->data);
... and if we swap, we ignore the data-len and swap 64-bit regardless, returning invalid data.
I.e. data=0x1122, data-len=2, once swapped we get data=0x0000.
Is that expected?
Quoted 18 lines
hw/core/generic-loader.c new file +18
-0+ } +} + +static void generic_loader_unrealize (DeviceState *dev,Error **errp) +{ +qemu_unregister_reset (generic_loader_reset, dev); +} + +static Property generic_loader_props[] = { +DEFINE_PROP_UINT64 ("addr" , GenericLoaderState, addr,0 ), +DEFINE_PROP_UINT64 ("data" , GenericLoaderState, data,0 ), +DEFINE_PROP_UINT8 ("data-len" , GenericLoaderState, data_len,0 ), +DEFINE_PROP_BOOL ("data-be" , GenericLoaderState, data_be, false), +DEFINE_PROP_UINT32 ("cpu-num" , GenericLoaderState, cpu_num,CPU_NONE ), +DEFINE_PROP_BOOL ("force-raw" , GenericLoaderState, force_raw, false), +DEFINE_PROP_STRING ("file" , GenericLoaderState, file), +DEFINE_PROP_END_OF_LIST (), +};
Re: [PATCH v13 1/2] generic-loader: Add a generic loader
Quoted 201 lines · Philippe Mathieu-Daudé
Hi,
[very old patch merged as commit
e481a1f63c93]Quoted 101 lines · Alistair Francis
Add a generic loader to QEMU which can be used to load images or set memory values.
Internally inside QEMU this is a device. It is a strange device that provides no hardware interface but allows QEMU to monkey patch memory specified when it is created. To be able to do this it has a reset callback that does the memory operations.
This device allows the user to monkey patch memory. To be able to do this it needs a backend to manage the datas, the same as other memory-related devices. In this case as the backend is so trivial we have merged it with the frontend instead of creating and maintaining a seperate backend.
Signed-off-by Alistair Francis <[email protected]>Reviewed-by Peter Maydell <[email protected]>---
hw/core/generic-loader.c new file +76
-0@@ -0,0 +1,211 @@ +/* +* Generic Loader +* +* Copyright (C) 2014 Li Guang +* Copyright (C) 2016 Xilinx Inc. +* Written by Li Guang <[email protected]> +* +* 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. +* +*/ + +/* +* Internally inside QEMU this is a device. It is a strange device that +* provides no hardware interface but allows QEMU to monkey patch memory +* specified when it is created. To be able to do this it has a reset +* callback that does the memory operations. + +* This device allows the user to monkey patch memory. To be able to do +* this it needs a backend to manage the datas, the same as other +* memory-related devices. In this case as the backend is so trivial we +* have merged it with the frontend instead of creating and maintaining a +* seperate backend. +*/ + +#include "qemu/osdep.h" +#include "qom/cpu.h" +#include "hw/sysbus.h" +#include "sysemu/dma.h" +#include "hw/loader.h" +#include "qapi/error.h" +#include "hw/core/generic-loader.h" + +#define CPU_NONE 0xFFFFFFFF + +static void generic_loader_reset (void *opaque) +{ +GenericLoaderState *s =GENERIC_LOADER (opaque); + +if (s->set_pc) { +CPUClass *cc =CPU_GET_CLASS (s->cpu); +cpu_reset (s->cpu); +if (cc) { + cc->set_pc (s->cpu, s->addr); + } + } + +if (s->data_len) { +assert (s->data_len <sizeof (s->data)); +dma_memory_write (s->cpu->as, s->addr, &s->data, s->data_len); + } +} + +static void generic_loader_realize (DeviceState *dev,Error **errp) +{ +GenericLoaderState *s =GENERIC_LOADER (dev); +hwaddr entry; +int big_endian; +int size =0 ; + + s->set_pc = false; + +/* Perform some error checking on the user's options */ +if (s->data || s->data_len || s->data_be) { +/* User is loading memory values */ +if (s->file) { +error_setg (errp,"Specifying a file is not supported when loading " +"memory values" ); +return ;hw/core/generic-loader.c line 86
Quoted 10 lines
hw/core/generic-loader.c new file +10
-0+ }else if (s->force_raw) { +error_setg (errp,"Specifying force-raw is not supported when " +"loading memory values" ); +return ; + }else if (!s->data_len) { +/* We cant' check for !data here as a value of 0 is still valid. */ +error_setg (errp,"Both data and data-len must be specified" ); +return ; + }else if (s->data_len >8 ) { +error_setg (errp,"data-len cannot be greater then 8 bytes" );If
s->data_len< 8 (like 4 or 2) ...Quoted 67 lines
hw/core/generic-loader.c new file +67
-0+return ; + } + }else if (s->file || s->force_raw) { +/* User is loading an image */ +if (s->data || s->data_len || s->data_be) { +error_setg (errp,"data can not be specified when loading an " +"image" ); +return ; + } + s->set_pc = true; + }else if (s->addr) { +/* User is setting the PC */ +if (s->data || s->data_len || s->data_be) { +error_setg (errp,"data can not be specified when setting a " +"program counter" ); +return ; + }else if (!s->cpu_num) { +error_setg (errp,"cpu_num must be specified when setting a " +"program counter" ); +return ; + } + s->set_pc = true; + }else { +/* Did the user specify anything? */ +error_setg (errp,"please include valid arguments" ); +return ; + } + +qemu_register_reset (generic_loader_reset, dev); + +if (s->cpu_num !=CPU_NONE ) { + s->cpu =qemu_get_cpu (s->cpu_num); +if (!s->cpu) { +error_setg (errp,"Specified boot CPU#%d is nonexistent" , + s->cpu_num); +return ; + } + }else { + s->cpu = first_cpu; + } + +#ifdef TARGET_WORDS_BIGENDIAN + big_endian =1 ; +#else + big_endian =0 ; +#endif + +if (s->file) { +if (!s->force_raw) { + size =load_elf_as (s->file,NULL ,NULL , &entry,NULL ,NULL , + big_endian,0 ,0 ,0 , s->cpu->as); + +if (size <0 ) { + size =load_uimage_as (s->file, &entry,NULL ,NULL ,NULL ,NULL , + s->cpu->as); + } + } + +if (size <0 || s->force_raw) { +/* Default to the maximum size being the machine's ram size */ + size =load_image_targphys_as (s->file, s->addr, ram_size, + s->cpu->as); + }else { + s->addr = entry; + } + +if (size <0 ) {hw/core/generic-loader.c line 163
Quoted 10 lines
hw/core/generic-loader.c new file +10
-0+error_setg (errp,"Cannot load specified image %s" , s->file); +return ; + } + } + +/* Convert the data endiannes */ +if (s->data_be) { + s->data =cpu_to_be64 (s->data); + }else { + s->data =cpu_to_le64 (s->data);... and if we swap, we ignore the data-len and swap 64-bit regardless, returning invalid data.
Quoted 3 lines
I.e. data=0x1122, data-len=2, once swapped we get data=0x0000.
Is that expected?
I don't think that's expected
Alistair
PATCH v13 2/2 docs: Add a generic loader explanation document
In reply to Alistair Francis---
V11:
- Fix corrections
V10:
- Split the data loading and PC setting
V9:
- Clarify the image loading options
V8:
- Improve documentation
V6:
- Fixup documentation
V4:
- Re-write to be more comprehensive
docs/generic-loader.txt | 84 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 docs/generic-loader.txt
docs/generic-loader.txt new file +84 -0
@@ -0,0 +1,84 @@ +Copyright (c) 2016 Xilinx 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. + + +The 'loader' device allows the user to load multiple images or values into +QEMU at startup. + +Loading Data into Memory Values +--------------------- +The loader device allows memory values to be set from the command line. This +can be done by following the syntax below: + + -device loader,addr=<addr>,data=<data>,data-len=<data-len> + [,data-be=<data-be>][,cpu-num=<cpu-num>] + + <addr> - The address to store the data in. + <data> - The value to be written to the address. The maximum size of + the data is 8 bytes. + <data-len> - The length of the data in bytes. This argument must be + included if the data argument is. + <data-be> - Set to true if the data to be stored on the guest should be + written as big endian data. The default is to write little + endian data. + <cpu-num> - The number of the CPU's address space where the data should + be loaded. If not specified the address space of the first + CPU is used. + +All values are parsed using the standard QemuOps parsing. This allows the user +to specify any values in any format supported. By default the values +will be parsed as decimal. To use hex values the user should prefix the number +with a '0x'. + +An example of loading value 0x8000000e to address 0xfd1a0104 is: + -device loader,addr=0xfd1a0104,data=0x8000000e,data-len=4 + +Setting a CPU's Program Counter +--------------------- +The loader device allows the CPU's PC to be set from the command line. This +can be done by following the syntax below: + + -device loader,addr=<addr>,cpu-num=<cpu-num> + + <addr> - The value to use as the CPU's PC. + <cpu-num> - The number of the CPU whose PC should be set to the + specified value. + +All values are parsed using the standard QemuOps parsing. This allows the user +to specify any values in any format supported. By default the values +will be parsed as decimal. To use hex values the user should prefix the number +with a '0x'. + +An example of setting CPU 0's PC to 0x8000 is: + -device loader,addr=0x8000,cpu-num=0 + +Loading Files +--------------------- +The loader device also allows files to be loaded into memory. This can be done +similarly to setting memory values. The syntax is shown below: + + -device loader,file=<file>[,addr=<addr>][,cpu-num=<cpu-num>][,force-raw=<raw>] + + <file> - A file to be loaded into memory + <addr> - The addr in memory that the file should be loaded. This is + ignored if you are using an ELF (unless force-raw is true). + This is required if you aren't loading an ELF. + <cpu-num> - This specifies the CPU that should be used. This is an + optional argument and will cause the CPU's PC to be set to + where the image is stored or in the case of an ELF file to + the value in the header. This option should only be used + for the boot image. + This will also cause the image to be written to the specified + CPU's address space. If not specified, the default is CPU 0. + <force-raw> - Forces the file to be treated as a raw image. This can be + used to specify the load address of ELF files. + +All values are parsed using the standard QemuOps parsing. This allows the user +to specify any values in any format supported. By default the values +will be parsed as decimal. To use hex values the user should prefix the number +with a '0x'. + +An example of loading an ELF file which CPU0 will boot is shown below: + -device loader,file=./images/boot.elf,cpu-num=0
Signature · 1 line
2.7.4
Peter Maydell
AppliedQuoted 30 lines · Alistair Francis
This work is based on the original work by Li Guang with extra features added by Peter C and myself.
The idea of this loader is to allow the user to load multiple images or values into QEMU at startup.
Memory values can be loaded like this: -device loader,addr=0xfd1a0104,data=0x8000000e,data-len=4
Images can be loaded like this: -device loader,file=./images/u-boot.elf,cpu=0
This can be useful and we use it a lot in Xilinx to load multiple images into a machine at creation (ATF, Kernel and DTB for example).
Tested with the latest Xilinx ZynqMP machine, if I enable EL3 and EL2 I can boot ATF through to u-boot using the loader to load the images.
It can also be used to set registers.
This patch series makes the
load_elf()function more generic by not requiring an architecture. It also adds new functionsload_elf_as(),load_uimage_asandload_image_targphys_aswhich allows custom AddressSpaces when loading images.V12:
- All patches have been reviewed
- Most patches have been merged
- The commit message of the actual device patch has been updated to justify why it is a device.
Quoted 3 lines
Alistair Francis (2):
generic-loader: Add a generic loader
docs: Add a generic loader explanation document
Applied to target-arm.next, thanks.
-- PMM