... | ... | ||
---|---|---|---|
19 | to using upstream QEMU for these tasks which have historically | 19 | to using upstream QEMU for these tasks which have historically |
20 | required maintaining a QEMU fork downstream (like QEMUAFL | 20 | required maintaining a QEMU fork downstream (like QEMUAFL |
21 | https://github.com/AFLplusplus/qemuafl), which is tedious, error | 21 | https://github.com/AFLplusplus/qemuafl), which is tedious, error |
22 | prone, and results in users missing out on enhancements to QEMU. | 22 | prone, and results in users missing out on enhancements to QEMU. |
23 | 23 | ||
24 | novafacing (2): | 24 | A test is provided, compile: |
25 | |||
26 | gcc -o tests/tcg/x86_64/inject-target tests/tcg/x86_64/inject-target.c | ||
27 | |||
28 | And run: | ||
29 | |||
30 | ./build/qemu-x86_64 -d plugin --plugin build/tests/tcg/plugins/libinject.so tests/tcg/x86_64/inject-target | ||
31 | |||
32 | Hopefully after a number of tries, the inject plugin will inject the right | ||
33 | value into the target program, leading to a victory message. This plugin | ||
34 | handles simple "hypercalls", only one of which is implemented and injects | ||
35 | data into guest memory. | ||
36 | |||
37 | novafacing (3): | ||
25 | Expose gdb_write_register function to consumers of gdbstub | 38 | Expose gdb_write_register function to consumers of gdbstub |
26 | Add plugin API functions for register R/W, hwaddr R/W, vaddr W | 39 | Add plugin API functions for register R/W, hwaddr R/W, vaddr W |
40 | Add inject plugin and x86_64 target for the inject plugin | ||
27 | 41 | ||
28 | gdbstub/gdbstub.c | 2 +- | 42 | gdbstub/gdbstub.c | 2 +- |
29 | include/exec/gdbstub.h | 14 +++++ | 43 | include/exec/gdbstub.h | 14 +++ |
30 | include/qemu/qemu-plugin.h | 116 +++++++++++++++++++++++++++++++++---- | 44 | include/qemu/qemu-plugin.h | 116 +++++++++++++++-- |
31 | plugins/api.c | 66 ++++++++++++++++++++- | 45 | plugins/api.c | 66 +++++++++- |
32 | 4 files changed, 183 insertions(+), 15 deletions(-) | 46 | tests/tcg/plugins/inject.c | 206 +++++++++++++++++++++++++++++++ |
47 | tests/tcg/plugins/meson.build | 2 +- | ||
48 | tests/tcg/x86_64/Makefile.target | 1 + | ||
49 | tests/tcg/x86_64/inject-target.c | 27 ++++ | ||
50 | 8 files changed, 418 insertions(+), 16 deletions(-) | ||
51 | create mode 100644 tests/tcg/plugins/inject.c | ||
52 | create mode 100644 tests/tcg/x86_64/inject-target.c | ||
33 | 53 | ||
34 | -- | 54 | -- |
35 | 2.46.1 | 55 | 2.46.1 | diff view generated by jsdifflib |
1 | From: novafacing <rowanbhart@gmail.com> | 1 | From: novafacing <rowanbhart@gmail.com> |
---|---|---|---|
2 | 2 | ||
3 | --- | 3 | --- |
4 | gdbstub/gdbstub.c | 2 +- | 4 | gdbstub/gdbstub.c | 2 +- |
5 | include/exec/gdbstub.h | 14 ++++++++++++++ | 5 | include/exec/gdbstub.h | 14 ++++++++++++++ |
6 | 2 files changed, 15 insertions(+), 1 deletion(-) | 6 | 2 files changed, 15 insertions(+), 1 deletion(-) |
7 | 7 | ||
8 | diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c | 8 | diff --git a/gdbstub/gdbstub.c b/gdbstub/gdbstub.c |
9 | index XXXXXXX..XXXXXXX 100644 | 9 | index XXXXXXX..XXXXXXX 100644 |
10 | --- a/gdbstub/gdbstub.c | 10 | --- a/gdbstub/gdbstub.c |
11 | +++ b/gdbstub/gdbstub.c | 11 | +++ b/gdbstub/gdbstub.c |
12 | @@ -XXX,XX +XXX,XX @@ int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) | 12 | @@ -XXX,XX +XXX,XX @@ int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg) |
13 | return 0; | 13 | return 0; |
14 | } | 14 | } |
15 | 15 | ||
16 | -static int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) | 16 | -static int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) |
17 | +int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) | 17 | +int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg) |
18 | { | 18 | { |
19 | CPUClass *cc = CPU_GET_CLASS(cpu); | 19 | CPUClass *cc = CPU_GET_CLASS(cpu); |
20 | GDBRegisterState *r; | 20 | GDBRegisterState *r; |
21 | diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h | 21 | diff --git a/include/exec/gdbstub.h b/include/exec/gdbstub.h |
22 | index XXXXXXX..XXXXXXX 100644 | 22 | index XXXXXXX..XXXXXXX 100644 |
23 | --- a/include/exec/gdbstub.h | 23 | --- a/include/exec/gdbstub.h |
24 | +++ b/include/exec/gdbstub.h | 24 | +++ b/include/exec/gdbstub.h |
25 | @@ -XXX,XX +XXX,XX @@ const GDBFeature *gdb_find_static_feature(const char *xmlname); | 25 | @@ -XXX,XX +XXX,XX @@ const GDBFeature *gdb_find_static_feature(const char *xmlname); |
26 | */ | 26 | */ |
27 | int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); | 27 | int gdb_read_register(CPUState *cpu, GByteArray *buf, int reg); |
28 | 28 | ||
29 | +/** | 29 | +/** |
30 | + * gdb_write_register() - Write a register associated with a CPU. | 30 | + * gdb_write_register() - Write a register associated with a CPU. |
31 | + * @cpu: The CPU associated with the register. | 31 | + * @cpu: The CPU associated with the register. |
32 | + * @buf: The buffer that the register contents will be set to. | 32 | + * @buf: The buffer that the register contents will be set to. |
33 | + * @reg: The register's number returned by gdb_find_feature_register(). | 33 | + * @reg: The register's number returned by gdb_find_feature_register(). |
34 | + * | 34 | + * |
35 | + * The size of @buf must be at least the size of the register being | 35 | + * The size of @buf must be at least the size of the register being |
36 | + * written. | 36 | + * written. |
37 | + * | 37 | + * |
38 | + * Return: The number of written bytes, or 0 if an error occurred (for | 38 | + * Return: The number of written bytes, or 0 if an error occurred (for |
39 | + * example, an unknown register was provided). | 39 | + * example, an unknown register was provided). |
40 | + */ | 40 | + */ |
41 | +int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg); | 41 | +int gdb_write_register(CPUState *cpu, uint8_t *mem_buf, int reg); |
42 | + | 42 | + |
43 | /** | 43 | /** |
44 | * typedef GDBRegDesc - a register description from gdbstub | 44 | * typedef GDBRegDesc - a register description from gdbstub |
45 | */ | 45 | */ |
46 | -- | 46 | -- |
47 | 2.46.1 | 47 | 2.46.1 | diff view generated by jsdifflib |
1 | From: novafacing <rowanbhart@gmail.com> | 1 | From: novafacing <rowanbhart@gmail.com> |
---|---|---|---|
2 | 2 | ||
3 | --- | 3 | --- |
4 | include/qemu/qemu-plugin.h | 116 +++++++++++++++++++++++++++++++++---- | 4 | include/qemu/qemu-plugin.h | 116 +++++++++++++++++++++++++++++++++---- |
5 | plugins/api.c | 66 ++++++++++++++++++++- | 5 | plugins/api.c | 66 ++++++++++++++++++++- |
6 | 2 files changed, 168 insertions(+), 14 deletions(-) | 6 | 2 files changed, 168 insertions(+), 14 deletions(-) |
7 | 7 | ||
8 | diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h | 8 | diff --git a/include/qemu/qemu-plugin.h b/include/qemu/qemu-plugin.h |
9 | index XXXXXXX..XXXXXXX 100644 | 9 | index XXXXXXX..XXXXXXX 100644 |
10 | --- a/include/qemu/qemu-plugin.h | 10 | --- a/include/qemu/qemu-plugin.h |
11 | +++ b/include/qemu/qemu-plugin.h | 11 | +++ b/include/qemu/qemu-plugin.h |
12 | @@ -XXX,XX +XXX,XX @@ typedef uint64_t qemu_plugin_id_t; | 12 | @@ -XXX,XX +XXX,XX @@ typedef uint64_t qemu_plugin_id_t; |
13 | * | 13 | * |
14 | * version 4: | 14 | * version 4: |
15 | * - added qemu_plugin_read_memory_vaddr | 15 | * - added qemu_plugin_read_memory_vaddr |
16 | + * | 16 | + * |
17 | + * version 5: | 17 | + * version 5: |
18 | + * - added qemu_plugin_write_memory_vaddr | 18 | + * - added qemu_plugin_write_memory_vaddr |
19 | + * - added qemu_plugin_read_memory_hwaddr | 19 | + * - added qemu_plugin_read_memory_hwaddr |
20 | + * - added qemu_plugin_write_memory_hwaddr | 20 | + * - added qemu_plugin_write_memory_hwaddr |
21 | + * - added qemu_plugin_write_register | 21 | + * - added qemu_plugin_write_register |
22 | + * | 22 | + * |
23 | */ | 23 | */ |
24 | 24 | ||
25 | extern QEMU_PLUGIN_EXPORT int qemu_plugin_version; | 25 | extern QEMU_PLUGIN_EXPORT int qemu_plugin_version; |
26 | 26 | ||
27 | -#define QEMU_PLUGIN_VERSION 4 | 27 | -#define QEMU_PLUGIN_VERSION 4 |
28 | +#define QEMU_PLUGIN_VERSION 5 | 28 | +#define QEMU_PLUGIN_VERSION 5 |
29 | 29 | ||
30 | /** | 30 | /** |
31 | * struct qemu_info_t - system information for plugins | 31 | * struct qemu_info_t - system information for plugins |
32 | @@ -XXX,XX +XXX,XX @@ typedef struct { | 32 | @@ -XXX,XX +XXX,XX @@ typedef struct { |
33 | * @QEMU_PLUGIN_CB_R_REGS: callback reads the CPU's regs | 33 | * @QEMU_PLUGIN_CB_R_REGS: callback reads the CPU's regs |
34 | * @QEMU_PLUGIN_CB_RW_REGS: callback reads and writes the CPU's regs | 34 | * @QEMU_PLUGIN_CB_RW_REGS: callback reads and writes the CPU's regs |
35 | * | 35 | * |
36 | - * Note: currently QEMU_PLUGIN_CB_RW_REGS is unused, plugins cannot change | 36 | - * Note: currently QEMU_PLUGIN_CB_RW_REGS is unused, plugins cannot change |
37 | - * system register state. | 37 | - * system register state. |
38 | */ | 38 | */ |
39 | enum qemu_plugin_cb_flags { | 39 | enum qemu_plugin_cb_flags { |
40 | QEMU_PLUGIN_CB_NO_REGS, | 40 | QEMU_PLUGIN_CB_NO_REGS, |
41 | @@ -XXX,XX +XXX,XX @@ typedef struct { | 41 | @@ -XXX,XX +XXX,XX @@ typedef struct { |
42 | QEMU_PLUGIN_API | 42 | QEMU_PLUGIN_API |
43 | GArray *qemu_plugin_get_registers(void); | 43 | GArray *qemu_plugin_get_registers(void); |
44 | 44 | ||
45 | +/** | 45 | +/** |
46 | + * qemu_plugin_read_register() - read register for current vCPU | 46 | + * qemu_plugin_read_register() - read register for current vCPU |
47 | + * | 47 | + * |
48 | + * @handle: a @qemu_plugin_reg_handle handle | 48 | + * @handle: a @qemu_plugin_reg_handle handle |
49 | + * @buf: A GByteArray for the data owned by the plugin | 49 | + * @buf: A GByteArray for the data owned by the plugin |
50 | + * | 50 | + * |
51 | + * This function is only available in a context that register read access is | 51 | + * This function is only available in a context that register read access is |
52 | + * explicitly requested via the QEMU_PLUGIN_CB_R_REGS flag. | 52 | + * explicitly requested via the QEMU_PLUGIN_CB_R_REGS flag. |
53 | + * | 53 | + * |
54 | + * Returns the size of the read register. The content of @buf is in target byte | 54 | + * Returns the size of the read register. The content of @buf is in target byte |
55 | + * order. On failure returns -1. | 55 | + * order. On failure returns -1. |
56 | + */ | 56 | + */ |
57 | +QEMU_PLUGIN_API | 57 | +QEMU_PLUGIN_API |
58 | +int qemu_plugin_read_register(struct qemu_plugin_register *handle, | 58 | +int qemu_plugin_read_register(struct qemu_plugin_register *handle, |
59 | + GByteArray *buf); | 59 | + GByteArray *buf); |
60 | + | 60 | + |
61 | +/** | 61 | +/** |
62 | + * qemu_plugin_write_register() - write register for current vCPU | 62 | + * qemu_plugin_write_register() - write register for current vCPU |
63 | + * | 63 | + * |
64 | + * @handle: a @qemu_plugin_reg_handle handle | 64 | + * @handle: a @qemu_plugin_reg_handle handle |
65 | + * @buf: A GByteArray for the data owned by the plugin | 65 | + * @buf: A GByteArray for the data owned by the plugin |
66 | + * | 66 | + * |
67 | + * This function is only available in a context that register write access is | 67 | + * This function is only available in a context that register write access is |
68 | + * explicitly requested via the QEMU_PLUGIN_CB_W_REGS flag. | 68 | + * explicitly requested via the QEMU_PLUGIN_CB_W_REGS flag. |
69 | + * | 69 | + * |
70 | + * The size of @buf must be at least the size of the requested register. | 70 | + * The size of @buf must be at least the size of the requested register. |
71 | + * Attempting to write a register with @buf smaller than the register size | 71 | + * Attempting to write a register with @buf smaller than the register size |
72 | + * will result in a crash or other undesired behavior. | 72 | + * will result in a crash or other undesired behavior. |
73 | + * | 73 | + * |
74 | + * Returns the number of bytes written. On failure returns 0. | 74 | + * Returns the number of bytes written. On failure returns 0. |
75 | + */ | 75 | + */ |
76 | +QEMU_PLUGIN_API | 76 | +QEMU_PLUGIN_API |
77 | +int qemu_plugin_write_register(struct qemu_plugin_register *handle, | 77 | +int qemu_plugin_write_register(struct qemu_plugin_register *handle, |
78 | + GByteArray *buf); | 78 | + GByteArray *buf); |
79 | + | 79 | + |
80 | /** | 80 | /** |
81 | * qemu_plugin_read_memory_vaddr() - read from memory using a virtual address | 81 | * qemu_plugin_read_memory_vaddr() - read from memory using a virtual address |
82 | * | 82 | * |
83 | @@ -XXX,XX +XXX,XX @@ bool qemu_plugin_read_memory_vaddr(uint64_t addr, | 83 | @@ -XXX,XX +XXX,XX @@ bool qemu_plugin_read_memory_vaddr(uint64_t addr, |
84 | GByteArray *data, size_t len); | 84 | GByteArray *data, size_t len); |
85 | 85 | ||
86 | /** | 86 | /** |
87 | - * qemu_plugin_read_register() - read register for current vCPU | 87 | - * qemu_plugin_read_register() - read register for current vCPU |
88 | + * qemu_plugin_write_memory_vaddr() - write to memory using a virtual address | 88 | + * qemu_plugin_write_memory_vaddr() - write to memory using a virtual address |
89 | * | 89 | * |
90 | - * @handle: a @qemu_plugin_reg_handle handle | 90 | - * @handle: a @qemu_plugin_reg_handle handle |
91 | - * @buf: A GByteArray for the data owned by the plugin | 91 | - * @buf: A GByteArray for the data owned by the plugin |
92 | + * @addr: A virtual address to write to | 92 | + * @addr: A virtual address to write to |
93 | + * @data: A byte array containing the data to write | 93 | + * @data: A byte array containing the data to write |
94 | * | 94 | * |
95 | - * This function is only available in a context that register read access is | 95 | - * This function is only available in a context that register read access is |
96 | - * explicitly requested via the QEMU_PLUGIN_CB_R_REGS flag. | 96 | - * explicitly requested via the QEMU_PLUGIN_CB_R_REGS flag. |
97 | + * The contents of @data will be written to memory starting at the virtual | 97 | + * The contents of @data will be written to memory starting at the virtual |
98 | + * address @addr. | 98 | + * address @addr. |
99 | * | 99 | * |
100 | - * Returns the size of the read register. The content of @buf is in target byte | 100 | - * Returns the size of the read register. The content of @buf is in target byte |
101 | - * order. On failure returns -1. | 101 | - * order. On failure returns -1. |
102 | + * This function does not guarantee consistency of writes, nor does it ensure | 102 | + * This function does not guarantee consistency of writes, nor does it ensure |
103 | + * that pending writes are flushed either before or after the write takes | 103 | + * that pending writes are flushed either before or after the write takes |
104 | + * place, so callers should take care when calling this function in plugin | 104 | + * place, so callers should take care when calling this function in plugin |
105 | + * callbacks to avoid depending on the existence of data written using this | 105 | + * callbacks to avoid depending on the existence of data written using this |
106 | + * function which may be overwritten afterward. | 106 | + * function which may be overwritten afterward. |
107 | + * | 107 | + * |
108 | + * Returns true on success and false on failure. | 108 | + * Returns true on success and false on failure. |
109 | */ | 109 | */ |
110 | QEMU_PLUGIN_API | 110 | QEMU_PLUGIN_API |
111 | -int qemu_plugin_read_register(struct qemu_plugin_register *handle, | 111 | -int qemu_plugin_read_register(struct qemu_plugin_register *handle, |
112 | - GByteArray *buf); | 112 | - GByteArray *buf); |
113 | +bool qemu_plugin_write_memory_vaddr(uint64_t addr, | 113 | +bool qemu_plugin_write_memory_vaddr(uint64_t addr, |
114 | + GByteArray *data); | 114 | + GByteArray *data); |
115 | + | 115 | + |
116 | +/** | 116 | +/** |
117 | + * qemu_plugin_read_memory_vaddr() - read from memory using a hardware address | 117 | + * qemu_plugin_read_memory_vaddr() - read from memory using a hardware address |
118 | + * | 118 | + * |
119 | + * @addr: A virtual address to read from | 119 | + * @addr: A virtual address to read from |
120 | + * @data: A byte array to store data into | 120 | + * @data: A byte array to store data into |
121 | + * @len: The number of bytes to read, starting from @addr | 121 | + * @len: The number of bytes to read, starting from @addr |
122 | + * | 122 | + * |
123 | + * @len bytes of data is read starting at @addr and stored into @data. If @data | 123 | + * @len bytes of data is read starting at @addr and stored into @data. If @data |
124 | + * is not large enough to hold @len bytes, it will be expanded to the necessary | 124 | + * is not large enough to hold @len bytes, it will be expanded to the necessary |
125 | + * size, reallocating if necessary. @len must be greater than 0. | 125 | + * size, reallocating if necessary. @len must be greater than 0. |
126 | + * | 126 | + * |
127 | + * This function does not ensure writes are flushed prior to reading, so | 127 | + * This function does not ensure writes are flushed prior to reading, so |
128 | + * callers should take care when calling this function in plugin callbacks to | 128 | + * callers should take care when calling this function in plugin callbacks to |
129 | + * avoid attempting to read data which may not yet be written and should use | 129 | + * avoid attempting to read data which may not yet be written and should use |
130 | + * the memory callback API instead. | 130 | + * the memory callback API instead. |
131 | + * | 131 | + * |
132 | + * This function is only valid for softmmu targets. | 132 | + * This function is only valid for softmmu targets. |
133 | + * | 133 | + * |
134 | + * Returns true on success and false on failure. | 134 | + * Returns true on success and false on failure. |
135 | + */ | 135 | + */ |
136 | +QEMU_PLUGIN_API | 136 | +QEMU_PLUGIN_API |
137 | +bool qemu_plugin_read_memory_hwaddr(uint64_t addr, | 137 | +bool qemu_plugin_read_memory_hwaddr(uint64_t addr, |
138 | + GByteArray *data, size_t len); | 138 | + GByteArray *data, size_t len); |
139 | + | 139 | + |
140 | +/** | 140 | +/** |
141 | + * qemu_plugin_write_memory_vaddr() - write to memory using a hardware address | 141 | + * qemu_plugin_write_memory_vaddr() - write to memory using a hardware address |
142 | + * | 142 | + * |
143 | + * @addr: A virtual address to write to | 143 | + * @addr: A virtual address to write to |
144 | + * @data: A byte array containing the data to write | 144 | + * @data: A byte array containing the data to write |
145 | + * | 145 | + * |
146 | + * The contents of @data will be written to memory starting at the hardware | 146 | + * The contents of @data will be written to memory starting at the hardware |
147 | + * address @addr. | 147 | + * address @addr. |
148 | + * | 148 | + * |
149 | + * This function does not guarantee consistency of writes, nor does it ensure | 149 | + * This function does not guarantee consistency of writes, nor does it ensure |
150 | + * that pending writes are flushed either before or after the write takes | 150 | + * that pending writes are flushed either before or after the write takes |
151 | + * place, so callers should take care when calling this function in plugin | 151 | + * place, so callers should take care when calling this function in plugin |
152 | + * callbacks to avoid depending on the existence of data written using this | 152 | + * callbacks to avoid depending on the existence of data written using this |
153 | + * function which may be overwritten afterward. | 153 | + * function which may be overwritten afterward. |
154 | + * | 154 | + * |
155 | + * This function is only valid for softmmu targets. | 155 | + * This function is only valid for softmmu targets. |
156 | + * | 156 | + * |
157 | + * Returns true on success and false on failure. | 157 | + * Returns true on success and false on failure. |
158 | + */ | 158 | + */ |
159 | +QEMU_PLUGIN_API | 159 | +QEMU_PLUGIN_API |
160 | +bool qemu_plugin_write_memory_hwaddr(uint64_t addr, | 160 | +bool qemu_plugin_write_memory_hwaddr(uint64_t addr, |
161 | + GByteArray *data); | 161 | + GByteArray *data); |
162 | 162 | ||
163 | /** | 163 | /** |
164 | * qemu_plugin_scoreboard_new() - alloc a new scoreboard | 164 | * qemu_plugin_scoreboard_new() - alloc a new scoreboard |
165 | diff --git a/plugins/api.c b/plugins/api.c | 165 | diff --git a/plugins/api.c b/plugins/api.c |
166 | index XXXXXXX..XXXXXXX 100644 | 166 | index XXXXXXX..XXXXXXX 100644 |
167 | --- a/plugins/api.c | 167 | --- a/plugins/api.c |
168 | +++ b/plugins/api.c | 168 | +++ b/plugins/api.c |
169 | @@ -XXX,XX +XXX,XX @@ GArray *qemu_plugin_get_registers(void) | 169 | @@ -XXX,XX +XXX,XX @@ GArray *qemu_plugin_get_registers(void) |
170 | return create_register_handles(regs); | 170 | return create_register_handles(regs); |
171 | } | 171 | } |
172 | 172 | ||
173 | +int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf) | 173 | +int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf) |
174 | +{ | 174 | +{ |
175 | + g_assert(current_cpu); | 175 | + g_assert(current_cpu); |
176 | + | 176 | + |
177 | + return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg) - 1); | 177 | + return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg) - 1); |
178 | +} | 178 | +} |
179 | + | 179 | + |
180 | +int qemu_plugin_write_register(struct qemu_plugin_register *reg, GByteArray *buf) | 180 | +int qemu_plugin_write_register(struct qemu_plugin_register *reg, GByteArray *buf) |
181 | +{ | 181 | +{ |
182 | + g_assert(current_cpu); | 182 | + g_assert(current_cpu); |
183 | + | 183 | + |
184 | + if (buf->len == 0) { | 184 | + if (buf->len == 0) { |
185 | + return 0; | 185 | + return 0; |
186 | + } | 186 | + } |
187 | + | 187 | + |
188 | + return gdb_write_register(current_cpu, buf->data, GPOINTER_TO_INT(reg) - 1); | 188 | + return gdb_write_register(current_cpu, buf->data, GPOINTER_TO_INT(reg) - 1); |
189 | +} | 189 | +} |
190 | + | 190 | + |
191 | bool qemu_plugin_read_memory_vaddr(vaddr addr, GByteArray *data, size_t len) | 191 | bool qemu_plugin_read_memory_vaddr(vaddr addr, GByteArray *data, size_t len) |
192 | { | 192 | { |
193 | g_assert(current_cpu); | 193 | g_assert(current_cpu); |
194 | @@ -XXX,XX +XXX,XX @@ bool qemu_plugin_read_memory_vaddr(vaddr addr, GByteArray *data, size_t len) | 194 | @@ -XXX,XX +XXX,XX @@ bool qemu_plugin_read_memory_vaddr(vaddr addr, GByteArray *data, size_t len) |
195 | return true; | 195 | return true; |
196 | } | 196 | } |
197 | 197 | ||
198 | -int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf) | 198 | -int qemu_plugin_read_register(struct qemu_plugin_register *reg, GByteArray *buf) |
199 | +bool qemu_plugin_write_memory_vaddr(vaddr addr, GByteArray *data) | 199 | +bool qemu_plugin_write_memory_vaddr(vaddr addr, GByteArray *data) |
200 | { | 200 | { |
201 | g_assert(current_cpu); | 201 | g_assert(current_cpu); |
202 | 202 | ||
203 | - return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg) - 1); | 203 | - return gdb_read_register(current_cpu, buf, GPOINTER_TO_INT(reg) - 1); |
204 | + if (data->len == 0) { | 204 | + if (data->len == 0) { |
205 | + return false; | 205 | + return false; |
206 | + } | 206 | + } |
207 | + | 207 | + |
208 | + int result = cpu_memory_rw_debug(current_cpu, addr, data->data, | 208 | + int result = cpu_memory_rw_debug(current_cpu, addr, data->data, |
209 | + data->len, true); | 209 | + data->len, true); |
210 | + | 210 | + |
211 | + if (result < 0) { | 211 | + if (result < 0) { |
212 | + return false; | 212 | + return false; |
213 | + } | 213 | + } |
214 | + | 214 | + |
215 | + return true; | 215 | + return true; |
216 | +} | 216 | +} |
217 | + | 217 | + |
218 | +bool qemu_plugin_read_memory_hwaddr(hwaddr addr, GByteArray *data, size_t len) | 218 | +bool qemu_plugin_read_memory_hwaddr(hwaddr addr, GByteArray *data, size_t len) |
219 | +{ | 219 | +{ |
220 | +#ifdef CONFIG_SOFTMMU | 220 | +#ifdef CONFIG_SOFTMMU |
221 | + if (len == 0) { | 221 | + if (len == 0) { |
222 | + return false; | 222 | + return false; |
223 | + } | 223 | + } |
224 | + | 224 | + |
225 | + g_byte_array_set_size(data, len); | 225 | + g_byte_array_set_size(data, len); |
226 | + | 226 | + |
227 | + cpu_physical_memory_rw(addr, data->data, data->len, false); | 227 | + cpu_physical_memory_rw(addr, data->data, data->len, false); |
228 | + | 228 | + |
229 | + return true; | 229 | + return true; |
230 | +#else | 230 | +#else |
231 | + return false; | 231 | + return false; |
232 | +#endif | 232 | +#endif |
233 | } | 233 | } |
234 | 234 | ||
235 | +bool qemu_plugin_write_memory_hwaddr(hwaddr addr, GByteArray *data) | 235 | +bool qemu_plugin_write_memory_hwaddr(hwaddr addr, GByteArray *data) |
236 | +{ | 236 | +{ |
237 | +#ifdef CONFIG_SOFTMMU | 237 | +#ifdef CONFIG_SOFTMMU |
238 | + if (data->len == 0) { | 238 | + if (data->len == 0) { |
239 | + return false; | 239 | + return false; |
240 | + } | 240 | + } |
241 | + | 241 | + |
242 | + cpu_physical_memory_rw(addr, data->data, data->len, true); | 242 | + cpu_physical_memory_rw(addr, data->data, data->len, true); |
243 | + | 243 | + |
244 | + return true; | 244 | + return true; |
245 | +#else | 245 | +#else |
246 | + return false; | 246 | + return false; |
247 | +#endif | 247 | +#endif |
248 | +} | 248 | +} |
249 | + | 249 | + |
250 | + | 250 | + |
251 | struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size) | 251 | struct qemu_plugin_scoreboard *qemu_plugin_scoreboard_new(size_t element_size) |
252 | { | 252 | { |
253 | return plugin_scoreboard_new(element_size); | 253 | return plugin_scoreboard_new(element_size); |
254 | -- | 254 | -- |
255 | 2.46.1 | 255 | 2.46.1 | diff view generated by jsdifflib |
New patch | |||
---|---|---|---|
1 | From: novafacing <rowanbhart@gmail.com> | ||
1 | 2 | ||
3 | --- | ||
4 | tests/tcg/plugins/inject.c | 206 +++++++++++++++++++++++++++++++ | ||
5 | tests/tcg/plugins/meson.build | 2 +- | ||
6 | tests/tcg/x86_64/Makefile.target | 1 + | ||
7 | tests/tcg/x86_64/inject-target.c | 27 ++++ | ||
8 | 4 files changed, 235 insertions(+), 1 deletion(-) | ||
9 | create mode 100644 tests/tcg/plugins/inject.c | ||
10 | create mode 100644 tests/tcg/x86_64/inject-target.c | ||
11 | |||
12 | diff --git a/tests/tcg/plugins/inject.c b/tests/tcg/plugins/inject.c | ||
13 | new file mode 100644 | ||
14 | index XXXXXXX..XXXXXXX | ||
15 | --- /dev/null | ||
16 | +++ b/tests/tcg/plugins/inject.c | ||
17 | @@ -XXX,XX +XXX,XX @@ | ||
18 | +/* | ||
19 | + * Copyright (C) 2024, Rowan Hart <rowanbhart@gmail.com> | ||
20 | + * | ||
21 | + * License: GNU GPL, version 2 or later. | ||
22 | + * See the COPYING file in the top-level directory. | ||
23 | + */ | ||
24 | +#include "glib.h" | ||
25 | +#include <assert.h> | ||
26 | +#include <inttypes.h> | ||
27 | +#include <stdio.h> | ||
28 | +#include <stdlib.h> | ||
29 | +#include <string.h> | ||
30 | +#include <unistd.h> | ||
31 | + | ||
32 | +#include <qemu-plugin.h> | ||
33 | + | ||
34 | +/* | ||
35 | + * Specifies a Hypercall for an architecture: | ||
36 | + * | ||
37 | + * - Architecture name | ||
38 | + * - Whether it is enabled | ||
39 | + * - The hypercall instruction | ||
40 | + * - The register names to pass the hypercall # and args | ||
41 | + */ | ||
42 | +struct HypercallSpec { | ||
43 | + const char *name; | ||
44 | + const bool enabled; | ||
45 | + const char *hypercall; | ||
46 | + const bool little_endian; | ||
47 | + const char *num_reg; | ||
48 | + const char *arg0_reg; | ||
49 | + const char *arg1_reg; | ||
50 | +}; | ||
51 | + | ||
52 | +static const struct HypercallSpec *hypercall_spec; | ||
53 | + | ||
54 | +static const struct HypercallSpec hypercall_specs[] = { | ||
55 | + { "aarch64", false, NULL, true, 0, 0, 0 }, | ||
56 | + { "aarch64_be", false, NULL, false, 0, 0, 0 }, | ||
57 | + { "alpha", false, NULL, true, 0, 0, 0 }, | ||
58 | + { "arm", false, NULL, true, 0, 0, 0 }, | ||
59 | + { "armeb", false, NULL, false, 0, 0, 0 }, | ||
60 | + { "avr", false, NULL, true, 0, 0, 0 }, | ||
61 | + { "hexagon", false, NULL, true, 0, 0, 0 }, | ||
62 | + { "hppa", false, NULL, false, 0, 0, 0 }, | ||
63 | + { "i386", false, NULL, true, 0, 0, 0 }, | ||
64 | + { "loongarch64", false, NULL, true, 0, 0, 0 }, | ||
65 | + { "m68k", false, NULL, false, 0, 0, 0 }, | ||
66 | + { "microblaze", false, NULL, false, 0, 0, 0 }, | ||
67 | + { "microblazeel", false, NULL, true, 0, 0, 0 }, | ||
68 | + { "mips", false, NULL, false, 0, 0, 0 }, | ||
69 | + { "mips64", false, NULL, false, 0, 0, 0 }, | ||
70 | + { "mips64el", false, NULL, true, 0, 0, 0 }, | ||
71 | + { "mipsel", false, NULL, true, 0, 0, 0 }, | ||
72 | + { "mipsn32", false, NULL, false, 0, 0, 0 }, | ||
73 | + { "mipsn32el", false, NULL, true, 0, 0, 0 }, | ||
74 | + { "or1k", false, NULL, false, 0, 0, 0 }, | ||
75 | + { "ppc", false, NULL, false, 0, 0, 0 }, | ||
76 | + { "ppc64", false, NULL, false, 0, 0, 0 }, | ||
77 | + { "ppc64le", false, NULL, true, 0, 0, 0 }, | ||
78 | + { "riscv32", false, NULL, true, 0, 0, 0 }, | ||
79 | + { "riscv64", false, NULL, true, 0, 0, 0 }, | ||
80 | + { "rx", false, NULL, true, 0, 0, 0 }, | ||
81 | + { "s390x", false, NULL, false, 0, 0, 0 }, | ||
82 | + { "sh4", false, NULL, true, 0, 0, 0 }, | ||
83 | + { "sh4eb", false, NULL, false, 0, 0, 0 }, | ||
84 | + { "sparc", false, NULL, false, 0, 0, 0 }, | ||
85 | + { "sparc32plus", false, NULL, false, 0, 0, 0 }, | ||
86 | + { "sparc64", false, NULL, false, 0, 0, 0 }, | ||
87 | + { "tricore", false, NULL, true, 0, 0, 0 }, | ||
88 | + { "x86_64", true, "\x0f\xa2", true, "rax", "rdi", "rsi" }, | ||
89 | + { "xtensa", false, NULL, true, 0, 0, 0 }, | ||
90 | + { "xtensaeb", false, NULL, false, 0, 0, 0 }, | ||
91 | + { NULL, false, NULL, false, 0, 0, 0 }, | ||
92 | +}; | ||
93 | + | ||
94 | +QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION; | ||
95 | + | ||
96 | +/* | ||
97 | + * Returns a handle to a register with a given name, or NULL if there is no | ||
98 | + * such register. | ||
99 | + */ | ||
100 | +static struct qemu_plugin_register *get_register(const char *name) | ||
101 | +{ | ||
102 | + GArray *registers = qemu_plugin_get_registers(); | ||
103 | + | ||
104 | + struct qemu_plugin_register *handle = NULL; | ||
105 | + | ||
106 | + qemu_plugin_reg_descriptor *reg_descriptors = | ||
107 | + (qemu_plugin_reg_descriptor *)registers->data; | ||
108 | + | ||
109 | + for (size_t i = 0; i < registers->len; i++) { | ||
110 | + if (!strcmp(reg_descriptors[i].name, name)) { | ||
111 | + handle = reg_descriptors[i].handle; | ||
112 | + } | ||
113 | + } | ||
114 | + | ||
115 | + g_array_free(registers, true); | ||
116 | + | ||
117 | + return handle; | ||
118 | +} | ||
119 | + | ||
120 | +/* | ||
121 | + * Transforms a byte array with at most 8 entries into a uint64_t | ||
122 | + * depending on the target machine's endianness. | ||
123 | + */ | ||
124 | +static uint64_t byte_array_to_uint64(GByteArray *buf) | ||
125 | +{ | ||
126 | + uint64_t value = 0; | ||
127 | + if (hypercall_spec->little_endian) { | ||
128 | + for (int i = 0; i < buf->len && i < sizeof(uint64_t); i++) { | ||
129 | + value |= ((uint64_t)buf->data[i]) << (i * 8); | ||
130 | + } | ||
131 | + } else { | ||
132 | + for (int i = 0; i < buf->len && i < sizeof(uint64_t); i++) { | ||
133 | + value |= ((uint64_t)buf->data[i]) << ((buf->len - 1 - i) * 8); | ||
134 | + } | ||
135 | + } | ||
136 | + return value; | ||
137 | +} | ||
138 | + | ||
139 | +/* | ||
140 | + * Handle a "hyperacll" instruction, which has some special meaning for this | ||
141 | + * plugin. | ||
142 | + */ | ||
143 | +static void hypercall(unsigned int vcpu_index, void *userdata) | ||
144 | +{ | ||
145 | + uint64_t num = 0, arg0 = 0, arg1 = 0; | ||
146 | + GByteArray *buf = g_byte_array_new(); | ||
147 | + qemu_plugin_read_register(get_register(hypercall_spec->num_reg), buf); | ||
148 | + num = byte_array_to_uint64(buf); | ||
149 | + | ||
150 | + g_byte_array_set_size(buf, 0); | ||
151 | + qemu_plugin_read_register(get_register(hypercall_spec->arg0_reg), buf); | ||
152 | + arg0 = byte_array_to_uint64(buf); | ||
153 | + | ||
154 | + g_byte_array_set_size(buf, 0); | ||
155 | + qemu_plugin_read_register(get_register(hypercall_spec->arg1_reg), buf); | ||
156 | + arg1 = byte_array_to_uint64(buf); | ||
157 | + | ||
158 | + switch (num) { | ||
159 | + /* | ||
160 | + * The write hypercall (#0x13371337) tells the plugin to write random bytes | ||
161 | + * of a given size into the memory of the emulated system at a particular | ||
162 | + * vaddr | ||
163 | + */ | ||
164 | + case 0x13371337: { | ||
165 | + GByteArray *data = g_byte_array_new(); | ||
166 | + g_byte_array_set_size(data, arg1); | ||
167 | + for (uint64_t i = 0; i < arg1; i++) { | ||
168 | + data->data[i] = (uint8_t)g_random_int(); | ||
169 | + } | ||
170 | + qemu_plugin_write_memory_vaddr(arg0, data); | ||
171 | + break; | ||
172 | + } | ||
173 | + default: | ||
174 | + break; | ||
175 | + } | ||
176 | + | ||
177 | + g_byte_array_free(buf, TRUE); | ||
178 | +} | ||
179 | + | ||
180 | +/* | ||
181 | + * Callback on translation of a translation block. | ||
182 | + */ | ||
183 | +static void vcpu_tb_trans(qemu_plugin_id_t id, struct qemu_plugin_tb *tb) | ||
184 | +{ | ||
185 | + for (size_t i = 0; i < qemu_plugin_tb_n_insns(tb); i++) { | ||
186 | + struct qemu_plugin_insn *insn = qemu_plugin_tb_get_insn(tb, i); | ||
187 | + GByteArray *insn_data = g_byte_array_new(); | ||
188 | + size_t insn_len = qemu_plugin_insn_size(insn); | ||
189 | + g_byte_array_set_size(insn_data, insn_len); | ||
190 | + qemu_plugin_insn_data(insn, insn_data->data, insn_data->len); | ||
191 | + if (!memcmp(insn_data->data, hypercall_spec->hypercall, insn_data->len)) { | ||
192 | + qemu_plugin_register_vcpu_insn_exec_cb(insn, hypercall, | ||
193 | + QEMU_PLUGIN_CB_R_REGS, NULL); | ||
194 | + } | ||
195 | + g_byte_array_free(insn_data, true); | ||
196 | + } | ||
197 | +} | ||
198 | + | ||
199 | + | ||
200 | +/* | ||
201 | + * Called when the plugin is installed | ||
202 | + */ | ||
203 | +QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id, | ||
204 | + const qemu_info_t *info, int argc, | ||
205 | + char **argv) | ||
206 | +{ | ||
207 | + hypercall_spec = &hypercall_specs[0]; | ||
208 | + while (hypercall_spec->name != NULL) { | ||
209 | + if (!strcmp(hypercall_spec->name, info->target_name)) { | ||
210 | + break; | ||
211 | + } | ||
212 | + hypercall_spec++; | ||
213 | + } | ||
214 | + | ||
215 | + if (hypercall_spec->name == NULL) { | ||
216 | + qemu_plugin_outs("Error: no hypercall spec."); | ||
217 | + return -1; | ||
218 | + } | ||
219 | + | ||
220 | + qemu_plugin_register_vcpu_tb_trans_cb(id, vcpu_tb_trans); | ||
221 | + | ||
222 | + return 0; | ||
223 | +} | ||
224 | diff --git a/tests/tcg/plugins/meson.build b/tests/tcg/plugins/meson.build | ||
225 | index XXXXXXX..XXXXXXX 100644 | ||
226 | --- a/tests/tcg/plugins/meson.build | ||
227 | +++ b/tests/tcg/plugins/meson.build | ||
228 | @@ -XXX,XX +XXX,XX @@ | ||
229 | t = [] | ||
230 | if get_option('plugins') | ||
231 | - foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'syscall'] | ||
232 | + foreach i : ['bb', 'empty', 'inline', 'insn', 'mem', 'syscall', 'inject'] | ||
233 | if host_os == 'windows' | ||
234 | t += shared_module(i, files(i + '.c') + '../../../contrib/plugins/win32_linker.c', | ||
235 | include_directories: '../../../include/qemu', | ||
236 | diff --git a/tests/tcg/x86_64/Makefile.target b/tests/tcg/x86_64/Makefile.target | ||
237 | index XXXXXXX..XXXXXXX 100644 | ||
238 | --- a/tests/tcg/x86_64/Makefile.target | ||
239 | +++ b/tests/tcg/x86_64/Makefile.target | ||
240 | @@ -XXX,XX +XXX,XX @@ X86_64_TESTS += adox | ||
241 | X86_64_TESTS += test-1648 | ||
242 | X86_64_TESTS += test-2175 | ||
243 | X86_64_TESTS += cross-modifying-code | ||
244 | +X86_64_TESTS += inject-target | ||
245 | TESTS=$(MULTIARCH_TESTS) $(X86_64_TESTS) test-x86_64 | ||
246 | else | ||
247 | TESTS=$(MULTIARCH_TESTS) | ||
248 | diff --git a/tests/tcg/x86_64/inject-target.c b/tests/tcg/x86_64/inject-target.c | ||
249 | new file mode 100644 | ||
250 | index XXXXXXX..XXXXXXX | ||
251 | --- /dev/null | ||
252 | +++ b/tests/tcg/x86_64/inject-target.c | ||
253 | @@ -XXX,XX +XXX,XX @@ | ||
254 | +#include <stddef.h> | ||
255 | +#include <stdint.h> | ||
256 | +#include <stdio.h> | ||
257 | + | ||
258 | +#define hypercall(num, arg0, arg1) \ | ||
259 | + unsigned int _a __attribute__((unused)) = 0; \ | ||
260 | + unsigned int _b __attribute__((unused)) = 0; \ | ||
261 | + unsigned int _c __attribute__((unused)) = 0; \ | ||
262 | + unsigned int _d __attribute__((unused)) = 0; \ | ||
263 | + __asm__ __volatile__("cpuid\n\t" \ | ||
264 | + : "=a"(_a), "=b"(_b), "=c"(_c), "=d"(_d) \ | ||
265 | + : "a"(num), "D"(arg0), "S"(arg1)); | ||
266 | + | ||
267 | +int main(void) | ||
268 | +{ | ||
269 | + uint16_t value; | ||
270 | + | ||
271 | + for (size_t i = 0; i < 1000000; i++) { | ||
272 | + hypercall(0x13371337, &value, sizeof(value)); | ||
273 | + if (value == 0x1337) { | ||
274 | + printf("Victory!\n"); | ||
275 | + return 0; | ||
276 | + } | ||
277 | + } | ||
278 | + return 1; | ||
279 | +} | ||
280 | + | ||
281 | -- | ||
282 | 2.46.1 | diff view generated by jsdifflib |