aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitlab-ci.d/containers.yml31
-rw-r--r--.gitlab-ci.yml5
-rw-r--r--gdbstub.c343
-rw-r--r--hmp-commands.hx4
-rw-r--r--hw/core/machine.c1
-rw-r--r--hw/input/pckbd.c353
-rw-r--r--hw/input/ps2.c22
-rw-r--r--hw/input/trace-events2
-rw-r--r--tests/plugin/syscall.c98
9 files changed, 590 insertions, 269 deletions
diff --git a/.gitlab-ci.d/containers.yml b/.gitlab-ci.d/containers.yml
index 765408ae27..7b7ca3790d 100644
--- a/.gitlab-ci.d/containers.yml
+++ b/.gitlab-ci.d/containers.yml
@@ -12,10 +12,9 @@
script:
- echo "TAG:$TAG"
- echo "COMMON_TAG:$COMMON_TAG"
- - docker pull "$TAG" || docker pull "$COMMON_TAG" || true
- ./tests/docker/docker.py --engine docker build
-t "qemu/$NAME" -f "tests/docker/dockerfiles/$NAME.docker"
- -r $CI_REGISTRY_IMAGE
+ -r $CI_REGISTRY/qemu-project/qemu
- docker tag "qemu/$NAME" "$TAG"
- docker push "$TAG"
after_script:
@@ -102,6 +101,34 @@ armhf-debian-cross-container:
variables:
NAME: debian-armhf-cross
+# We never want to build hexagon in the CI system and by default we
+# always want to refer to the master registry where it lives.
+hexagon-cross-container:
+ image: docker:stable
+ stage: containers
+ rules:
+ - if: '$CI_PROJECT_NAMESPACE == "qemu-project"'
+ when: never
+ - when: always
+ variables:
+ NAME: debian-hexagon-cross
+ GIT_DEPTH: 1
+ services:
+ - docker:dind
+ before_script:
+ - export TAG="$CI_REGISTRY_IMAGE/qemu/$NAME:latest"
+ - export COMMON_TAG="$CI_REGISTRY/qemu-project/qemu/qemu/$NAME:latest"
+ - docker info
+ - docker login $CI_REGISTRY -u "$CI_REGISTRY_USER" -p "$CI_REGISTRY_PASSWORD"
+ script:
+ - echo "TAG:$TAG"
+ - echo "COMMON_TAG:$COMMON_TAG"
+ - docker pull $COMMON_TAG
+ - docker tag $COMMON_TAG $TAG
+ - docker push "$TAG"
+ after_script:
+ - docker logout
+
hppa-debian-cross-container:
extends: .container_job_template
stage: containers-layer2
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index a6290d43bc..1c46392b2f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -417,10 +417,13 @@ build-user-static:
MAKE_CHECK_ARGS: check-tcg
# Because the hexagon cross-compiler takes so long to build we don't rely
-# on the CI system to build it and hence this job has no dependency
+# on the CI system to build it and hence this job has an optional dependency
# declared. The image is manually uploaded.
build-user-hexagon:
extends: .native_build_job_template
+ needs:
+ job: hexagon-cross-container
+ optional: true
variables:
IMAGE: debian-hexagon-cross
TARGETS: hexagon-linux-user
diff --git a/gdbstub.c b/gdbstub.c
index 9103ffc902..52bde5bdc9 100644
--- a/gdbstub.c
+++ b/gdbstub.c
@@ -465,6 +465,15 @@ int use_gdb_syscalls(void)
return gdb_syscall_mode == GDB_SYS_ENABLED;
}
+static bool stub_can_reverse(void)
+{
+#ifdef CONFIG_USER_ONLY
+ return false;
+#else
+ return replay_mode == REPLAY_MODE_PLAY;
+#endif
+}
+
/* Resume execution. */
static inline void gdb_continue(void)
{
@@ -1338,6 +1347,8 @@ typedef union GdbCmdVariant {
} thread_id;
} GdbCmdVariant;
+#define get_param(p, i) (&g_array_index(p, GdbCmdVariant, i))
+
static const char *cmd_next_param(const char *param, const char delimiter)
{
static const char all_delimiters[] = ",;:=";
@@ -1363,55 +1374,52 @@ static const char *cmd_next_param(const char *param, const char delimiter)
}
static int cmd_parse_params(const char *data, const char *schema,
- GdbCmdVariant *params, int *num_params)
+ GArray *params)
{
- int curr_param;
const char *curr_schema, *curr_data;
- *num_params = 0;
-
- if (!schema) {
- return 0;
- }
+ g_assert(schema);
+ g_assert(params->len == 0);
curr_schema = schema;
- curr_param = 0;
curr_data = data;
while (curr_schema[0] && curr_schema[1] && *curr_data) {
+ GdbCmdVariant this_param;
+
switch (curr_schema[0]) {
case 'l':
if (qemu_strtoul(curr_data, &curr_data, 16,
- &params[curr_param].val_ul)) {
+ &this_param.val_ul)) {
return -EINVAL;
}
- curr_param++;
curr_data = cmd_next_param(curr_data, curr_schema[1]);
+ g_array_append_val(params, this_param);
break;
case 'L':
if (qemu_strtou64(curr_data, &curr_data, 16,
- (uint64_t *)&params[curr_param].val_ull)) {
+ (uint64_t *)&this_param.val_ull)) {
return -EINVAL;
}
- curr_param++;
curr_data = cmd_next_param(curr_data, curr_schema[1]);
+ g_array_append_val(params, this_param);
break;
case 's':
- params[curr_param].data = curr_data;
- curr_param++;
+ this_param.data = curr_data;
curr_data = cmd_next_param(curr_data, curr_schema[1]);
+ g_array_append_val(params, this_param);
break;
case 'o':
- params[curr_param].opcode = *(uint8_t *)curr_data;
- curr_param++;
+ this_param.opcode = *(uint8_t *)curr_data;
curr_data = cmd_next_param(curr_data, curr_schema[1]);
+ g_array_append_val(params, this_param);
break;
case 't':
- params[curr_param].thread_id.kind =
+ this_param.thread_id.kind =
read_thread_id(curr_data, &curr_data,
- &params[curr_param].thread_id.pid,
- &params[curr_param].thread_id.tid);
- curr_param++;
+ &this_param.thread_id.pid,
+ &this_param.thread_id.tid);
curr_data = cmd_next_param(curr_data, curr_schema[1]);
+ g_array_append_val(params, this_param);
break;
case '?':
curr_data = cmd_next_param(curr_data, curr_schema[1]);
@@ -1422,16 +1430,10 @@ static int cmd_parse_params(const char *data, const char *schema,
curr_schema += 2;
}
- *num_params = curr_param;
return 0;
}
-typedef struct GdbCmdContext {
- GdbCmdVariant *params;
- int num_params;
-} GdbCmdContext;
-
-typedef void (*GdbCmdHandler)(GdbCmdContext *gdb_ctx, void *user_ctx);
+typedef void (*GdbCmdHandler)(GArray *params, void *user_ctx);
/*
* cmd_startswith -> cmd is compared using startswith
@@ -1471,8 +1473,8 @@ static inline int startswith(const char *string, const char *pattern)
static int process_string_cmd(void *user_ctx, const char *data,
const GdbCmdParseEntry *cmds, int num_cmds)
{
- int i, schema_len, max_num_params = 0;
- GdbCmdContext gdb_ctx;
+ int i;
+ g_autoptr(GArray) params = g_array_new(false, true, sizeof(GdbCmdVariant));
if (!cmds) {
return -1;
@@ -1488,24 +1490,13 @@ static int process_string_cmd(void *user_ctx, const char *data,
}
if (cmd->schema) {
- schema_len = strlen(cmd->schema);
- if (schema_len % 2) {
- return -2;
+ if (cmd_parse_params(&data[strlen(cmd->cmd)],
+ cmd->schema, params)) {
+ return -1;
}
-
- max_num_params = schema_len / 2;
- }
-
- gdb_ctx.params =
- (GdbCmdVariant *)alloca(sizeof(*gdb_ctx.params) * max_num_params);
- memset(gdb_ctx.params, 0, sizeof(*gdb_ctx.params) * max_num_params);
-
- if (cmd_parse_params(&data[strlen(cmd->cmd)], cmd->schema,
- gdb_ctx.params, &gdb_ctx.num_params)) {
- return -1;
}
- cmd->handler(&gdb_ctx, user_ctx);
+ cmd->handler(params, user_ctx);
return 0;
}
@@ -1528,18 +1519,18 @@ static void run_cmd_parser(const char *data, const GdbCmdParseEntry *cmd)
}
}
-static void handle_detach(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_detach(GArray *params, void *user_ctx)
{
GDBProcess *process;
uint32_t pid = 1;
if (gdbserver_state.multiprocess) {
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
put_packet("E22");
return;
}
- pid = gdb_ctx->params[0].val_ul;
+ pid = get_param(params, 0)->val_ul;
}
process = gdb_get_process(pid);
@@ -1562,22 +1553,22 @@ static void handle_detach(GdbCmdContext *gdb_ctx, void *user_ctx)
put_packet("OK");
}
-static void handle_thread_alive(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_thread_alive(GArray *params, void *user_ctx)
{
CPUState *cpu;
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
put_packet("E22");
return;
}
- if (gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) {
+ if (get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) {
put_packet("E22");
return;
}
- cpu = gdb_get_cpu(gdb_ctx->params[0].thread_id.pid,
- gdb_ctx->params[0].thread_id.tid);
+ cpu = gdb_get_cpu(get_param(params, 0)->thread_id.pid,
+ get_param(params, 0)->thread_id.tid);
if (!cpu) {
put_packet("E22");
return;
@@ -1586,17 +1577,17 @@ static void handle_thread_alive(GdbCmdContext *gdb_ctx, void *user_ctx)
put_packet("OK");
}
-static void handle_continue(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_continue(GArray *params, void *user_ctx)
{
- if (gdb_ctx->num_params) {
- gdb_set_cpu_pc(gdb_ctx->params[0].val_ull);
+ if (params->len) {
+ gdb_set_cpu_pc(get_param(params, 0)->val_ull);
}
gdbserver_state.signal = 0;
gdb_continue();
}
-static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_cont_with_sig(GArray *params, void *user_ctx)
{
unsigned long signal = 0;
@@ -1604,8 +1595,8 @@ static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx)
* Note: C sig;[addr] is currently unsupported and we simply
* omit the addr parameter
*/
- if (gdb_ctx->num_params) {
- signal = gdb_ctx->params[0].val_ul;
+ if (params->len) {
+ signal = get_param(params, 0)->val_ul;
}
gdbserver_state.signal = gdb_signal_to_target(signal);
@@ -1615,27 +1606,27 @@ static void handle_cont_with_sig(GdbCmdContext *gdb_ctx, void *user_ctx)
gdb_continue();
}
-static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_set_thread(GArray *params, void *user_ctx)
{
CPUState *cpu;
- if (gdb_ctx->num_params != 2) {
+ if (params->len != 2) {
put_packet("E22");
return;
}
- if (gdb_ctx->params[1].thread_id.kind == GDB_READ_THREAD_ERR) {
+ if (get_param(params, 1)->thread_id.kind == GDB_READ_THREAD_ERR) {
put_packet("E22");
return;
}
- if (gdb_ctx->params[1].thread_id.kind != GDB_ONE_THREAD) {
+ if (get_param(params, 1)->thread_id.kind != GDB_ONE_THREAD) {
put_packet("OK");
return;
}
- cpu = gdb_get_cpu(gdb_ctx->params[1].thread_id.pid,
- gdb_ctx->params[1].thread_id.tid);
+ cpu = gdb_get_cpu(get_param(params, 1)->thread_id.pid,
+ get_param(params, 1)->thread_id.tid);
if (!cpu) {
put_packet("E22");
return;
@@ -1645,7 +1636,7 @@ static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx)
* Note: This command is deprecated and modern gdb's will be using the
* vCont command instead.
*/
- switch (gdb_ctx->params[0].opcode) {
+ switch (get_param(params, 0)->opcode) {
case 'c':
gdbserver_state.c_cpu = cpu;
put_packet("OK");
@@ -1660,18 +1651,18 @@ static void handle_set_thread(GdbCmdContext *gdb_ctx, void *user_ctx)
}
}
-static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_insert_bp(GArray *params, void *user_ctx)
{
int res;
- if (gdb_ctx->num_params != 3) {
+ if (params->len != 3) {
put_packet("E22");
return;
}
- res = gdb_breakpoint_insert(gdb_ctx->params[0].val_ul,
- gdb_ctx->params[1].val_ull,
- gdb_ctx->params[2].val_ull);
+ res = gdb_breakpoint_insert(get_param(params, 0)->val_ul,
+ get_param(params, 1)->val_ull,
+ get_param(params, 2)->val_ull);
if (res >= 0) {
put_packet("OK");
return;
@@ -1683,18 +1674,18 @@ static void handle_insert_bp(GdbCmdContext *gdb_ctx, void *user_ctx)
put_packet("E22");
}
-static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_remove_bp(GArray *params, void *user_ctx)
{
int res;
- if (gdb_ctx->num_params != 3) {
+ if (params->len != 3) {
put_packet("E22");
return;
}
- res = gdb_breakpoint_remove(gdb_ctx->params[0].val_ul,
- gdb_ctx->params[1].val_ull,
- gdb_ctx->params[2].val_ull);
+ res = gdb_breakpoint_remove(get_param(params, 0)->val_ul,
+ get_param(params, 1)->val_ull,
+ get_param(params, 2)->val_ull);
if (res >= 0) {
put_packet("OK");
return;
@@ -1717,7 +1708,7 @@ static void handle_remove_bp(GdbCmdContext *gdb_ctx, void *user_ctx)
* the remote gdb to fallback to older methods.
*/
-static void handle_set_reg(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_set_reg(GArray *params, void *user_ctx)
{
int reg_size;
@@ -1726,19 +1717,19 @@ static void handle_set_reg(GdbCmdContext *gdb_ctx, void *user_ctx)
return;
}
- if (gdb_ctx->num_params != 2) {
+ if (params->len != 2) {
put_packet("E22");
return;
}
- reg_size = strlen(gdb_ctx->params[1].data) / 2;
- hextomem(gdbserver_state.mem_buf, gdb_ctx->params[1].data, reg_size);
+ reg_size = strlen(get_param(params, 1)->data) / 2;
+ hextomem(gdbserver_state.mem_buf, get_param(params, 1)->data, reg_size);
gdb_write_register(gdbserver_state.g_cpu, gdbserver_state.mem_buf->data,
- gdb_ctx->params[0].val_ull);
+ get_param(params, 0)->val_ull);
put_packet("OK");
}
-static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_get_reg(GArray *params, void *user_ctx)
{
int reg_size;
@@ -1747,14 +1738,14 @@ static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx)
return;
}
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
put_packet("E14");
return;
}
reg_size = gdb_read_register(gdbserver_state.g_cpu,
gdbserver_state.mem_buf,
- gdb_ctx->params[0].val_ull);
+ get_param(params, 0)->val_ull);
if (!reg_size) {
put_packet("E14");
return;
@@ -1766,22 +1757,24 @@ static void handle_get_reg(GdbCmdContext *gdb_ctx, void *user_ctx)
put_strbuf();
}
-static void handle_write_mem(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_write_mem(GArray *params, void *user_ctx)
{
- if (gdb_ctx->num_params != 3) {
+ if (params->len != 3) {
put_packet("E22");
return;
}
/* hextomem() reads 2*len bytes */
- if (gdb_ctx->params[1].val_ull > strlen(gdb_ctx->params[2].data) / 2) {
+ if (get_param(params, 1)->val_ull >
+ strlen(get_param(params, 2)->data) / 2) {
put_packet("E22");
return;
}
- hextomem(gdbserver_state.mem_buf, gdb_ctx->params[2].data,
- gdb_ctx->params[1].val_ull);
- if (target_memory_rw_debug(gdbserver_state.g_cpu, gdb_ctx->params[0].val_ull,
+ hextomem(gdbserver_state.mem_buf, get_param(params, 2)->data,
+ get_param(params, 1)->val_ull);
+ if (target_memory_rw_debug(gdbserver_state.g_cpu,
+ get_param(params, 0)->val_ull,
gdbserver_state.mem_buf->data,
gdbserver_state.mem_buf->len, true)) {
put_packet("E14");
@@ -1791,22 +1784,24 @@ static void handle_write_mem(GdbCmdContext *gdb_ctx, void *user_ctx)
put_packet("OK");
}
-static void handle_read_mem(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_read_mem(GArray *params, void *user_ctx)
{
- if (gdb_ctx->num_params != 2) {
+ if (params->len != 2) {
put_packet("E22");
return;
}
/* memtohex() doubles the required space */
- if (gdb_ctx->params[1].val_ull > MAX_PACKET_LENGTH / 2) {
+ if (get_param(params, 1)->val_ull > MAX_PACKET_LENGTH / 2) {
put_packet("E22");
return;
}
- g_byte_array_set_size(gdbserver_state.mem_buf, gdb_ctx->params[1].val_ull);
+ g_byte_array_set_size(gdbserver_state.mem_buf,
+ get_param(params, 1)->val_ull);
- if (target_memory_rw_debug(gdbserver_state.g_cpu, gdb_ctx->params[0].val_ull,
+ if (target_memory_rw_debug(gdbserver_state.g_cpu,
+ get_param(params, 0)->val_ull,
gdbserver_state.mem_buf->data,
gdbserver_state.mem_buf->len, false)) {
put_packet("E14");
@@ -1818,19 +1813,19 @@ static void handle_read_mem(GdbCmdContext *gdb_ctx, void *user_ctx)
put_strbuf();
}
-static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_write_all_regs(GArray *params, void *user_ctx)
{
target_ulong addr, len;
uint8_t *registers;
int reg_size;
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
return;
}
cpu_synchronize_state(gdbserver_state.g_cpu);
- len = strlen(gdb_ctx->params[0].data) / 2;
- hextomem(gdbserver_state.mem_buf, gdb_ctx->params[0].data, len);
+ len = strlen(get_param(params, 0)->data) / 2;
+ hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len);
registers = gdbserver_state.mem_buf->data;
for (addr = 0; addr < gdbserver_state.g_cpu->gdb_num_g_regs && len > 0;
addr++) {
@@ -1841,7 +1836,7 @@ static void handle_write_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx)
put_packet("OK");
}
-static void handle_read_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_read_all_regs(GArray *params, void *user_ctx)
{
target_ulong addr, len;
@@ -1859,14 +1854,14 @@ static void handle_read_all_regs(GdbCmdContext *gdb_ctx, void *user_ctx)
put_strbuf();
}
-static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_file_io(GArray *params, void *user_ctx)
{
- if (gdb_ctx->num_params >= 1 && gdbserver_state.current_syscall_cb) {
+ if (params->len >= 1 && gdbserver_state.current_syscall_cb) {
target_ulong ret, err;
- ret = (target_ulong)gdb_ctx->params[0].val_ull;
- if (gdb_ctx->num_params >= 2) {
- err = (target_ulong)gdb_ctx->params[1].val_ull;
+ ret = (target_ulong)get_param(params, 0)->val_ull;
+ if (params->len >= 2) {
+ err = (target_ulong)get_param(params, 1)->val_ull;
} else {
err = 0;
}
@@ -1874,7 +1869,7 @@ static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx)
gdbserver_state.current_syscall_cb = NULL;
}
- if (gdb_ctx->num_params >= 3 && gdb_ctx->params[2].opcode == (uint8_t)'C') {
+ if (params->len >= 3 && get_param(params, 2)->opcode == (uint8_t)'C') {
put_packet("T02");
return;
}
@@ -1882,23 +1877,23 @@ static void handle_file_io(GdbCmdContext *gdb_ctx, void *user_ctx)
gdb_continue();
}
-static void handle_step(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_step(GArray *params, void *user_ctx)
{
- if (gdb_ctx->num_params) {
- gdb_set_cpu_pc((target_ulong)gdb_ctx->params[0].val_ull);
+ if (params->len) {
+ gdb_set_cpu_pc((target_ulong)get_param(params, 0)->val_ull);
}
cpu_single_step(gdbserver_state.c_cpu, get_sstep_flags());
gdb_continue();
}
-static void handle_backward(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_backward(GArray *params, void *user_ctx)
{
- if (replay_mode != REPLAY_MODE_PLAY) {
+ if (!stub_can_reverse()) {
put_packet("E22");
}
- if (gdb_ctx->num_params == 1) {
- switch (gdb_ctx->params[0].opcode) {
+ if (params->len == 1) {
+ switch (get_param(params, 0)->opcode) {
case 's':
if (replay_reverse_step()) {
gdb_continue();
@@ -1920,20 +1915,20 @@ static void handle_backward(GdbCmdContext *gdb_ctx, void *user_ctx)
put_packet("");
}
-static void handle_v_cont_query(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_v_cont_query(GArray *params, void *user_ctx)
{
put_packet("vCont;c;C;s;S");
}
-static void handle_v_cont(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_v_cont(GArray *params, void *user_ctx)
{
int res;
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
return;
}
- res = gdb_handle_vcont(gdb_ctx->params[0].data);
+ res = gdb_handle_vcont(get_param(params, 0)->data);
if ((res == -EINVAL) || (res == -ERANGE)) {
put_packet("E22");
} else if (res) {
@@ -1941,17 +1936,17 @@ static void handle_v_cont(GdbCmdContext *gdb_ctx, void *user_ctx)
}
}
-static void handle_v_attach(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_v_attach(GArray *params, void *user_ctx)
{
GDBProcess *process;
CPUState *cpu;
g_string_assign(gdbserver_state.str_buf, "E22");
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
goto cleanup;
}
- process = gdb_get_process(gdb_ctx->params[0].val_ul);
+ process = gdb_get_process(get_param(params, 0)->val_ul);
if (!process) {
goto cleanup;
}
@@ -1972,7 +1967,7 @@ cleanup:
put_strbuf();
}
-static void handle_v_kill(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_v_kill(GArray *params, void *user_ctx)
{
/* Kill the target */
put_packet("OK");
@@ -1981,7 +1976,7 @@ static void handle_v_kill(GdbCmdContext *gdb_ctx, void *user_ctx)
exit(0);
}
-static GdbCmdParseEntry gdb_v_commands_table[] = {
+static const GdbCmdParseEntry gdb_v_commands_table[] = {
/* Order is important if has same prefix */
{
.handler = handle_v_cont_query,
@@ -2007,43 +2002,43 @@ static GdbCmdParseEntry gdb_v_commands_table[] = {
},
};
-static void handle_v_commands(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_v_commands(GArray *params, void *user_ctx)
{
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
return;
}
- if (process_string_cmd(NULL, gdb_ctx->params[0].data,
+ if (process_string_cmd(NULL, get_param(params, 0)->data,
gdb_v_commands_table,
ARRAY_SIZE(gdb_v_commands_table))) {
put_packet("");
}
}
-static void handle_query_qemu_sstepbits(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_qemu_sstepbits(GArray *params, void *user_ctx)
{
g_string_printf(gdbserver_state.str_buf, "ENABLE=%x,NOIRQ=%x,NOTIMER=%x",
SSTEP_ENABLE, SSTEP_NOIRQ, SSTEP_NOTIMER);
put_strbuf();
}
-static void handle_set_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_set_qemu_sstep(GArray *params, void *user_ctx)
{
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
return;
}
- sstep_flags = gdb_ctx->params[0].val_ul;
+ sstep_flags = get_param(params, 0)->val_ul;
put_packet("OK");
}
-static void handle_query_qemu_sstep(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_qemu_sstep(GArray *params, void *user_ctx)
{
g_string_printf(gdbserver_state.str_buf, "0x%x", sstep_flags);
put_strbuf();
}
-static void handle_query_curr_tid(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_curr_tid(GArray *params, void *user_ctx)
{
CPUState *cpu;
GDBProcess *process;
@@ -2060,7 +2055,7 @@ static void handle_query_curr_tid(GdbCmdContext *gdb_ctx, void *user_ctx)
put_strbuf();
}
-static void handle_query_threads(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_threads(GArray *params, void *user_ctx)
{
if (!gdbserver_state.query_cpu) {
put_packet("l");
@@ -2073,25 +2068,25 @@ static void handle_query_threads(GdbCmdContext *gdb_ctx, void *user_ctx)
gdbserver_state.query_cpu = gdb_next_attached_cpu(gdbserver_state.query_cpu);
}
-static void handle_query_first_threads(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_first_threads(GArray *params, void *user_ctx)
{
gdbserver_state.query_cpu = gdb_first_attached_cpu();
- handle_query_threads(gdb_ctx, user_ctx);
+ handle_query_threads(params, user_ctx);
}
-static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_thread_extra(GArray *params, void *user_ctx)
{
g_autoptr(GString) rs = g_string_new(NULL);
CPUState *cpu;
- if (!gdb_ctx->num_params ||
- gdb_ctx->params[0].thread_id.kind == GDB_READ_THREAD_ERR) {
+ if (!params->len ||
+ get_param(params, 0)->thread_id.kind == GDB_READ_THREAD_ERR) {
put_packet("E22");
return;
}
- cpu = gdb_get_cpu(gdb_ctx->params[0].thread_id.pid,
- gdb_ctx->params[0].thread_id.tid);
+ cpu = gdb_get_cpu(get_param(params, 0)->thread_id.pid,
+ get_param(params, 0)->thread_id.tid);
if (!cpu) {
return;
}
@@ -2116,7 +2111,7 @@ static void handle_query_thread_extra(GdbCmdContext *gdb_ctx, void *user_ctx)
}
#ifdef CONFIG_USER_ONLY
-static void handle_query_offsets(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_offsets(GArray *params, void *user_ctx)
{
TaskState *ts;
@@ -2131,17 +2126,17 @@ static void handle_query_offsets(GdbCmdContext *gdb_ctx, void *user_ctx)
put_strbuf();
}
#else
-static void handle_query_rcmd(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_rcmd(GArray *params, void *user_ctx)
{
const guint8 zero = 0;
int len;
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
put_packet("E22");
return;
}
- len = strlen(gdb_ctx->params[0].data);
+ len = strlen(get_param(params, 0)->data);
if (len % 2) {
put_packet("E01");
return;
@@ -2149,7 +2144,7 @@ static void handle_query_rcmd(GdbCmdContext *gdb_ctx, void *user_ctx)
g_assert(gdbserver_state.mem_buf->len == 0);
len = len / 2;
- hextomem(gdbserver_state.mem_buf, gdb_ctx->params[0].data, len);
+ hextomem(gdbserver_state.mem_buf, get_param(params, 0)->data, len);
g_byte_array_append(gdbserver_state.mem_buf, &zero, 1);
qemu_chr_be_write(gdbserver_state.mon_chr, gdbserver_state.mem_buf->data,
gdbserver_state.mem_buf->len);
@@ -2157,7 +2152,7 @@ static void handle_query_rcmd(GdbCmdContext *gdb_ctx, void *user_ctx)
}
#endif
-static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_supported(GArray *params, void *user_ctx)
{
CPUClass *cc;
@@ -2167,7 +2162,7 @@ static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
g_string_append(gdbserver_state.str_buf, ";qXfer:features:read+");
}
- if (replay_mode == REPLAY_MODE_PLAY) {
+ if (stub_can_reverse()) {
g_string_append(gdbserver_state.str_buf,
";ReverseStep+;ReverseContinue+");
}
@@ -2178,8 +2173,8 @@ static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
}
#endif
- if (gdb_ctx->num_params &&
- strstr(gdb_ctx->params[0].data, "multiprocess+")) {
+ if (params->len &&
+ strstr(get_param(params, 0)->data, "multiprocess+")) {
gdbserver_state.multiprocess = true;
}
@@ -2187,7 +2182,7 @@ static void handle_query_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
put_strbuf();
}
-static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_xfer_features(GArray *params, void *user_ctx)
{
GDBProcess *process;
CPUClass *cc;
@@ -2195,7 +2190,7 @@ static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx)
const char *xml;
const char *p;
- if (gdb_ctx->num_params < 3) {
+ if (params->len < 3) {
put_packet("E22");
return;
}
@@ -2208,15 +2203,15 @@ static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx)
}
gdb_has_xml = true;
- p = gdb_ctx->params[0].data;
+ p = get_param(params, 0)->data;
xml = get_feature_xml(p, &p, process);
if (!xml) {
put_packet("E00");
return;
}
- addr = gdb_ctx->params[1].val_ul;
- len = gdb_ctx->params[2].val_ul;
+ addr = get_param(params, 1)->val_ul;
+ len = get_param(params, 2)->val_ul;
total_len = strlen(xml);
if (addr > total_len) {
put_packet("E00");
@@ -2240,18 +2235,18 @@ static void handle_query_xfer_features(GdbCmdContext *gdb_ctx, void *user_ctx)
}
#if defined(CONFIG_USER_ONLY) && defined(CONFIG_LINUX_USER)
-static void handle_query_xfer_auxv(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_xfer_auxv(GArray *params, void *user_ctx)
{
TaskState *ts;
unsigned long offset, len, saved_auxv, auxv_len;
- if (gdb_ctx->num_params < 2) {
+ if (params->len < 2) {
put_packet("E22");
return;
}
- offset = gdb_ctx->params[0].val_ul;
- len = gdb_ctx->params[1].val_ul;
+ offset = get_param(params, 0)->val_ul;
+ len = get_param(params, 1)->val_ul;
ts = gdbserver_state.c_cpu->opaque;
saved_auxv = ts->info->saved_auxv;
auxv_len = ts->info->auxv_len;
@@ -2286,12 +2281,12 @@ static void handle_query_xfer_auxv(GdbCmdContext *gdb_ctx, void *user_ctx)
}
#endif
-static void handle_query_attached(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_attached(GArray *params, void *user_ctx)
{
put_packet(GDB_ATTACHED);
}
-static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_query_qemu_supported(GArray *params, void *user_ctx)
{
g_string_printf(gdbserver_state.str_buf, "sstepbits;sstep");
#ifndef CONFIG_USER_ONLY
@@ -2301,21 +2296,21 @@ static void handle_query_qemu_supported(GdbCmdContext *gdb_ctx, void *user_ctx)
}
#ifndef CONFIG_USER_ONLY
-static void handle_query_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx,
+static void handle_query_qemu_phy_mem_mode(GArray *params,
void *user_ctx)
{
g_string_printf(gdbserver_state.str_buf, "%d", phy_memory_mode);
put_strbuf();
}
-static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_set_qemu_phy_mem_mode(GArray *params, void *user_ctx)
{
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
put_packet("E22");
return;
}
- if (!gdb_ctx->params[0].val_ul) {
+ if (!get_param(params, 0)->val_ul) {
phy_memory_mode = 0;
} else {
phy_memory_mode = 1;
@@ -2324,7 +2319,7 @@ static void handle_set_qemu_phy_mem_mode(GdbCmdContext *gdb_ctx, void *user_ctx)
}
#endif
-static GdbCmdParseEntry gdb_gen_query_set_common_table[] = {
+static const GdbCmdParseEntry gdb_gen_query_set_common_table[] = {
/* Order is important if has same prefix */
{
.handler = handle_query_qemu_sstepbits,
@@ -2342,7 +2337,7 @@ static GdbCmdParseEntry gdb_gen_query_set_common_table[] = {
},
};
-static GdbCmdParseEntry gdb_gen_query_table[] = {
+static const GdbCmdParseEntry gdb_gen_query_table[] = {
{
.handler = handle_query_curr_tid,
.cmd = "C",
@@ -2420,7 +2415,7 @@ static GdbCmdParseEntry gdb_gen_query_table[] = {
#endif
};
-static GdbCmdParseEntry gdb_gen_set_table[] = {
+static const GdbCmdParseEntry gdb_gen_set_table[] = {
/* Order is important if has same prefix */
{
.handler = handle_set_qemu_sstep,
@@ -2438,45 +2433,45 @@ static GdbCmdParseEntry gdb_gen_set_table[] = {
#endif
};
-static void handle_gen_query(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_gen_query(GArray *params, void *user_ctx)
{
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
return;
}
- if (!process_string_cmd(NULL, gdb_ctx->params[0].data,
+ if (!process_string_cmd(NULL, get_param(params, 0)->data,
gdb_gen_query_set_common_table,
ARRAY_SIZE(gdb_gen_query_set_common_table))) {
return;
}
- if (process_string_cmd(NULL, gdb_ctx->params[0].data,
+ if (process_string_cmd(NULL, get_param(params, 0)->data,
gdb_gen_query_table,
ARRAY_SIZE(gdb_gen_query_table))) {
put_packet("");
}
}
-static void handle_gen_set(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_gen_set(GArray *params, void *user_ctx)
{
- if (!gdb_ctx->num_params) {
+ if (!params->len) {
return;
}
- if (!process_string_cmd(NULL, gdb_ctx->params[0].data,
+ if (!process_string_cmd(NULL, get_param(params, 0)->data,
gdb_gen_query_set_common_table,
ARRAY_SIZE(gdb_gen_query_set_common_table))) {
return;
}
- if (process_string_cmd(NULL, gdb_ctx->params[0].data,
+ if (process_string_cmd(NULL, get_param(params, 0)->data,
gdb_gen_set_table,
ARRAY_SIZE(gdb_gen_set_table))) {
put_packet("");
}
}
-static void handle_target_halt(GdbCmdContext *gdb_ctx, void *user_ctx)
+static void handle_target_halt(GArray *params, void *user_ctx)
{
g_string_printf(gdbserver_state.str_buf, "T%02xthread:", GDB_SIGNAL_TRAP);
gdb_append_thread_id(gdbserver_state.c_cpu, gdbserver_state.str_buf);
diff --git a/hmp-commands.hx b/hmp-commands.hx
index 146a13c896..2d21fe5ad4 100644
--- a/hmp-commands.hx
+++ b/hmp-commands.hx
@@ -1669,7 +1669,7 @@ ERST
{
.name = "replay_break",
- .args_type = "icount:i",
+ .args_type = "icount:l",
.params = "icount",
.help = "set breakpoint at the specified instruction count",
.cmd = hmp_replay_break,
@@ -1701,7 +1701,7 @@ ERST
{
.name = "replay_seek",
- .args_type = "icount:i",
+ .args_type = "icount:l",
.params = "icount",
.help = "replay execution to the specified instruction count",
.cmd = hmp_replay_seek,
diff --git a/hw/core/machine.c b/hw/core/machine.c
index 1bf0e687b9..55b9bc7817 100644
--- a/hw/core/machine.c
+++ b/hw/core/machine.c
@@ -38,6 +38,7 @@
GlobalProperty hw_compat_6_0[] = {
{ "gpex-pcihost", "allow-unmapped-accesses", "false" },
+ { "i8042", "extended-state", "false"},
};
const size_t hw_compat_6_0_len = G_N_ELEMENTS(hw_compat_6_0);
diff --git a/hw/input/pckbd.c b/hw/input/pckbd.c
index dde85ba6c6..baba62f357 100644
--- a/hw/input/pckbd.c
+++ b/hw/input/pckbd.c
@@ -23,13 +23,16 @@
*/
#include "qemu/osdep.h"
+#include "qemu/error-report.h"
#include "qemu/log.h"
+#include "qemu/timer.h"
#include "hw/isa/isa.h"
#include "migration/vmstate.h"
#include "hw/acpi/aml-build.h"
#include "hw/input/ps2.h"
#include "hw/irq.h"
#include "hw/input/i8042.h"
+#include "hw/qdev-properties.h"
#include "sysemu/reset.h"
#include "sysemu/runstate.h"
@@ -59,21 +62,6 @@
#define KBD_CCMD_RESET 0xFE /* Pulse bit 0 of the output port P2 = CPU reset. */
#define KBD_CCMD_NO_OP 0xFF /* Pulse no bits of the output port P2. */
-/* Keyboard Commands */
-#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */
-#define KBD_CMD_ECHO 0xEE
-#define KBD_CMD_GET_ID 0xF2 /* get keyboard ID */
-#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */
-#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */
-#define KBD_CMD_RESET_DISABLE 0xF5 /* reset and disable scanning */
-#define KBD_CMD_RESET_ENABLE 0xF6 /* reset and enable scanning */
-#define KBD_CMD_RESET 0xFF /* Reset */
-
-/* Keyboard Replies */
-#define KBD_REPLY_POR 0xAA /* Power on reset */
-#define KBD_REPLY_ACK 0xFA /* Command ACK */
-#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */
-
/* Status Register Bits */
#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */
#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */
@@ -106,41 +94,37 @@
*/
#define KBD_OUT_ONES 0xcc
-/* Mouse Commands */
-#define AUX_SET_SCALE11 0xE6 /* Set 1:1 scaling */
-#define AUX_SET_SCALE21 0xE7 /* Set 2:1 scaling */
-#define AUX_SET_RES 0xE8 /* Set resolution */
-#define AUX_GET_SCALE 0xE9 /* Get scaling factor */
-#define AUX_SET_STREAM 0xEA /* Set stream mode */
-#define AUX_POLL 0xEB /* Poll */
-#define AUX_RESET_WRAP 0xEC /* Reset wrap mode */
-#define AUX_SET_WRAP 0xEE /* Set wrap mode */
-#define AUX_SET_REMOTE 0xF0 /* Set remote mode */
-#define AUX_GET_TYPE 0xF2 /* Get type */
-#define AUX_SET_SAMPLE 0xF3 /* Set sample rate */
-#define AUX_ENABLE_DEV 0xF4 /* Enable aux device */
-#define AUX_DISABLE_DEV 0xF5 /* Disable aux device */
-#define AUX_SET_DEFAULT 0xF6
-#define AUX_RESET 0xFF /* Reset aux device */
-#define AUX_ACK 0xFA /* Command byte ACK. */
-
-#define MOUSE_STATUS_REMOTE 0x40
-#define MOUSE_STATUS_ENABLED 0x20
-#define MOUSE_STATUS_SCALE21 0x10
-
-#define KBD_PENDING_KBD 1
-#define KBD_PENDING_AUX 2
+#define KBD_PENDING_KBD_COMPAT 0x01
+#define KBD_PENDING_AUX_COMPAT 0x02
+#define KBD_PENDING_CTRL_KBD 0x04
+#define KBD_PENDING_CTRL_AUX 0x08
+#define KBD_PENDING_KBD KBD_MODE_DISABLE_KBD /* 0x10 */
+#define KBD_PENDING_AUX KBD_MODE_DISABLE_MOUSE /* 0x20 */
+
+#define KBD_MIGR_TIMER_PENDING 0x1
+
+#define KBD_OBSRC_KBD 0x01
+#define KBD_OBSRC_MOUSE 0x02
+#define KBD_OBSRC_CTRL 0x04
typedef struct KBDState {
uint8_t write_cmd; /* if non zero, write data to port 60 is expected */
uint8_t status;
uint8_t mode;
uint8_t outport;
+ uint32_t migration_flags;
+ uint32_t obsrc;
bool outport_present;
+ bool extended_state;
+ bool extended_state_loaded;
/* Bitmask of devices with data available. */
uint8_t pending;
+ uint8_t obdata;
+ uint8_t cbdata;
+ uint8_t pending_tmp;
void *kbd;
void *mouse;
+ QEMUTimer *throttle_timer;
qemu_irq irq_kbd;
qemu_irq irq_mouse;
@@ -148,56 +132,123 @@ typedef struct KBDState {
hwaddr mask;
} KBDState;
-/* update irq and KBD_STAT_[MOUSE_]OBF */
/* XXX: not generating the irqs if KBD_MODE_DISABLE_KBD is set may be
incorrect, but it avoids having to simulate exact delays */
-static void kbd_update_irq(KBDState *s)
+static void kbd_update_irq_lines(KBDState *s)
{
int irq_kbd_level, irq_mouse_level;
irq_kbd_level = 0;
irq_mouse_level = 0;
+
+ if (s->status & KBD_STAT_OBF) {
+ if (s->status & KBD_STAT_MOUSE_OBF) {
+ if (s->mode & KBD_MODE_MOUSE_INT) {
+ irq_mouse_level = 1;
+ }
+ } else {
+ if ((s->mode & KBD_MODE_KBD_INT) &&
+ !(s->mode & KBD_MODE_DISABLE_KBD)) {
+ irq_kbd_level = 1;
+ }
+ }
+ }
+ qemu_set_irq(s->irq_kbd, irq_kbd_level);
+ qemu_set_irq(s->irq_mouse, irq_mouse_level);
+}
+
+static void kbd_deassert_irq(KBDState *s)
+{
+ s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
+ s->outport &= ~(KBD_OUT_OBF | KBD_OUT_MOUSE_OBF);
+ kbd_update_irq_lines(s);
+}
+
+static uint8_t kbd_pending(KBDState *s)
+{
+ if (s->extended_state) {
+ return s->pending & (~s->mode | ~(KBD_PENDING_KBD | KBD_PENDING_AUX));
+ } else {
+ return s->pending;
+ }
+}
+
+/* update irq and KBD_STAT_[MOUSE_]OBF */
+static void kbd_update_irq(KBDState *s)
+{
+ uint8_t pending = kbd_pending(s);
+
s->status &= ~(KBD_STAT_OBF | KBD_STAT_MOUSE_OBF);
s->outport &= ~(KBD_OUT_OBF | KBD_OUT_MOUSE_OBF);
- if (s->pending) {
+ if (pending) {
s->status |= KBD_STAT_OBF;
s->outport |= KBD_OUT_OBF;
- /* kbd data takes priority over aux data. */
- if (s->pending == KBD_PENDING_AUX) {
+ if (pending & KBD_PENDING_CTRL_KBD) {
+ s->obsrc = KBD_OBSRC_CTRL;
+ } else if (pending & KBD_PENDING_CTRL_AUX) {
s->status |= KBD_STAT_MOUSE_OBF;
s->outport |= KBD_OUT_MOUSE_OBF;
- if (s->mode & KBD_MODE_MOUSE_INT)
- irq_mouse_level = 1;
+ s->obsrc = KBD_OBSRC_CTRL;
+ } else if (pending & KBD_PENDING_KBD) {
+ s->obsrc = KBD_OBSRC_KBD;
} else {
- if ((s->mode & KBD_MODE_KBD_INT) &&
- !(s->mode & KBD_MODE_DISABLE_KBD))
- irq_kbd_level = 1;
+ s->status |= KBD_STAT_MOUSE_OBF;
+ s->outport |= KBD_OUT_MOUSE_OBF;
+ s->obsrc = KBD_OBSRC_MOUSE;
}
}
- qemu_set_irq(s->irq_kbd, irq_kbd_level);
- qemu_set_irq(s->irq_mouse, irq_mouse_level);
+ kbd_update_irq_lines(s);
+}
+
+static void kbd_safe_update_irq(KBDState *s)
+{
+ /*
+ * with KBD_STAT_OBF set, a call to kbd_read_data() will eventually call
+ * kbd_update_irq()
+ */
+ if (s->status & KBD_STAT_OBF) {
+ return;
+ }
+ /* the throttle timer is pending and will call kbd_update_irq() */
+ if (s->throttle_timer && timer_pending(s->throttle_timer)) {
+ return;
+ }
+ if (kbd_pending(s)) {
+ kbd_update_irq(s);
+ }
}
static void kbd_update_kbd_irq(void *opaque, int level)
{
- KBDState *s = (KBDState *)opaque;
+ KBDState *s = opaque;
- if (level)
+ if (level) {
s->pending |= KBD_PENDING_KBD;
- else
+ } else {
s->pending &= ~KBD_PENDING_KBD;
- kbd_update_irq(s);
+ }
+ kbd_safe_update_irq(s);
}
static void kbd_update_aux_irq(void *opaque, int level)
{
- KBDState *s = (KBDState *)opaque;
+ KBDState *s = opaque;
- if (level)
+ if (level) {
s->pending |= KBD_PENDING_AUX;
- else
+ } else {
s->pending &= ~KBD_PENDING_AUX;
- kbd_update_irq(s);
+ }
+ kbd_safe_update_irq(s);
+}
+
+static void kbd_throttle_timeout(void *opaque)
+{
+ KBDState *s = opaque;
+
+ if (kbd_pending(s)) {
+ kbd_update_irq(s);
+ }
}
static uint64_t kbd_read_status(void *opaque, hwaddr addr,
@@ -212,10 +263,25 @@ static uint64_t kbd_read_status(void *opaque, hwaddr addr,
static void kbd_queue(KBDState *s, int b, int aux)
{
- if (aux)
- ps2_queue(s->mouse, b);
- else
- ps2_queue(s->kbd, b);
+ if (s->extended_state) {
+ s->cbdata = b;
+ s->pending &= ~KBD_PENDING_CTRL_KBD & ~KBD_PENDING_CTRL_AUX;
+ s->pending |= aux ? KBD_PENDING_CTRL_AUX : KBD_PENDING_CTRL_KBD;
+ kbd_safe_update_irq(s);
+ } else {
+ ps2_queue(aux ? s->mouse : s->kbd, b);
+ }
+}
+
+static uint8_t kbd_dequeue(KBDState *s)
+{
+ uint8_t b = s->cbdata;
+
+ s->pending &= ~KBD_PENDING_CTRL_KBD & ~KBD_PENDING_CTRL_AUX;
+ if (kbd_pending(s)) {
+ kbd_update_irq(s);
+ }
+ return b;
}
static void outport_write(KBDState *s, uint32_t val)
@@ -265,6 +331,7 @@ static void kbd_write_command(void *opaque, hwaddr addr,
break;
case KBD_CCMD_MOUSE_ENABLE:
s->mode &= ~KBD_MODE_DISABLE_MOUSE;
+ kbd_safe_update_irq(s);
break;
case KBD_CCMD_TEST_MOUSE:
kbd_queue(s, 0x00, 0);
@@ -278,11 +345,10 @@ static void kbd_write_command(void *opaque, hwaddr addr,
break;
case KBD_CCMD_KBD_DISABLE:
s->mode |= KBD_MODE_DISABLE_KBD;
- kbd_update_irq(s);
break;
case KBD_CCMD_KBD_ENABLE:
s->mode &= ~KBD_MODE_DISABLE_KBD;
- kbd_update_irq(s);
+ kbd_safe_update_irq(s);
break;
case KBD_CCMD_READ_INPORT:
kbd_queue(s, 0x80, 0);
@@ -315,15 +381,24 @@ static uint64_t kbd_read_data(void *opaque, hwaddr addr,
unsigned size)
{
KBDState *s = opaque;
- uint32_t val;
- if (s->pending == KBD_PENDING_AUX)
- val = ps2_read_data(s->mouse);
- else
- val = ps2_read_data(s->kbd);
+ if (s->status & KBD_STAT_OBF) {
+ kbd_deassert_irq(s);
+ if (s->obsrc & KBD_OBSRC_KBD) {
+ if (s->throttle_timer) {
+ timer_mod(s->throttle_timer,
+ qemu_clock_get_us(QEMU_CLOCK_VIRTUAL) + 1000);
+ }
+ s->obdata = ps2_read_data(s->kbd);
+ } else if (s->obsrc & KBD_OBSRC_MOUSE) {
+ s->obdata = ps2_read_data(s->mouse);
+ } else if (s->obsrc & KBD_OBSRC_CTRL) {
+ s->obdata = kbd_dequeue(s);
+ }
+ }
- trace_pckbd_kbd_read_data(val);
- return val;
+ trace_pckbd_kbd_read_data(s->obdata);
+ return s->obdata;
}
static void kbd_write_data(void *opaque, hwaddr addr,
@@ -336,12 +411,23 @@ static void kbd_write_data(void *opaque, hwaddr addr,
switch(s->write_cmd) {
case 0:
ps2_write_keyboard(s->kbd, val);
+ /* sending data to the keyboard reenables PS/2 communication */
+ s->mode &= ~KBD_MODE_DISABLE_KBD;
+ kbd_safe_update_irq(s);
break;
case KBD_CCMD_WRITE_MODE:
s->mode = val;
ps2_keyboard_set_translation(s->kbd, (s->mode & KBD_MODE_KCC) != 0);
- /* ??? */
- kbd_update_irq(s);
+ /*
+ * a write to the mode byte interrupt enable flags directly updates
+ * the irq lines
+ */
+ kbd_update_irq_lines(s);
+ /*
+ * a write to the mode byte disable interface flags may raise
+ * an irq if there is pending data in the PS/2 queues.
+ */
+ kbd_safe_update_irq(s);
break;
case KBD_CCMD_WRITE_OBUF:
kbd_queue(s, val, 0);
@@ -354,6 +440,9 @@ static void kbd_write_data(void *opaque, hwaddr addr,
break;
case KBD_CCMD_WRITE_MOUSE:
ps2_write_mouse(s->mouse, val);
+ /* sending data to the mouse reenables PS/2 communication */
+ s->mode &= ~KBD_MODE_DISABLE_MOUSE;
+ kbd_safe_update_irq(s);
break;
default:
break;
@@ -368,7 +457,11 @@ static void kbd_reset(void *opaque)
s->mode = KBD_MODE_KBD_INT | KBD_MODE_MOUSE_INT;
s->status = KBD_STAT_CMD | KBD_STAT_UNLOCKED;
s->outport = KBD_OUT_RESET | KBD_OUT_A20 | KBD_OUT_ONES;
- s->outport_present = false;
+ s->pending = 0;
+ kbd_deassert_irq(s);
+ if (s->throttle_timer) {
+ timer_del(s->throttle_timer);
+ }
}
static uint8_t kbd_outport_default(KBDState *s)
@@ -403,13 +496,99 @@ static const VMStateDescription vmstate_kbd_outport = {
}
};
+static int kbd_extended_state_pre_save(void *opaque)
+{
+ KBDState *s = opaque;
+
+ s->migration_flags = 0;
+ if (s->throttle_timer && timer_pending(s->throttle_timer)) {
+ s->migration_flags |= KBD_MIGR_TIMER_PENDING;
+ }
+
+ return 0;
+}
+
+static int kbd_extended_state_post_load(void *opaque, int version_id)
+{
+ KBDState *s = opaque;
+
+ if (s->migration_flags & KBD_MIGR_TIMER_PENDING) {
+ kbd_throttle_timeout(s);
+ }
+ s->extended_state_loaded = true;
+
+ return 0;
+}
+
+static bool kbd_extended_state_needed(void *opaque)
+{
+ KBDState *s = opaque;
+
+ return s->extended_state;
+}
+
+static const VMStateDescription vmstate_kbd_extended_state = {
+ .name = "pckbd/extended_state",
+ .post_load = kbd_extended_state_post_load,
+ .pre_save = kbd_extended_state_pre_save,
+ .needed = kbd_extended_state_needed,
+ .fields = (VMStateField[]) {
+ VMSTATE_UINT32(migration_flags, KBDState),
+ VMSTATE_UINT32(obsrc, KBDState),
+ VMSTATE_UINT8(obdata, KBDState),
+ VMSTATE_UINT8(cbdata, KBDState),
+ VMSTATE_END_OF_LIST()
+ }
+};
+
+static int kbd_pre_save(void *opaque)
+{
+ KBDState *s = opaque;
+
+ if (s->extended_state) {
+ s->pending_tmp = s->pending;
+ } else {
+ s->pending_tmp = 0;
+ if (s->pending & KBD_PENDING_KBD) {
+ s->pending_tmp |= KBD_PENDING_KBD_COMPAT;
+ }
+ if (s->pending & KBD_PENDING_AUX) {
+ s->pending_tmp |= KBD_PENDING_AUX_COMPAT;
+ }
+ }
+ return 0;
+}
+
+static int kbd_pre_load(void *opaque)
+{
+ KBDState *s = opaque;
+
+ s->outport_present = false;
+ s->extended_state_loaded = false;
+ return 0;
+}
+
static int kbd_post_load(void *opaque, int version_id)
{
KBDState *s = opaque;
if (!s->outport_present) {
s->outport = kbd_outport_default(s);
}
- s->outport_present = false;
+ s->pending = s->pending_tmp;
+ if (!s->extended_state_loaded) {
+ s->obsrc = s->status & KBD_STAT_OBF ?
+ (s->status & KBD_STAT_MOUSE_OBF ? KBD_OBSRC_MOUSE : KBD_OBSRC_KBD) :
+ 0;
+ if (s->pending & KBD_PENDING_KBD_COMPAT) {
+ s->pending |= KBD_PENDING_KBD;
+ }
+ if (s->pending & KBD_PENDING_AUX_COMPAT) {
+ s->pending |= KBD_PENDING_AUX;
+ }
+ }
+ /* clear all unused flags */
+ s->pending &= KBD_PENDING_CTRL_KBD | KBD_PENDING_CTRL_AUX |
+ KBD_PENDING_KBD | KBD_PENDING_AUX;
return 0;
}
@@ -417,16 +596,19 @@ static const VMStateDescription vmstate_kbd = {
.name = "pckbd",
.version_id = 3,
.minimum_version_id = 3,
+ .pre_load = kbd_pre_load,
.post_load = kbd_post_load,
+ .pre_save = kbd_pre_save,
.fields = (VMStateField[]) {
VMSTATE_UINT8(write_cmd, KBDState),
VMSTATE_UINT8(status, KBDState),
VMSTATE_UINT8(mode, KBDState),
- VMSTATE_UINT8(pending, KBDState),
+ VMSTATE_UINT8(pending_tmp, KBDState),
VMSTATE_END_OF_LIST()
},
.subsections = (const VMStateDescription*[]) {
&vmstate_kbd_outport,
+ &vmstate_kbd_extended_state,
NULL
}
};
@@ -472,6 +654,8 @@ void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
s->irq_mouse = mouse_irq;
s->mask = mask;
+ s->extended_state = true;
+
vmstate_register(NULL, 0, &vmstate_kbd, s);
memory_region_init_io(region, NULL, &i8042_mmio_ops, s, "i8042", size);
@@ -485,6 +669,7 @@ struct ISAKBDState {
ISADevice parent_obj;
KBDState kbd;
+ bool kbd_throttle;
MemoryRegion io[2];
};
@@ -557,6 +742,13 @@ static void i8042_realizefn(DeviceState *dev, Error **errp)
s->kbd = ps2_kbd_init(kbd_update_kbd_irq, s);
s->mouse = ps2_mouse_init(kbd_update_aux_irq, s);
+ if (isa_s->kbd_throttle && !isa_s->kbd.extended_state) {
+ warn_report(TYPE_I8042 ": can't enable kbd-throttle without"
+ " extended-state, disabling kbd-throttle");
+ } else if (isa_s->kbd_throttle) {
+ s->throttle_timer = timer_new_us(QEMU_CLOCK_VIRTUAL,
+ kbd_throttle_timeout, s);
+ }
qemu_register_reset(kbd_reset, s);
}
@@ -588,11 +780,18 @@ static void i8042_build_aml(ISADevice *isadev, Aml *scope)
aml_append(scope, mou);
}
+static Property i8042_properties[] = {
+ DEFINE_PROP_BOOL("extended-state", ISAKBDState, kbd.extended_state, true),
+ DEFINE_PROP_BOOL("kbd-throttle", ISAKBDState, kbd_throttle, false),
+ DEFINE_PROP_END_OF_LIST(),
+};
+
static void i8042_class_initfn(ObjectClass *klass, void *data)
{
DeviceClass *dc = DEVICE_CLASS(klass);
ISADeviceClass *isa = ISA_DEVICE_CLASS(klass);
+ device_class_set_props(dc, i8042_properties);
dc->realize = i8042_realizefn;
dc->vmsd = &vmstate_kbd_isa;
isa->build_aml = i8042_build_aml;
diff --git a/hw/input/ps2.c b/hw/input/ps2.c
index 72cdb80ae1..8dd482c1f6 100644
--- a/hw/input/ps2.c
+++ b/hw/input/ps2.c
@@ -212,8 +212,12 @@ void ps2_raise_irq(PS2State *s)
void ps2_queue(PS2State *s, int b)
{
+ if (PS2_QUEUE_SIZE - s->queue.count < 1) {
+ return;
+ }
+
ps2_queue_noirq(s, b);
- s->update_irq(s->update_arg, 1);
+ ps2_raise_irq(s);
}
void ps2_queue_2(PS2State *s, int b1, int b2)
@@ -224,7 +228,7 @@ void ps2_queue_2(PS2State *s, int b1, int b2)
ps2_queue_noirq(s, b1);
ps2_queue_noirq(s, b2);
- s->update_irq(s->update_arg, 1);
+ ps2_raise_irq(s);
}
void ps2_queue_3(PS2State *s, int b1, int b2, int b3)
@@ -236,7 +240,7 @@ void ps2_queue_3(PS2State *s, int b1, int b2, int b3)
ps2_queue_noirq(s, b1);
ps2_queue_noirq(s, b2);
ps2_queue_noirq(s, b3);
- s->update_irq(s->update_arg, 1);
+ ps2_raise_irq(s);
}
void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4)
@@ -249,7 +253,7 @@ void ps2_queue_4(PS2State *s, int b1, int b2, int b3, int b4)
ps2_queue_noirq(s, b2);
ps2_queue_noirq(s, b3);
ps2_queue_noirq(s, b4);
- s->update_irq(s->update_arg, 1);
+ ps2_raise_irq(s);
}
/* keycode is the untranslated scancode in the current scancode set. */
@@ -293,7 +297,8 @@ static void ps2_keyboard_event(DeviceState *dev, QemuConsole *src,
qcode = qemu_input_key_value_to_qcode(key->key);
mod = ps2_modifier_bit(qcode);
- trace_ps2_keyboard_event(s, qcode, key->down, mod, s->modifiers);
+ trace_ps2_keyboard_event(s, qcode, key->down, mod,
+ s->modifiers, s->scancode_set, s->translate);
if (key->down) {
s->modifiers |= mod;
} else {
@@ -515,7 +520,9 @@ uint32_t ps2_read_data(PS2State *s)
/* reading deasserts IRQ */
s->update_irq(s->update_arg, 0);
/* reassert IRQs if data left */
- s->update_irq(s->update_arg, q->count != 0);
+ if (q->count) {
+ s->update_irq(s->update_arg, 1);
+ }
}
return val;
}
@@ -645,7 +652,8 @@ void ps2_keyboard_set_translation(void *opaque, int mode)
static int ps2_mouse_send_packet(PS2MouseState *s)
{
- const int needed = 3 + (s->mouse_type - 2);
+ /* IMPS/2 and IMEX send 4 bytes, PS2 sends 3 bytes */
+ const int needed = s->mouse_type ? 4 : 3;
unsigned int b;
int dx1, dy1, dz1;
diff --git a/hw/input/trace-events b/hw/input/trace-events
index 33741e74f5..109bdf7a18 100644
--- a/hw/input/trace-events
+++ b/hw/input/trace-events
@@ -30,7 +30,7 @@ pckbd_kbd_write_data(uint64_t val) "0x%02"PRIx64
# ps2.c
ps2_put_keycode(void *opaque, int keycode) "%p keycode 0x%02x"
-ps2_keyboard_event(void *opaque, int qcode, int down, unsigned int modifier, unsigned int modifiers) "%p qcode %d down %d modifier 0x%x modifiers 0x%x"
+ps2_keyboard_event(void *opaque, int qcode, int down, unsigned int modifier, unsigned int modifiers, int set, int xlate) "%p qcode %d down %d modifier 0x%x modifiers 0x%x set %d xlate %d"
ps2_read_data(void *opaque) "%p"
ps2_set_ledstate(void *s, int ledstate) "%p ledstate %d"
ps2_reset_keyboard(void *s) "%p"
diff --git a/tests/plugin/syscall.c b/tests/plugin/syscall.c
index 53ee2ab6c4..6dd71092e1 100644
--- a/tests/plugin/syscall.c
+++ b/tests/plugin/syscall.c
@@ -16,32 +16,120 @@
QEMU_PLUGIN_EXPORT int qemu_plugin_version = QEMU_PLUGIN_VERSION;
+typedef struct {
+ int64_t num;
+ int64_t calls;
+ int64_t errors;
+} SyscallStats;
+
+static GMutex lock;
+static GHashTable *statistics;
+
+static SyscallStats *get_or_create_entry(int64_t num)
+{
+ SyscallStats *entry =
+ (SyscallStats *) g_hash_table_lookup(statistics, GINT_TO_POINTER(num));
+
+ if (!entry) {
+ entry = g_new0(SyscallStats, 1);
+ entry->num = num;
+ g_hash_table_insert(statistics, GINT_TO_POINTER(num), (gpointer) entry);
+ }
+
+ return entry;
+}
+
static void vcpu_syscall(qemu_plugin_id_t id, unsigned int vcpu_index,
int64_t num, uint64_t a1, uint64_t a2,
uint64_t a3, uint64_t a4, uint64_t a5,
uint64_t a6, uint64_t a7, uint64_t a8)
{
- g_autofree gchar *out = g_strdup_printf("syscall #%" PRIi64 "\n", num);
- qemu_plugin_outs(out);
+ if (statistics) {
+ SyscallStats *entry;
+ g_mutex_lock(&lock);
+ entry = get_or_create_entry(num);
+ entry->calls++;
+ g_mutex_unlock(&lock);
+ } else {
+ g_autofree gchar *out = g_strdup_printf("syscall #%" PRIi64 "\n", num);
+ qemu_plugin_outs(out);
+ }
}
static void vcpu_syscall_ret(qemu_plugin_id_t id, unsigned int vcpu_idx,
int64_t num, int64_t ret)
{
+ if (statistics) {
+ SyscallStats *entry;
+
+ g_mutex_lock(&lock);
+ /* Should always return an existent entry. */
+ entry = get_or_create_entry(num);
+ if (ret < 0) {
+ entry->errors++;
+ }
+ g_mutex_unlock(&lock);
+ } else {
+ g_autofree gchar *out;
+ out = g_strdup_printf("syscall #%" PRIi64 " returned -> %" PRIi64 "\n",
+ num, ret);
+ qemu_plugin_outs(out);
+ }
+}
+
+static void print_entry(gpointer val, gpointer user_data)
+{
g_autofree gchar *out;
- out = g_strdup_printf("syscall #%" PRIi64 " returned -> %" PRIi64 "\n",
- num, ret);
+ SyscallStats *entry = (SyscallStats *) val;
+ int64_t syscall_num = entry->num;
+ out = g_strdup_printf(
+ "%-13" PRIi64 "%-6" PRIi64 " %" PRIi64 "\n",
+ syscall_num, entry->calls, entry->errors);
qemu_plugin_outs(out);
}
+static gint comp_func(gconstpointer ea, gconstpointer eb)
+{
+ SyscallStats *ent_a = (SyscallStats *) ea;
+ SyscallStats *ent_b = (SyscallStats *) eb;
+
+ return ent_a->calls > ent_b->calls ? -1 : 1;
+}
+
/* ************************************************************************* */
+static void plugin_exit(qemu_plugin_id_t id, void *p)
+{
+ if (!statistics) {
+ return;
+ }
+
+ g_mutex_lock(&lock);
+ GList *entries = g_hash_table_get_values(statistics);
+ entries = g_list_sort(entries, comp_func);
+ qemu_plugin_outs("syscall no. calls errors\n");
-static void plugin_exit(qemu_plugin_id_t id, void *p) {}
+ g_list_foreach(entries, print_entry, NULL);
+
+ g_list_free(entries);
+ g_hash_table_destroy(statistics);
+ g_mutex_unlock(&lock);
+}
QEMU_PLUGIN_EXPORT int qemu_plugin_install(qemu_plugin_id_t id,
const qemu_info_t *info,
int argc, char **argv)
{
+ if (argc == 0) {
+ statistics = g_hash_table_new_full(NULL, g_direct_equal, NULL, g_free);
+ } else {
+ for (int i = 0; i < argc; i++) {
+ if (g_strcmp0(argv[i], "print") != 0) {
+ fprintf(stderr, "unsupported argument: %s\n", argv[i]);
+ return -1;
+ }
+ }
+ }
+
qemu_plugin_register_vcpu_syscall_cb(id, vcpu_syscall);
qemu_plugin_register_vcpu_syscall_ret_cb(id, vcpu_syscall_ret);
qemu_plugin_register_atexit_cb(id, plugin_exit, NULL);