[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: Subject: [PATCH] loader: Add register setting support via cli
From: |
Sam Price |
Subject: |
Re: Subject: [PATCH] loader: Add register setting support via cli |
Date: |
Tue, 7 Jan 2025 21:28:29 -0500 |
I made the changes, and added documentation.
https://gitlab.com/thesamprice/qemu/-/compare/master...loader?from_project_id=11167699
I left it as [PREFIX]<RegNumber>
I can switch this to just RegNumber if desired.
I am still struggling with the email format sorry.
---
docs/system/generic-loader.rst | 98 ++++++++++++++++++++++++++++++++
hw/core/generic-loader.c | 46 +++++++++++----
include/hw/core/generic-loader.h | 7 +++
3 files changed, 139 insertions(+), 12 deletions(-)
diff --git a/docs/system/generic-loader.rst b/docs/system/generic-loader.rst
index 4f9fb005f1..71d4aaa097 100644
--- a/docs/system/generic-loader.rst
+++ b/docs/system/generic-loader.rst
@@ -117,4 +117,102 @@ future the internal state 'set_pc' (which exists
in the generic loader
now) should be exposed to the user so that they can choose if the PC
is set or not.
+Loading Data into Registers
+^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+The `loader` device allows the initialization of CPU registers from the command
+line. This feature is particularly useful for setting up the processor state
+before starting an executable. By configuring registers prior to execution, the
+`loader` can mimic the state that a bootloader would leave the processor in
+before transferring control to an ELF file or another executable.
+
+The syntax for loading data into registers is as follows::
+
+ -device loader,reg=<reg>,data=<data>,data-len=<data-len>
+
+**Parameters:**
+
+``<reg>``
+ The target register to set. Format must pass the following regex
+ ``[a-zA-Z]+[0-9]+``. The numeric part corresponds to the processor's GDB \
+ register index. For general-purpose registers, this is typically the
+ number in the register's name (e.g., ``r5`` translates to ``5``).
+ Special-purpose registers have specific IDs defined in their processor's
+ `gdbstub.c` file. Note that these IDs vary between processors.
+
+``<data>``
+ The value to load into the specified register. The data must not exceed 8
+ bytes in size.
+
+``<data-len>``
+ The length of the data in bytes. This parameter is mandatory when using
+ the ``data`` argument.
+
+**Examples:**
+
+Set a general-purpose register
+""""""""""""""""""""""""""""""
+
+To set register ``r5`` to ``0xc0001000`` (4 bytes) on CPU 0::
+
+ -device loader,reg=r5,data=0xc0001000,data-len=4
+
+Set a special register
+""""""""""""""""""""""
+
+To set the Program Counter (PC, register ``32``) to ``0x80000000`` on CPU 0::
+
+ -device loader,reg=pc32,data=0x80000000,data-len=4
+
+You must look in your processor's `gdbstub.c` file to special register to index
+mappings.
+
+**Special Registers:**
+
+Special registers are defined in the processor's ``gdbstub.c`` file
with numeric IDs.
+Examples from the MicroBlaze processor at one point looked like. include::
+
+ enum {
+ GDB_PC = 32 + 0,
+ GDB_MSR = 32 + 1,
+ GDB_EAR = 32 + 2,
+ GDB_ESR = 32 + 3,
+ GDB_FSR = 32 + 4,
+ GDB_BTR = 32 + 5,
+ GDB_PVR0 = 32 + 6,
+ GDB_PVR11 = 32 + 17,
+ GDB_EDR = 32 + 18,
+ GDB_SLR = 32 + 25,
+ GDB_SHR = 32 + 26,
+ };
+
+For example, to set the Machine State Register (``GDB_MSR``) on a MicroBlaze
+processor::
+
+ -device loader,reg=MSR33,data=0x00000001,data-len=4
+
+**Register Loading Notes:**
+
+1. **Processor-Specific IDs**:
+ The numeric IDs for registers vary between processors. Always refer to the
+ `gdbstub.c` file for the target processor to identify the correct register
+ mappings.
+
+2. **Pre-Execution State**:
+ This capability is ideal for initializing a simulated environment to match
+ the state expected by an ELF file. For example, you can configure stack
+ pointers, machine state registers, and program counters to prepare the
+ processor to run a bootstrapped application.
+
+3. **Validation**:
+ Register numbers are validated by the `gdb_write_register` function. Ensure
+ the specified register is supported by the target architecture.
+
+4. **Endianess**:
+ The `data` value is written using the processor's native endian format.
+
+By using the `loader` device to initialize registers, you can simulate
+realistic execution environments, enabling detailed testing and debugging
+of embedded software, including bootloaders interactions and operating
+system kernels.
diff --git a/hw/core/generic-loader.c b/hw/core/generic-loader.c
index ea8628b892..9408ecd150 100644
--- a/hw/core/generic-loader.c
+++ b/hw/core/generic-loader.c
@@ -55,6 +55,14 @@ static void generic_loader_reset(void *opaque)
}
}
+ if(s->reg.name) {
+ CPUClass *cc = CPU_GET_CLASS(s->cpu);
+ int bytes_written = cc->gdb_write_register(s->cpu, (uint8_t*)
&s->reg.value, s->reg.num);
+ if(bytes_written != s->reg.data_len) {
+ printf("Error setting register %d to value %lX expected to write %d,
but wrote %d\n", s->reg.num, s->reg.value, s->reg.data_len,
bytes_written);
+ }
+ }
+
if (s->data_len) {
assert(s->data_len <= sizeof(s->data));
dma_memory_write(s->cpu->as, s->addr, &s->data, s->data_len,
@@ -89,14 +97,12 @@ static void generic_loader_realize(DeviceState
*dev, Error **errp)
} else if (s->data_len > 8) {
error_setg(errp, "data-len cannot be greater then 8 bytes");
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");
+ }else if (s->addr) {
+ error_setg(errp, "data can not be specified when setting a "
+ "program counter");
return;
}
+ } else if (s->file || s->force_raw) {
/* The user specified a file, only set the PC if they also specified
* a CPU to use.
*/
@@ -105,17 +111,13 @@ static void generic_loader_realize(DeviceState
*dev, Error **errp)
}
} 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 == CPU_NONE) {
+ if (s->cpu_num == CPU_NONE) {
error_setg(errp, "cpu_num must be specified when setting a "
"program counter");
return;
}
s->set_pc = true;
- } else {
+ } else {
/* Did the user specify anything? */
error_setg(errp, "please include valid arguments");
return;
@@ -166,6 +168,25 @@ static void generic_loader_realize(DeviceState
*dev, Error **errp)
}
}
+ if (s->reg.name) {
+ int reg_num;
+ CPUClass *cc = CPU_GET_CLASS(s->cpu);
+ char prefix[32]; /* Read up to 32 characters prior to the register number*/
+ int scan_num = sscanf(s->reg.name, "%31[a-zA-Z]%d",prefix, ®_num);
+ if ( scan_num != 2){
+ error_setg(errp, "Unsupported register: %s, Failed to deterimine
register number", s->reg.name);
+ s->reg.name = 0x0;
+ }else if( reg_num < 0 || cc->gdb_num_core_regs < reg_num){
+ error_setg(errp, "Unsupported register: %s, register number must be
less than %d, got %d", s->reg.name, cc->gdb_num_core_regs, reg_num);
+ s->reg.name = 0x0;
+ }else{
+ s->reg.value = s->data;
+ s->reg.num = reg_num;
+ s->reg.data_len = s->data_len;
+ s->data_len = 0;
+ }
+ }
+
/* Convert the data endianness */
if (s->data_be) {
s->data = cpu_to_be64(s->data);
@@ -187,6 +208,7 @@ static Property generic_loader_props[] = {
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_STRING("reg", GenericLoaderState, reg.name),
DEFINE_PROP_END_OF_LIST(),
};
diff --git a/include/hw/core/generic-loader.h b/include/hw/core/generic-loader.h
index 19d87b39c8..ba826806d3 100644
--- a/include/hw/core/generic-loader.h
+++ b/include/hw/core/generic-loader.h
@@ -39,6 +39,13 @@ struct GenericLoaderState {
bool force_raw;
bool data_be;
bool set_pc;
+
+ struct {
+ char * name;
+ int num;
+ int data_len;
+ uint64_t value;
+ } reg;
};
#define TYPE_GENERIC_LOADER "loader"
--
2.45.2