1 | From: Sameeh Jubran <sjubran@redhat.com> | 1 | Hi, |
---|---|---|---|
2 | 2 | ||
3 | This series fixes qemu-ga's behaviour upon facing a missing serial/serial | 3 | This new version removed the translate_fn() from patch 1 because it |
4 | driver by listening to the serial device's events. | 4 | wasn't removing the sign-extension for pentry as we thought it would. |
5 | A more detailed explanation is given in the commit msg of patch 1. | ||
5 | 6 | ||
6 | For more info on why this series is needed checkout the commit message | 7 | We're now retrieving the 'lowaddr' value from load_elf_ram_sym() and |
7 | of the third patch and the following bugzilla: https://bugzilla.redhat.com/show_bug.cgi?id=990629. | 8 | using it when we're running a 32-bit CPU. This worked with 32 bit |
9 | 'virt' machine booting with the -kernel option. | ||
8 | 10 | ||
9 | Sameeh Jubran (3): | 11 | If this approach doesn't work for the Xvisor use case, IMO we should |
10 | qga: Channel: Add functions for checking serial status | 12 | just filter kernel_load_addr bits directly as we were doing a handful of |
11 | qga: main: make qga config and socket activation global | 13 | versions ago. |
12 | qga: Prevent qemu-ga exit if serial doesn't exist | ||
13 | 14 | ||
14 | Makefile | 4 + | 15 | Patches are based on current riscv-to-apply.next. |
15 | qga/channel-posix.c | 54 ++++++++++ | 16 | |
16 | qga/channel-win32.c | 60 +++++++++++ | 17 | Changes from v9: |
17 | qga/channel.h | 9 ++ | 18 | - patch 1: |
18 | qga/main.c | 284 ++++++++++++++++++++++++++++++++++++++++++++++------ | 19 | - removed the translate_fn() callback |
19 | qga/service-win32.h | 4 + | 20 | - return 'kernel_low' when running a 32-bit CPU |
20 | 6 files changed, 385 insertions(+), 30 deletions(-) | 21 | - v9 link: https://lists.gnu.org/archive/html/qemu-devel/2023-01/msg04509.html |
22 | |||
23 | Daniel Henrique Barboza (3): | ||
24 | hw/riscv: handle 32 bit CPUs kernel_addr in riscv_load_kernel() | ||
25 | hw/riscv/boot.c: consolidate all kernel init in riscv_load_kernel() | ||
26 | hw/riscv/boot.c: make riscv_load_initrd() static | ||
27 | |||
28 | hw/riscv/boot.c | 96 +++++++++++++++++++++++--------------- | ||
29 | hw/riscv/microchip_pfsoc.c | 12 +---- | ||
30 | hw/riscv/opentitan.c | 4 +- | ||
31 | hw/riscv/sifive_e.c | 4 +- | ||
32 | hw/riscv/sifive_u.c | 12 +---- | ||
33 | hw/riscv/spike.c | 14 ++---- | ||
34 | hw/riscv/virt.c | 12 +---- | ||
35 | include/hw/riscv/boot.h | 3 +- | ||
36 | 8 files changed, 76 insertions(+), 81 deletions(-) | ||
21 | 37 | ||
22 | -- | 38 | -- |
23 | 2.9.4 | 39 | 2.39.1 |
24 | |||
25 | diff view generated by jsdifflib |
1 | From: Sameeh Jubran <sjubran@redhat.com> | 1 | load_elf_ram_sym() will sign-extend 32 bit addresses. If a 32 bit QEMU |
---|---|---|---|
2 | guest happens to be running in a hypervisor that are using 64 bits to | ||
3 | encode its address, kernel_entry can be padded with '1's and create | ||
4 | problems [1]. | ||
2 | 5 | ||
3 | Signed-off-by: Sameeh Jubran <sjubran@redhat.com> | 6 | Using a translate_fn() callback in load_elf_ram_sym() to filter the |
7 | padding from the address doesn't work. A more detailed explanation can | ||
8 | be found in [2]. The short version is that glue(load_elf, SZ), from | ||
9 | include/hw/elf_ops.h, will calculate 'pentry' (mapped into the | ||
10 | 'kernel_load_base' var in riscv_load_Kernel()) before using | ||
11 | translate_fn(), and will not recalculate it after executing it. This | ||
12 | means that the callback does not prevent the padding from | ||
13 | kernel_load_base to appear. | ||
14 | |||
15 | Let's instead use a kernel_low var to capture the 'lowaddr' value from | ||
16 | load_elf_ram_sim(), and return it when we're dealing with 32 bit CPUs. | ||
17 | |||
18 | [1] https://lists.gnu.org/archive/html/qemu-devel/2023-01/msg02281.html | ||
19 | [2] https://lists.gnu.org/archive/html/qemu-devel/2023-02/msg00099.html | ||
20 | |||
21 | Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> | ||
4 | --- | 22 | --- |
5 | qga/main.c | 44 +++++++++++++++++++++++--------------------- | 23 | hw/riscv/boot.c | 15 +++++++++++---- |
6 | 1 file changed, 23 insertions(+), 21 deletions(-) | 24 | hw/riscv/microchip_pfsoc.c | 3 ++- |
25 | hw/riscv/opentitan.c | 3 ++- | ||
26 | hw/riscv/sifive_e.c | 3 ++- | ||
27 | hw/riscv/sifive_u.c | 3 ++- | ||
28 | hw/riscv/spike.c | 3 ++- | ||
29 | hw/riscv/virt.c | 3 ++- | ||
30 | include/hw/riscv/boot.h | 1 + | ||
31 | 8 files changed, 24 insertions(+), 10 deletions(-) | ||
7 | 32 | ||
8 | diff --git a/qga/main.c b/qga/main.c | 33 | diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c |
9 | index XXXXXXX..XXXXXXX 100644 | 34 | index XXXXXXX..XXXXXXX 100644 |
10 | --- a/qga/main.c | 35 | --- a/hw/riscv/boot.c |
11 | +++ b/qga/main.c | 36 | +++ b/hw/riscv/boot.c |
12 | @@ -XXX,XX +XXX,XX @@ struct GAState { | 37 | @@ -XXX,XX +XXX,XX @@ target_ulong riscv_load_firmware(const char *firmware_filename, |
13 | GAPersistentState pstate; | 38 | } |
14 | }; | 39 | |
15 | 40 | target_ulong riscv_load_kernel(MachineState *machine, | |
16 | +typedef struct GAConfig { | 41 | + RISCVHartArrayState *harts, |
17 | + char *channel_path; | 42 | target_ulong kernel_start_addr, |
18 | + char *method; | 43 | symbol_fn_t sym_cb) |
19 | + char *log_filepath; | 44 | { |
20 | + char *pid_filepath; | 45 | const char *kernel_filename = machine->kernel_filename; |
21 | +#ifdef CONFIG_FSFREEZE | 46 | - uint64_t kernel_load_base, kernel_entry; |
22 | + char *fsfreeze_hook; | 47 | + uint64_t kernel_load_base, kernel_entry, kernel_low; |
23 | +#endif | 48 | |
24 | + char *state_dir; | 49 | g_assert(kernel_filename != NULL); |
25 | +#ifdef _WIN32 | 50 | |
26 | + const char *service; | 51 | @@ -XXX,XX +XXX,XX @@ target_ulong riscv_load_kernel(MachineState *machine, |
27 | +#endif | 52 | * the (expected) load address load address. This allows kernels to have |
28 | + gchar *bliststr; /* blacklist may point to this string */ | 53 | * separate SBI and ELF entry points (used by FreeBSD, for example). |
29 | + GList *blacklist; | 54 | */ |
30 | + int daemonize; | 55 | - if (load_elf_ram_sym(kernel_filename, NULL, NULL, NULL, |
31 | + GLogLevelFlags log_level; | 56 | - NULL, &kernel_load_base, NULL, NULL, 0, |
32 | + int dumpconf; | 57 | + if (load_elf_ram_sym(kernel_filename, NULL, NULL, NULL, NULL, |
33 | +} GAConfig; | 58 | + &kernel_load_base, &kernel_low, NULL, 0, |
59 | EM_RISCV, 1, 0, NULL, true, sym_cb) > 0) { | ||
60 | - return kernel_load_base; | ||
61 | + kernel_entry = kernel_load_base; | ||
34 | + | 62 | + |
35 | struct GAState *ga_state; | 63 | + if (riscv_is_32bit(harts)) { |
36 | +struct GAConfig *ga_config; | 64 | + kernel_entry = kernel_low; |
37 | +int ga_socket_activation; | 65 | + } |
38 | QmpCommandList ga_commands; | 66 | + |
39 | 67 | + return kernel_entry; | |
40 | /* commands that are safe to issue while filesystems are frozen */ | 68 | } |
41 | @@ -XXX,XX +XXX,XX @@ static GList *split_list(const gchar *str, const gchar *delim) | 69 | |
42 | return list; | 70 | if (load_uimage_as(kernel_filename, &kernel_entry, NULL, NULL, |
71 | diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c | ||
72 | index XXXXXXX..XXXXXXX 100644 | ||
73 | --- a/hw/riscv/microchip_pfsoc.c | ||
74 | +++ b/hw/riscv/microchip_pfsoc.c | ||
75 | @@ -XXX,XX +XXX,XX @@ static void microchip_icicle_kit_machine_init(MachineState *machine) | ||
76 | kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc.u_cpus, | ||
77 | firmware_end_addr); | ||
78 | |||
79 | - kernel_entry = riscv_load_kernel(machine, kernel_start_addr, NULL); | ||
80 | + kernel_entry = riscv_load_kernel(machine, &s->soc.u_cpus, | ||
81 | + kernel_start_addr, NULL); | ||
82 | |||
83 | if (machine->initrd_filename) { | ||
84 | riscv_load_initrd(machine, kernel_entry); | ||
85 | diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c | ||
86 | index XXXXXXX..XXXXXXX 100644 | ||
87 | --- a/hw/riscv/opentitan.c | ||
88 | +++ b/hw/riscv/opentitan.c | ||
89 | @@ -XXX,XX +XXX,XX @@ static void opentitan_board_init(MachineState *machine) | ||
90 | } | ||
91 | |||
92 | if (machine->kernel_filename) { | ||
93 | - riscv_load_kernel(machine, memmap[IBEX_DEV_RAM].base, NULL); | ||
94 | + riscv_load_kernel(machine, &s->soc.cpus, | ||
95 | + memmap[IBEX_DEV_RAM].base, NULL); | ||
96 | } | ||
43 | } | 97 | } |
44 | 98 | ||
45 | -typedef struct GAConfig { | 99 | diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c |
46 | - char *channel_path; | 100 | index XXXXXXX..XXXXXXX 100644 |
47 | - char *method; | 101 | --- a/hw/riscv/sifive_e.c |
48 | - char *log_filepath; | 102 | +++ b/hw/riscv/sifive_e.c |
49 | - char *pid_filepath; | 103 | @@ -XXX,XX +XXX,XX @@ static void sifive_e_machine_init(MachineState *machine) |
50 | -#ifdef CONFIG_FSFREEZE | 104 | memmap[SIFIVE_E_DEV_MROM].base, &address_space_memory); |
51 | - char *fsfreeze_hook; | 105 | |
52 | -#endif | 106 | if (machine->kernel_filename) { |
53 | - char *state_dir; | 107 | - riscv_load_kernel(machine, memmap[SIFIVE_E_DEV_DTIM].base, NULL); |
54 | -#ifdef _WIN32 | 108 | + riscv_load_kernel(machine, &s->soc.cpus, |
55 | - const char *service; | 109 | + memmap[SIFIVE_E_DEV_DTIM].base, NULL); |
56 | -#endif | ||
57 | - gchar *bliststr; /* blacklist may point to this string */ | ||
58 | - GList *blacklist; | ||
59 | - int daemonize; | ||
60 | - GLogLevelFlags log_level; | ||
61 | - int dumpconf; | ||
62 | -} GAConfig; | ||
63 | - | ||
64 | static void config_load(GAConfig *config) | ||
65 | { | ||
66 | GError *gerr = NULL; | ||
67 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) | ||
68 | { | ||
69 | int ret = EXIT_SUCCESS; | ||
70 | GAState *s = g_new0(GAState, 1); | ||
71 | - GAConfig *config = g_new0(GAConfig, 1); | ||
72 | + GAConfig *config = ga_config = g_new0(GAConfig, 1); | ||
73 | int socket_activation; | ||
74 | |||
75 | config->log_level = G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL; | ||
76 | @@ -XXX,XX +XXX,XX @@ int main(int argc, char **argv) | ||
77 | config->method = g_strdup("virtio-serial"); | ||
78 | } | 110 | } |
79 | 111 | } | |
80 | - socket_activation = check_socket_activation(); | 112 | |
81 | + ga_socket_activation = socket_activation = check_socket_activation(); | 113 | diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c |
82 | if (socket_activation > 1) { | 114 | index XXXXXXX..XXXXXXX 100644 |
83 | g_critical("qemu-ga only supports listening on one socket"); | 115 | --- a/hw/riscv/sifive_u.c |
84 | ret = EXIT_FAILURE; | 116 | +++ b/hw/riscv/sifive_u.c |
117 | @@ -XXX,XX +XXX,XX @@ static void sifive_u_machine_init(MachineState *machine) | ||
118 | kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc.u_cpus, | ||
119 | firmware_end_addr); | ||
120 | |||
121 | - kernel_entry = riscv_load_kernel(machine, kernel_start_addr, NULL); | ||
122 | + kernel_entry = riscv_load_kernel(machine, &s->soc.u_cpus, | ||
123 | + kernel_start_addr, NULL); | ||
124 | |||
125 | if (machine->initrd_filename) { | ||
126 | riscv_load_initrd(machine, kernel_entry); | ||
127 | diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c | ||
128 | index XXXXXXX..XXXXXXX 100644 | ||
129 | --- a/hw/riscv/spike.c | ||
130 | +++ b/hw/riscv/spike.c | ||
131 | @@ -XXX,XX +XXX,XX @@ static void spike_board_init(MachineState *machine) | ||
132 | kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc[0], | ||
133 | firmware_end_addr); | ||
134 | |||
135 | - kernel_entry = riscv_load_kernel(machine, kernel_start_addr, | ||
136 | + kernel_entry = riscv_load_kernel(machine, &s->soc[0], | ||
137 | + kernel_start_addr, | ||
138 | htif_symbol_callback); | ||
139 | |||
140 | if (machine->initrd_filename) { | ||
141 | diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c | ||
142 | index XXXXXXX..XXXXXXX 100644 | ||
143 | --- a/hw/riscv/virt.c | ||
144 | +++ b/hw/riscv/virt.c | ||
145 | @@ -XXX,XX +XXX,XX @@ static void virt_machine_done(Notifier *notifier, void *data) | ||
146 | kernel_start_addr = riscv_calc_kernel_start_addr(&s->soc[0], | ||
147 | firmware_end_addr); | ||
148 | |||
149 | - kernel_entry = riscv_load_kernel(machine, kernel_start_addr, NULL); | ||
150 | + kernel_entry = riscv_load_kernel(machine, &s->soc[0], | ||
151 | + kernel_start_addr, NULL); | ||
152 | |||
153 | if (machine->initrd_filename) { | ||
154 | riscv_load_initrd(machine, kernel_entry); | ||
155 | diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h | ||
156 | index XXXXXXX..XXXXXXX 100644 | ||
157 | --- a/include/hw/riscv/boot.h | ||
158 | +++ b/include/hw/riscv/boot.h | ||
159 | @@ -XXX,XX +XXX,XX @@ target_ulong riscv_load_firmware(const char *firmware_filename, | ||
160 | hwaddr firmware_load_addr, | ||
161 | symbol_fn_t sym_cb); | ||
162 | target_ulong riscv_load_kernel(MachineState *machine, | ||
163 | + RISCVHartArrayState *harts, | ||
164 | target_ulong firmware_end_addr, | ||
165 | symbol_fn_t sym_cb); | ||
166 | void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry); | ||
85 | -- | 167 | -- |
86 | 2.9.4 | 168 | 2.39.1 |
87 | |||
88 | diff view generated by jsdifflib |
1 | From: Sameeh Jubran <sjubran@redhat.com> | 1 | The microchip_icicle_kit, sifive_u, spike and virt boards are now doing |
---|---|---|---|
2 | 2 | the same steps when '-kernel' is used: | |
3 | This commit adds functions to check if the serial is | 3 | |
4 | connected/disconnected or else if it has been attached or detached. | 4 | - execute load_kernel() |
5 | 5 | - load init_rd() | |
6 | Signed-off-by: Sameeh Jubran <sjubran@redhat.com> | 6 | - write kernel_cmdline |
7 | |||
8 | Let's fold everything inside riscv_load_kernel() to avoid code | ||
9 | repetition. To not change the behavior of boards that aren't calling | ||
10 | riscv_load_init(), add an 'load_initrd' flag to riscv_load_kernel() and | ||
11 | allow these boards to opt out from initrd loading. | ||
12 | |||
13 | Cc: Palmer Dabbelt <palmer@dabbelt.com> | ||
14 | Reviewed-by: Bin Meng <bmeng@tinylab.org> | ||
15 | Reviewed-by: Alistair Francis <alistair.francis@wdc.com> | ||
16 | Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> | ||
7 | --- | 17 | --- |
8 | qga/channel-posix.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++ | 18 | hw/riscv/boot.c | 21 ++++++++++++++++++--- |
9 | qga/channel-win32.c | 60 +++++++++++++++++++++++++++++++++++++++++++++++++++++ | 19 | hw/riscv/microchip_pfsoc.c | 11 +---------- |
10 | qga/channel.h | 9 ++++++++ | 20 | hw/riscv/opentitan.c | 3 ++- |
11 | 3 files changed, 123 insertions(+) | 21 | hw/riscv/sifive_e.c | 3 ++- |
12 | 22 | hw/riscv/sifive_u.c | 11 +---------- | |
13 | diff --git a/qga/channel-posix.c b/qga/channel-posix.c | 23 | hw/riscv/spike.c | 11 +---------- |
14 | index XXXXXXX..XXXXXXX 100644 | 24 | hw/riscv/virt.c | 11 +---------- |
15 | --- a/qga/channel-posix.c | 25 | include/hw/riscv/boot.h | 1 + |
16 | +++ b/qga/channel-posix.c | 26 | 8 files changed, 27 insertions(+), 45 deletions(-) |
17 | @@ -XXX,XX +XXX,XX @@ void ga_channel_free(GAChannel *c) | 27 | |
18 | } | 28 | diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c |
19 | g_free(c); | 29 | index XXXXXXX..XXXXXXX 100644 |
20 | } | 30 | --- a/hw/riscv/boot.c |
31 | +++ b/hw/riscv/boot.c | ||
32 | @@ -XXX,XX +XXX,XX @@ target_ulong riscv_load_firmware(const char *firmware_filename, | ||
33 | target_ulong riscv_load_kernel(MachineState *machine, | ||
34 | RISCVHartArrayState *harts, | ||
35 | target_ulong kernel_start_addr, | ||
36 | + bool load_initrd, | ||
37 | symbol_fn_t sym_cb) | ||
38 | { | ||
39 | const char *kernel_filename = machine->kernel_filename; | ||
40 | uint64_t kernel_load_base, kernel_entry, kernel_low; | ||
41 | + void *fdt = machine->fdt; | ||
42 | |||
43 | g_assert(kernel_filename != NULL); | ||
44 | |||
45 | @@ -XXX,XX +XXX,XX @@ target_ulong riscv_load_kernel(MachineState *machine, | ||
46 | kernel_entry = kernel_low; | ||
47 | } | ||
48 | |||
49 | - return kernel_entry; | ||
50 | + goto out; | ||
51 | } | ||
52 | |||
53 | if (load_uimage_as(kernel_filename, &kernel_entry, NULL, NULL, | ||
54 | NULL, NULL, NULL) > 0) { | ||
55 | - return kernel_entry; | ||
56 | + goto out; | ||
57 | } | ||
58 | |||
59 | if (load_image_targphys_as(kernel_filename, kernel_start_addr, | ||
60 | current_machine->ram_size, NULL) > 0) { | ||
61 | - return kernel_start_addr; | ||
62 | + kernel_entry = kernel_start_addr; | ||
63 | + goto out; | ||
64 | } | ||
65 | |||
66 | error_report("could not load kernel '%s'", kernel_filename); | ||
67 | exit(1); | ||
21 | + | 68 | + |
22 | +static bool is_serial_present(GAChannelMethod method, const gchar *path, | 69 | +out: |
23 | + int *error_code) | 70 | + if (load_initrd && machine->initrd_filename) { |
24 | +{ | 71 | + riscv_load_initrd(machine, kernel_entry); |
25 | + int fd = -1; | ||
26 | + bool ret = true; | ||
27 | + | ||
28 | + assert(error_code); | ||
29 | + *error_code = 0; | ||
30 | + | ||
31 | + switch (method) { | ||
32 | + case GA_CHANNEL_VIRTIO_SERIAL: | ||
33 | + fd = qemu_open(path, O_RDWR | O_NONBLOCK | ||
34 | +#ifndef CONFIG_SOLARIS | ||
35 | + | O_ASYNC | ||
36 | +#endif | ||
37 | + ); | ||
38 | + break; | ||
39 | + case GA_CHANNEL_ISA_SERIAL: | ||
40 | + fd = qemu_open(path, O_RDWR | O_NOCTTY | O_NONBLOCK); | ||
41 | + break; | ||
42 | + default: | ||
43 | + ret = false; | ||
44 | + } | ||
45 | + if (fd < 0) { | ||
46 | + *error_code = errno; | ||
47 | + ret = false; | ||
48 | + } else { | ||
49 | + close(fd); | ||
50 | + } | ||
51 | + return ret; | ||
52 | +} | ||
53 | + | ||
54 | +bool ga_channel_serial_is_present(GAChannelMethod method, const gchar *path) | ||
55 | +{ | ||
56 | + int error_code = 0; | ||
57 | + return is_serial_present(method, path, &error_code) || | ||
58 | + error_code == EBUSY; | ||
59 | +} | ||
60 | + | ||
61 | +bool ga_channel_was_serial_attached(GAChannelMethod method, const gchar *path, | ||
62 | + bool is_serial_attached) | ||
63 | +{ | ||
64 | + int error_code = 0; | ||
65 | + return !is_serial_attached && | ||
66 | + is_serial_present(method, path, &error_code); | ||
67 | +} | ||
68 | +bool ga_channel_was_serial_detached(GAChannelMethod method, const gchar *path, | ||
69 | + bool is_serial_attached) | ||
70 | +{ | ||
71 | + int error_code = 0; | ||
72 | + return is_serial_attached && !is_serial_present(method, path, &error_code) | ||
73 | + && error_code == ENOENT; | ||
74 | +} | ||
75 | diff --git a/qga/channel-win32.c b/qga/channel-win32.c | ||
76 | index XXXXXXX..XXXXXXX 100644 | ||
77 | --- a/qga/channel-win32.c | ||
78 | +++ b/qga/channel-win32.c | ||
79 | @@ -XXX,XX +XXX,XX @@ void ga_channel_free(GAChannel *c) | ||
80 | g_free(c->rstate.buf); | ||
81 | g_free(c); | ||
82 | } | ||
83 | + | ||
84 | +static bool is_serial_present(GAChannelMethod method, const gchar *path, | ||
85 | + DWORD *err) | ||
86 | +{ | ||
87 | + gchar newpath[MAXPATHLEN] = { 0 }; | ||
88 | + bool ret = false; | ||
89 | + | ||
90 | + assert(err); | ||
91 | + | ||
92 | + if (method != GA_CHANNEL_VIRTIO_SERIAL && method != GA_CHANNEL_ISA_SERIAL) { | ||
93 | + g_critical("unsupported communication method"); | ||
94 | + return false; | ||
95 | + } | 72 | + } |
96 | + | 73 | + |
97 | + if (method == GA_CHANNEL_ISA_SERIAL) { | 74 | + if (fdt && machine->kernel_cmdline && *machine->kernel_cmdline) { |
98 | + snprintf(newpath, sizeof(newpath), "\\\\.\\%s", path); | 75 | + qemu_fdt_setprop_string(fdt, "/chosen", "bootargs", |
99 | + } else { | 76 | + machine->kernel_cmdline); |
100 | + g_strlcpy(newpath, path, sizeof(newpath)); | ||
101 | + } | 77 | + } |
102 | + | 78 | + |
103 | + HANDLE handle = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE, 0, NULL, | 79 | + return kernel_entry; |
104 | + OPEN_EXISTING, | 80 | } |
105 | + FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL); | 81 | |
106 | + | 82 | void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry) |
107 | + if (handle == INVALID_HANDLE_VALUE) { | 83 | diff --git a/hw/riscv/microchip_pfsoc.c b/hw/riscv/microchip_pfsoc.c |
108 | + *err = GetLastError(); | 84 | index XXXXXXX..XXXXXXX 100644 |
109 | + ret = false; | 85 | --- a/hw/riscv/microchip_pfsoc.c |
110 | + } else { | 86 | +++ b/hw/riscv/microchip_pfsoc.c |
111 | + ret = true; | 87 | @@ -XXX,XX +XXX,XX @@ static void microchip_icicle_kit_machine_init(MachineState *machine) |
112 | + } | 88 | firmware_end_addr); |
113 | + | 89 | |
114 | + CloseHandle(handle); | 90 | kernel_entry = riscv_load_kernel(machine, &s->soc.u_cpus, |
115 | + return ret; | 91 | - kernel_start_addr, NULL); |
116 | +} | 92 | - |
117 | + | 93 | - if (machine->initrd_filename) { |
118 | +bool ga_channel_serial_is_present(GAChannelMethod method, const gchar *path) | 94 | - riscv_load_initrd(machine, kernel_entry); |
119 | +{ | 95 | - } |
120 | + DWORD err_code; | 96 | - |
121 | + return is_serial_present(method, path, &err_code) || | 97 | - if (machine->kernel_cmdline && *machine->kernel_cmdline) { |
122 | + err_code == ERROR_ACCESS_DENIED; | 98 | - qemu_fdt_setprop_string(machine->fdt, "/chosen", |
123 | +} | 99 | - "bootargs", machine->kernel_cmdline); |
124 | + | 100 | - } |
125 | +bool ga_channel_was_serial_attached(GAChannelMethod method, const gchar *path, | 101 | + kernel_start_addr, true, NULL); |
126 | + bool is_serial_attached) | 102 | |
127 | +{ | 103 | /* Compute the fdt load address in dram */ |
128 | + DWORD err_code; | 104 | fdt_load_addr = riscv_compute_fdt_addr(memmap[MICROCHIP_PFSOC_DRAM_LO].base, |
129 | + return !is_serial_attached && is_serial_present(method, path, &err_code); | 105 | diff --git a/hw/riscv/opentitan.c b/hw/riscv/opentitan.c |
130 | +} | 106 | index XXXXXXX..XXXXXXX 100644 |
131 | + | 107 | --- a/hw/riscv/opentitan.c |
132 | +bool ga_channel_was_serial_detached(GAChannelMethod method, const gchar *path, | 108 | +++ b/hw/riscv/opentitan.c |
133 | + bool is_serial_attached) | 109 | @@ -XXX,XX +XXX,XX @@ static void opentitan_board_init(MachineState *machine) |
134 | +{ | 110 | |
135 | + DWORD err_code = NO_ERROR; | 111 | if (machine->kernel_filename) { |
136 | + /* In order to make sure the serial that qemu-ga uses is the one that | 112 | riscv_load_kernel(machine, &s->soc.cpus, |
137 | + * was detached. We'll get the error ERROR_FILE_NOT_FOUND when | 113 | - memmap[IBEX_DEV_RAM].base, NULL); |
138 | + * attempting to call CreateFile with the serial path. | 114 | + memmap[IBEX_DEV_RAM].base, |
139 | + */ | 115 | + false, NULL); |
140 | + return is_serial_attached && !is_serial_present(method, path, &err_code) | 116 | } |
141 | + && err_code == ERROR_FILE_NOT_FOUND; | 117 | } |
142 | +} | 118 | |
143 | diff --git a/qga/channel.h b/qga/channel.h | 119 | diff --git a/hw/riscv/sifive_e.c b/hw/riscv/sifive_e.c |
144 | index XXXXXXX..XXXXXXX 100644 | 120 | index XXXXXXX..XXXXXXX 100644 |
145 | --- a/qga/channel.h | 121 | --- a/hw/riscv/sifive_e.c |
146 | +++ b/qga/channel.h | 122 | +++ b/hw/riscv/sifive_e.c |
147 | @@ -XXX,XX +XXX,XX @@ | 123 | @@ -XXX,XX +XXX,XX @@ static void sifive_e_machine_init(MachineState *machine) |
148 | #ifndef QGA_CHANNEL_H | 124 | |
149 | #define QGA_CHANNEL_H | 125 | if (machine->kernel_filename) { |
150 | 126 | riscv_load_kernel(machine, &s->soc.cpus, | |
151 | +#ifndef _WIN32 | 127 | - memmap[SIFIVE_E_DEV_DTIM].base, NULL); |
152 | +#define SUBSYSTEM_VIRTIO_SERIAL "virtio-ports"; | 128 | + memmap[SIFIVE_E_DEV_DTIM].base, |
153 | +#define SUBSYSTEM_ISA_SERIAL "isa-serial"; | 129 | + false, NULL); |
154 | +#endif | 130 | } |
155 | 131 | } | |
156 | typedef struct GAChannel GAChannel; | 132 | |
157 | 133 | diff --git a/hw/riscv/sifive_u.c b/hw/riscv/sifive_u.c | |
158 | @@ -XXX,XX +XXX,XX @@ GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path, | 134 | index XXXXXXX..XXXXXXX 100644 |
159 | void ga_channel_free(GAChannel *c); | 135 | --- a/hw/riscv/sifive_u.c |
160 | GIOStatus ga_channel_read(GAChannel *c, gchar *buf, gsize size, gsize *count); | 136 | +++ b/hw/riscv/sifive_u.c |
161 | GIOStatus ga_channel_write_all(GAChannel *c, const gchar *buf, gsize size); | 137 | @@ -XXX,XX +XXX,XX @@ static void sifive_u_machine_init(MachineState *machine) |
162 | +bool ga_channel_serial_is_present(GAChannelMethod method, const gchar *path); | 138 | firmware_end_addr); |
163 | +bool ga_channel_was_serial_attached(GAChannelMethod method, const gchar *path, | 139 | |
164 | + bool is_serial_attached); | 140 | kernel_entry = riscv_load_kernel(machine, &s->soc.u_cpus, |
165 | +bool ga_channel_was_serial_detached(GAChannelMethod method, const gchar *path, | 141 | - kernel_start_addr, NULL); |
166 | + bool is_serial_attached); | 142 | - |
167 | 143 | - if (machine->initrd_filename) { | |
168 | #endif | 144 | - riscv_load_initrd(machine, kernel_entry); |
145 | - } | ||
146 | - | ||
147 | - if (machine->kernel_cmdline && *machine->kernel_cmdline) { | ||
148 | - qemu_fdt_setprop_string(machine->fdt, "/chosen", "bootargs", | ||
149 | - machine->kernel_cmdline); | ||
150 | - } | ||
151 | + kernel_start_addr, true, NULL); | ||
152 | } else { | ||
153 | /* | ||
154 | * If dynamic firmware is used, it doesn't know where is the next mode | ||
155 | diff --git a/hw/riscv/spike.c b/hw/riscv/spike.c | ||
156 | index XXXXXXX..XXXXXXX 100644 | ||
157 | --- a/hw/riscv/spike.c | ||
158 | +++ b/hw/riscv/spike.c | ||
159 | @@ -XXX,XX +XXX,XX @@ static void spike_board_init(MachineState *machine) | ||
160 | |||
161 | kernel_entry = riscv_load_kernel(machine, &s->soc[0], | ||
162 | kernel_start_addr, | ||
163 | - htif_symbol_callback); | ||
164 | - | ||
165 | - if (machine->initrd_filename) { | ||
166 | - riscv_load_initrd(machine, kernel_entry); | ||
167 | - } | ||
168 | - | ||
169 | - if (machine->kernel_cmdline && *machine->kernel_cmdline) { | ||
170 | - qemu_fdt_setprop_string(machine->fdt, "/chosen", "bootargs", | ||
171 | - machine->kernel_cmdline); | ||
172 | - } | ||
173 | + true, htif_symbol_callback); | ||
174 | } else { | ||
175 | /* | ||
176 | * If dynamic firmware is used, it doesn't know where is the next mode | ||
177 | diff --git a/hw/riscv/virt.c b/hw/riscv/virt.c | ||
178 | index XXXXXXX..XXXXXXX 100644 | ||
179 | --- a/hw/riscv/virt.c | ||
180 | +++ b/hw/riscv/virt.c | ||
181 | @@ -XXX,XX +XXX,XX @@ static void virt_machine_done(Notifier *notifier, void *data) | ||
182 | firmware_end_addr); | ||
183 | |||
184 | kernel_entry = riscv_load_kernel(machine, &s->soc[0], | ||
185 | - kernel_start_addr, NULL); | ||
186 | - | ||
187 | - if (machine->initrd_filename) { | ||
188 | - riscv_load_initrd(machine, kernel_entry); | ||
189 | - } | ||
190 | - | ||
191 | - if (machine->kernel_cmdline && *machine->kernel_cmdline) { | ||
192 | - qemu_fdt_setprop_string(machine->fdt, "/chosen", "bootargs", | ||
193 | - machine->kernel_cmdline); | ||
194 | - } | ||
195 | + kernel_start_addr, true, NULL); | ||
196 | } else { | ||
197 | /* | ||
198 | * If dynamic firmware is used, it doesn't know where is the next mode | ||
199 | diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h | ||
200 | index XXXXXXX..XXXXXXX 100644 | ||
201 | --- a/include/hw/riscv/boot.h | ||
202 | +++ b/include/hw/riscv/boot.h | ||
203 | @@ -XXX,XX +XXX,XX @@ target_ulong riscv_load_firmware(const char *firmware_filename, | ||
204 | target_ulong riscv_load_kernel(MachineState *machine, | ||
205 | RISCVHartArrayState *harts, | ||
206 | target_ulong firmware_end_addr, | ||
207 | + bool load_initrd, | ||
208 | symbol_fn_t sym_cb); | ||
209 | void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry); | ||
210 | uint64_t riscv_compute_fdt_addr(hwaddr dram_start, uint64_t dram_size, | ||
169 | -- | 211 | -- |
170 | 2.9.4 | 212 | 2.39.1 |
171 | |||
172 | diff view generated by jsdifflib |
1 | From: Sameeh Jubran <sjubran@redhat.com> | 1 | The only remaining caller is riscv_load_kernel_and_initrd() which |
---|---|---|---|
2 | belongs to the same file. | ||
2 | 3 | ||
3 | Currently whenever the qemu-ga's service doesn't find the virtio-serial | 4 | Signed-off-by: Daniel Henrique Barboza <dbarboza@ventanamicro.com> |
4 | it terminates. This commit addresses this issue by listening to the serial events | 5 | Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> |
5 | by registering for notifications for the chosen serial and it handles channel | 6 | Reviewed-by: Bin Meng <bmeng@tinylab.org> |
6 | initialization accordingily. | 7 | Reviewed-by: Alistair Francis <alistair.francis@wdc.com> |
8 | --- | ||
9 | hw/riscv/boot.c | 80 ++++++++++++++++++++--------------------- | ||
10 | include/hw/riscv/boot.h | 1 - | ||
11 | 2 files changed, 40 insertions(+), 41 deletions(-) | ||
7 | 12 | ||
8 | A list of possible scenarios of which could lead to this behavour: | 13 | diff --git a/hw/riscv/boot.c b/hw/riscv/boot.c |
9 | |||
10 | * qemu-ga's service is started while the virtio-serial driver hasn't been installed yet | ||
11 | * hotplug/ unplug of the virtio-serial device | ||
12 | * upgrading the virtio-serial driver | ||
13 | |||
14 | Note: This problem is much more common on Windows as the virtio-serial | ||
15 | driver should be installed by the user and isn't shipped with the Windows OS. | ||
16 | |||
17 | Signed-off-by: Sameeh Jubran <sjubran@redhat.com> | ||
18 | --- | ||
19 | Makefile | 4 + | ||
20 | qga/main.c | 240 ++++++++++++++++++++++++++++++++++++++++++++++++++-- | ||
21 | qga/service-win32.h | 4 + | ||
22 | 3 files changed, 239 insertions(+), 9 deletions(-) | ||
23 | |||
24 | diff --git a/Makefile b/Makefile | ||
25 | index XXXXXXX..XXXXXXX 100644 | 14 | index XXXXXXX..XXXXXXX 100644 |
26 | --- a/Makefile | 15 | --- a/hw/riscv/boot.c |
27 | +++ b/Makefile | 16 | +++ b/hw/riscv/boot.c |
28 | @@ -XXX,XX +XXX,XX @@ $(call set-vpath, $(SRC_PATH)) | 17 | @@ -XXX,XX +XXX,XX @@ target_ulong riscv_load_firmware(const char *firmware_filename, |
29 | 18 | exit(1); | |
30 | LIBS+=-lz $(LIBS_TOOLS) | 19 | } |
31 | 20 | ||
32 | +ifndef CONFIG_WIN32 | 21 | +static void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry) |
33 | +LIBS_QGA+=-ludev | 22 | +{ |
34 | +endif | 23 | + const char *filename = machine->initrd_filename; |
24 | + uint64_t mem_size = machine->ram_size; | ||
25 | + void *fdt = machine->fdt; | ||
26 | + hwaddr start, end; | ||
27 | + ssize_t size; | ||
35 | + | 28 | + |
36 | HELPERS-$(CONFIG_LINUX) = qemu-bridge-helper$(EXESUF) | 29 | + g_assert(filename != NULL); |
37 | |||
38 | ifdef BUILD_DOCS | ||
39 | diff --git a/qga/main.c b/qga/main.c | ||
40 | index XXXXXXX..XXXXXXX 100644 | ||
41 | --- a/qga/main.c | ||
42 | +++ b/qga/main.c | ||
43 | @@ -XXX,XX +XXX,XX @@ | ||
44 | #ifndef _WIN32 | ||
45 | #include <syslog.h> | ||
46 | #include <sys/wait.h> | ||
47 | +#include <libudev.h> | ||
48 | +#include <sys/types.h> | ||
49 | #endif | ||
50 | #include "qapi/qmp/json-streamer.h" | ||
51 | #include "qapi/qmp/json-parser.h" | ||
52 | @@ -XXX,XX +XXX,XX @@ | ||
53 | #include "qemu/sockets.h" | ||
54 | #include "qemu/systemd.h" | ||
55 | #ifdef _WIN32 | ||
56 | +#include <dbt.h> | ||
57 | #include "qga/service-win32.h" | ||
58 | #include "qga/vss-win32.h" | ||
59 | #endif | ||
60 | @@ -XXX,XX +XXX,XX @@ struct GAState { | ||
61 | GLogLevelFlags log_level; | ||
62 | FILE *log_file; | ||
63 | bool logging_enabled; | ||
64 | + bool serial_connected; | ||
65 | +#ifndef _WIN32 | ||
66 | + struct udev_monitor *udev_monitor; | ||
67 | +#endif | ||
68 | #ifdef _WIN32 | ||
69 | GAService service; | ||
70 | #endif | ||
71 | @@ -XXX,XX +XXX,XX @@ static const char *ga_freeze_whitelist[] = { | ||
72 | #ifdef _WIN32 | ||
73 | DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data, | ||
74 | LPVOID ctx); | ||
75 | +DWORD WINAPI handle_serial_device_events(DWORD type, LPVOID data); | ||
76 | VOID WINAPI service_main(DWORD argc, TCHAR *argv[]); | ||
77 | #endif | ||
78 | |||
79 | +static bool get_channel_method(GAChannelMethod *channel_method, | ||
80 | + const gchar *method, GAState *s); | ||
81 | +static gboolean channel_init(GAState *s, const gchar *method, const gchar *path, | ||
82 | + int listen_fd, bool *serial_connected); | ||
83 | + | 30 | + |
84 | static void | 31 | + /* |
85 | init_dfl_pathnames(void) | 32 | + * We want to put the initrd far enough into RAM that when the |
86 | { | 33 | + * kernel is uncompressed it will not clobber the initrd. However |
87 | @@ -XXX,XX +XXX,XX @@ static void quit_handler(int sig) | 34 | + * on boards without much RAM we must ensure that we still leave |
88 | } | 35 | + * enough room for a decent sized initrd, and on boards with large |
89 | 36 | + * amounts of RAM we must avoid the initrd being so far up in RAM | |
90 | #ifndef _WIN32 | 37 | + * that it is outside lowmem and inaccessible to the kernel. |
91 | +static int get_method_udev_subsystem(const char **serial_subsystem) | 38 | + * So for boards with less than 256MB of RAM we put the initrd |
92 | +{ | 39 | + * halfway into RAM, and for boards with 256MB of RAM or more we put |
93 | + if (strcmp(ga_config->method, "virtio-serial") == 0) { | 40 | + * the initrd at 128MB. |
94 | + *serial_subsystem = SUBSYSTEM_VIRTIO_SERIAL; | 41 | + */ |
95 | + } else if (strcmp(ga_config->method, "isa-serial") == 0) { | 42 | + start = kernel_entry + MIN(mem_size / 2, 128 * MiB); |
96 | + /* try the default path for the serial port - COM1 */ | ||
97 | + *serial_subsystem = SUBSYSTEM_ISA_SERIAL; | ||
98 | + } else { | ||
99 | + serial_subsystem = NULL; | ||
100 | + return -1; | ||
101 | + } | ||
102 | + return 0; | ||
103 | +} | ||
104 | + | 43 | + |
105 | +static gboolean serial_event_callback(GIOChannel *source, | 44 | + size = load_ramdisk(filename, start, mem_size - start); |
106 | + GIOCondition condition, gpointer data) | 45 | + if (size == -1) { |
107 | +{ | 46 | + size = load_image_targphys(filename, start, mem_size - start); |
108 | + struct udev_monitor *mon = ga_state->udev_monitor; | 47 | + if (size == -1) { |
109 | + const char *serial_subsystem = NULL; | 48 | + error_report("could not load ramdisk '%s'", filename); |
110 | + struct udev_device *dev; | 49 | + exit(1); |
111 | + | ||
112 | + if (get_method_udev_subsystem(&serial_subsystem) == -1) { | ||
113 | + return false; | ||
114 | + } | ||
115 | + dev = udev_monitor_receive_device(mon); | ||
116 | + | ||
117 | + if (dev && serial_subsystem && strcmp(udev_device_get_subsystem(dev), | ||
118 | + serial_subsystem) == 0) { | ||
119 | + | ||
120 | + GAChannelMethod channel_method; | ||
121 | + get_channel_method(&channel_method, ga_config->method, ga_state); | ||
122 | + if (ga_channel_was_serial_attached(channel_method, | ||
123 | + ga_config->channel_path, ga_state->serial_connected)) { | ||
124 | + ga_state->serial_connected = true; | ||
125 | + if (!channel_init(ga_state, | ||
126 | + ga_config->method, ga_config->channel_path, | ||
127 | + ga_socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1, | ||
128 | + &ga_state->serial_connected)) { | ||
129 | + g_critical("failed to initialize guest agent channel"); | ||
130 | + } | ||
131 | + } | 50 | + } |
132 | + | ||
133 | + if (ga_channel_was_serial_detached(channel_method, | ||
134 | + ga_config->channel_path, ga_state->serial_connected)) { | ||
135 | + ga_state->serial_connected = false; | ||
136 | + ga_channel_free(ga_state->channel); | ||
137 | + } | ||
138 | + udev_device_unref(dev); | ||
139 | + } | ||
140 | + return true; | ||
141 | +} | ||
142 | + | ||
143 | +static int monitor_serial_events(void) | ||
144 | +{ | ||
145 | + int ret = 0; | ||
146 | + const char *serial_subsystem = NULL; | ||
147 | + struct udev *udev = NULL; | ||
148 | + ga_state->udev_monitor = NULL; | ||
149 | + GIOChannel *channel = NULL; | ||
150 | + GSource *watch_source = NULL; | ||
151 | + if (get_method_udev_subsystem(&serial_subsystem) == -1) { | ||
152 | + ret = -1; | ||
153 | + goto out; | ||
154 | + } | 51 | + } |
155 | + | 52 | + |
156 | + udev = udev_new(); | 53 | + /* Some RISC-V machines (e.g. opentitan) don't have a fdt. */ |
157 | + if (!udev) { | 54 | + if (fdt) { |
158 | + g_error("Couldn't create udev\n"); | 55 | + end = start + size; |
159 | + ret = -1; | 56 | + qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", start); |
160 | + goto out; | 57 | + qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", end); |
161 | + } | ||
162 | + | ||
163 | + ga_state->udev_monitor = | ||
164 | + udev_monitor_new_from_netlink(udev, "udev"); | ||
165 | + if (!ga_state->udev_monitor) { | ||
166 | + ret = -1; | ||
167 | + goto out; | ||
168 | + } else { | ||
169 | + /* We don't want the udev_monitor to be freed on out, so increase | ||
170 | + * ref count | ||
171 | + */ | ||
172 | + udev_monitor_ref(ga_state->udev_monitor); | ||
173 | + } | ||
174 | + | ||
175 | + if (udev_monitor_filter_add_match_subsystem_devtype(ga_state->udev_monitor, | ||
176 | + serial_subsystem, NULL) < 0 || | ||
177 | + udev_monitor_enable_receiving(ga_state->udev_monitor) < 0) { | ||
178 | + ret = -1; | ||
179 | + goto out; | ||
180 | + } | ||
181 | + | ||
182 | + channel = | ||
183 | + g_io_channel_unix_new(udev_monitor_get_fd(ga_state->udev_monitor)); | ||
184 | + if (!channel) { | ||
185 | + ret = -1; | ||
186 | + goto out; | ||
187 | + } | ||
188 | + watch_source = g_io_create_watch(channel, G_IO_IN); | ||
189 | + if (!watch_source) { | ||
190 | + ret = -1; | ||
191 | + goto out; | ||
192 | + } | ||
193 | + g_source_set_callback(watch_source, (GSourceFunc)serial_event_callback, | ||
194 | + ga_state->udev_monitor, NULL); | ||
195 | + g_source_attach(watch_source, g_main_loop_get_context(ga_state->main_loop)); | ||
196 | + | ||
197 | +out: | ||
198 | + if (udev) { | ||
199 | + udev_unref(udev); | ||
200 | + } | ||
201 | + if (ga_state->udev_monitor) { | ||
202 | + udev_monitor_unref(ga_state->udev_monitor); | ||
203 | + } | ||
204 | + if (channel) { | ||
205 | + g_io_channel_unref(channel); | ||
206 | + } | ||
207 | + if (watch_source) { | ||
208 | + g_source_unref(watch_source); | ||
209 | + } | ||
210 | + | ||
211 | + return ret; | ||
212 | +} | ||
213 | + | ||
214 | +static void free_monitor_resources(void) | ||
215 | +{ | ||
216 | + if (ga_state->udev_monitor) { | ||
217 | + udev_monitor_unref(ga_state->udev_monitor); | ||
218 | + } | 58 | + } |
219 | +} | 59 | +} |
220 | + | 60 | + |
221 | static gboolean register_signal_handlers(void) | 61 | target_ulong riscv_load_kernel(MachineState *machine, |
222 | { | 62 | RISCVHartArrayState *harts, |
223 | struct sigaction sigact; | 63 | target_ulong kernel_start_addr, |
224 | @@ -XXX,XX +XXX,XX @@ static gboolean channel_event_cb(GIOCondition condition, gpointer data) | 64 | @@ -XXX,XX +XXX,XX @@ out: |
225 | return true; | 65 | return kernel_entry; |
226 | } | 66 | } |
227 | 67 | ||
228 | -static gboolean channel_init(GAState *s, const gchar *method, const gchar *path, | 68 | -void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry) |
229 | - int listen_fd) | 69 | -{ |
230 | +static bool get_channel_method(GAChannelMethod *channel_method, | 70 | - const char *filename = machine->initrd_filename; |
231 | + const gchar *method, GAState *s) | 71 | - uint64_t mem_size = machine->ram_size; |
232 | { | 72 | - void *fdt = machine->fdt; |
233 | - GAChannelMethod channel_method; | 73 | - hwaddr start, end; |
234 | + assert(channel_method); | 74 | - ssize_t size; |
235 | + assert(method); | ||
236 | |||
237 | if (strcmp(method, "virtio-serial") == 0) { | ||
238 | + assert(s); | ||
239 | s->virtio = true; /* virtio requires special handling in some cases */ | ||
240 | - channel_method = GA_CHANNEL_VIRTIO_SERIAL; | ||
241 | + *channel_method = GA_CHANNEL_VIRTIO_SERIAL; | ||
242 | } else if (strcmp(method, "isa-serial") == 0) { | ||
243 | - channel_method = GA_CHANNEL_ISA_SERIAL; | ||
244 | + *channel_method = GA_CHANNEL_ISA_SERIAL; | ||
245 | } else if (strcmp(method, "unix-listen") == 0) { | ||
246 | - channel_method = GA_CHANNEL_UNIX_LISTEN; | ||
247 | + *channel_method = GA_CHANNEL_UNIX_LISTEN; | ||
248 | } else if (strcmp(method, "vsock-listen") == 0) { | ||
249 | - channel_method = GA_CHANNEL_VSOCK_LISTEN; | ||
250 | + *channel_method = GA_CHANNEL_VSOCK_LISTEN; | ||
251 | } else { | ||
252 | g_critical("unsupported channel method/type: %s", method); | ||
253 | return false; | ||
254 | } | ||
255 | + return true; | ||
256 | +} | ||
257 | + | ||
258 | +static gboolean channel_init(GAState *s, const gchar *method, const gchar *path, | ||
259 | + int listen_fd, bool *serial_connected) | ||
260 | +{ | ||
261 | + assert(serial_connected); | ||
262 | + GAChannelMethod channel_method; | ||
263 | + if (!get_channel_method(&channel_method, method, s)) { | ||
264 | + return false; | ||
265 | + } | ||
266 | + *serial_connected = ga_channel_serial_is_present(channel_method, path); | ||
267 | |||
268 | s->channel = ga_channel_new(channel_method, path, listen_fd, | ||
269 | channel_event_cb, s); | ||
270 | @@ -XXX,XX +XXX,XX @@ static gboolean channel_init(GAState *s, const gchar *method, const gchar *path, | ||
271 | } | ||
272 | |||
273 | #ifdef _WIN32 | ||
274 | +DWORD WINAPI handle_serial_device_events(DWORD type, LPVOID data) | ||
275 | +{ | ||
276 | + DWORD ret = NO_ERROR; | ||
277 | + PDEV_BROADCAST_HDR broadcast_header = (PDEV_BROADCAST_HDR)data; | ||
278 | + GAChannelMethod channel_method; | ||
279 | + | ||
280 | + if (broadcast_header->dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE) { | ||
281 | + get_channel_method(&channel_method, ga_config->method, ga_state); | ||
282 | + switch (type) { | ||
283 | + /* Device inserted */ | ||
284 | + case DBT_DEVICEARRIVAL: | ||
285 | + /* Start QEMU-ga's service */ | ||
286 | + if (ga_channel_was_serial_attached(channel_method, | ||
287 | + ga_config->channel_path, ga_state->serial_connected)) { | ||
288 | + if (!channel_init(ga_state, | ||
289 | + ga_config->method, ga_config->channel_path, | ||
290 | + ga_socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1, | ||
291 | + &ga_state->serial_connected)) { | ||
292 | + g_critical("failed to initialize guest agent channel"); | ||
293 | + ret = EXIT_FAILURE; | ||
294 | + } | ||
295 | + } | ||
296 | + break; | ||
297 | + /* Device removed */ | ||
298 | + case DBT_DEVICEQUERYREMOVE: | ||
299 | + case DBT_DEVICEREMOVEPENDING: | ||
300 | + case DBT_DEVICEREMOVECOMPLETE: | ||
301 | + | ||
302 | + /* Stop QEMU-ga's service */ | ||
303 | + if (ga_channel_was_serial_detached(channel_method, | ||
304 | + ga_config->channel_path, ga_state->serial_connected)) { | ||
305 | + ga_state->serial_connected = false; | ||
306 | + ga_channel_free(ga_state->channel); | ||
307 | + } | ||
308 | + break; | ||
309 | + default: | ||
310 | + ret = ERROR_CALL_NOT_IMPLEMENTED; | ||
311 | + } | ||
312 | + } | ||
313 | + return ret; | ||
314 | +} | ||
315 | + | ||
316 | DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data, | ||
317 | LPVOID ctx) | ||
318 | { | ||
319 | @@ -XXX,XX +XXX,XX @@ DWORD WINAPI service_ctrl_handler(DWORD ctrl, DWORD type, LPVOID data, | ||
320 | service->status.dwCurrentState = SERVICE_STOP_PENDING; | ||
321 | SetServiceStatus(service->status_handle, &service->status); | ||
322 | break; | ||
323 | + case SERVICE_CONTROL_DEVICEEVENT: | ||
324 | + handle_serial_device_events(type, data); | ||
325 | + break; | ||
326 | |||
327 | default: | ||
328 | ret = ERROR_CALL_NOT_IMPLEMENTED; | ||
329 | @@ -XXX,XX +XXX,XX @@ VOID WINAPI service_main(DWORD argc, TCHAR *argv[]) | ||
330 | service->status.dwServiceSpecificExitCode = NO_ERROR; | ||
331 | service->status.dwCheckPoint = 0; | ||
332 | service->status.dwWaitHint = 0; | ||
333 | + DEV_BROADCAST_DEVICEINTERFACE notification_filter; | ||
334 | + ZeroMemory(¬ification_filter, sizeof(notification_filter)); | ||
335 | + notification_filter.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE; | ||
336 | + notification_filter.dbcc_size = sizeof(DEV_BROADCAST_DEVICEINTERFACE); | ||
337 | + notification_filter.dbcc_classguid = GUID_VIOSERIAL_PORT; | ||
338 | + | ||
339 | + service->device_notification_handle = | ||
340 | + RegisterDeviceNotification(service->status_handle, | ||
341 | + ¬ification_filter, DEVICE_NOTIFY_SERVICE_HANDLE); | ||
342 | + if (!service->device_notification_handle) { | ||
343 | + g_critical("Failed to register device notification handle!\n"); | ||
344 | + return; | ||
345 | + } | ||
346 | SetServiceStatus(service->status_handle, &service->status); | ||
347 | |||
348 | g_main_loop_run(ga_state->main_loop); | ||
349 | |||
350 | + UnregisterDeviceNotification(service->device_notification_handle); | ||
351 | service->status.dwCurrentState = SERVICE_STOPPED; | ||
352 | SetServiceStatus(service->status_handle, &service->status); | ||
353 | } | ||
354 | @@ -XXX,XX +XXX,XX @@ static int run_agent(GAState *s, GAConfig *config, int socket_activation) | ||
355 | #endif | ||
356 | |||
357 | s->main_loop = g_main_loop_new(NULL, false); | ||
358 | - | 75 | - |
359 | if (!channel_init(ga_state, config->method, config->channel_path, | 76 | - g_assert(filename != NULL); |
360 | - socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1)) { | 77 | - |
361 | + socket_activation ? FIRST_SOCKET_ACTIVATION_FD : -1, | 78 | - /* |
362 | + &ga_state->serial_connected) && | 79 | - * We want to put the initrd far enough into RAM that when the |
363 | + ga_state->serial_connected) { | 80 | - * kernel is uncompressed it will not clobber the initrd. However |
364 | g_critical("failed to initialize guest agent channel"); | 81 | - * on boards without much RAM we must ensure that we still leave |
365 | return EXIT_FAILURE; | 82 | - * enough room for a decent sized initrd, and on boards with large |
366 | } | 83 | - * amounts of RAM we must avoid the initrd being so far up in RAM |
367 | #ifndef _WIN32 | 84 | - * that it is outside lowmem and inaccessible to the kernel. |
368 | + monitor_serial_events(); | 85 | - * So for boards with less than 256MB of RAM we put the initrd |
369 | g_main_loop_run(ga_state->main_loop); | 86 | - * halfway into RAM, and for boards with 256MB of RAM or more we put |
370 | + free_monitor_resources(); | 87 | - * the initrd at 128MB. |
371 | #else | 88 | - */ |
372 | if (config->daemonize) { | 89 | - start = kernel_entry + MIN(mem_size / 2, 128 * MiB); |
373 | SERVICE_TABLE_ENTRY service_table[] = { | 90 | - |
374 | { (char *)QGA_SERVICE_NAME, service_main }, { NULL, NULL } }; | 91 | - size = load_ramdisk(filename, start, mem_size - start); |
375 | StartServiceCtrlDispatcher(service_table); | 92 | - if (size == -1) { |
376 | } else { | 93 | - size = load_image_targphys(filename, start, mem_size - start); |
377 | + if (!ga_state->serial_connected) { | 94 | - if (size == -1) { |
378 | + return EXIT_FAILURE; | 95 | - error_report("could not load ramdisk '%s'", filename); |
379 | + } | 96 | - exit(1); |
380 | g_main_loop_run(ga_state->main_loop); | 97 | - } |
381 | } | 98 | - } |
382 | #endif | 99 | - |
383 | diff --git a/qga/service-win32.h b/qga/service-win32.h | 100 | - /* Some RISC-V machines (e.g. opentitan) don't have a fdt. */ |
101 | - if (fdt) { | ||
102 | - end = start + size; | ||
103 | - qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-start", start); | ||
104 | - qemu_fdt_setprop_cell(fdt, "/chosen", "linux,initrd-end", end); | ||
105 | - } | ||
106 | -} | ||
107 | - | ||
108 | /* | ||
109 | * This function makes an assumption that the DRAM interval | ||
110 | * 'dram_base' + 'dram_size' is contiguous. | ||
111 | diff --git a/include/hw/riscv/boot.h b/include/hw/riscv/boot.h | ||
384 | index XXXXXXX..XXXXXXX 100644 | 112 | index XXXXXXX..XXXXXXX 100644 |
385 | --- a/qga/service-win32.h | 113 | --- a/include/hw/riscv/boot.h |
386 | +++ b/qga/service-win32.h | 114 | +++ b/include/hw/riscv/boot.h |
387 | @@ -XXX,XX +XXX,XX @@ | 115 | @@ -XXX,XX +XXX,XX @@ target_ulong riscv_load_kernel(MachineState *machine, |
388 | #define QGA_SERVICE_NAME "qemu-ga" | 116 | target_ulong firmware_end_addr, |
389 | #define QGA_SERVICE_DESCRIPTION "Enables integration with QEMU machine emulator and virtualizer." | 117 | bool load_initrd, |
390 | 118 | symbol_fn_t sym_cb); | |
391 | +static const GUID GUID_VIOSERIAL_PORT = { 0x6fde7521, 0x1b65, 0x48ae, | 119 | -void riscv_load_initrd(MachineState *machine, uint64_t kernel_entry); |
392 | +{ 0xb6, 0x28, 0x80, 0xbe, 0x62, 0x1, 0x60, 0x26 } }; | 120 | uint64_t riscv_compute_fdt_addr(hwaddr dram_start, uint64_t dram_size, |
393 | + | 121 | MachineState *ms); |
394 | typedef struct GAService { | 122 | void riscv_load_fdt(hwaddr fdt_addr, void *fdt); |
395 | SERVICE_STATUS status; | ||
396 | SERVICE_STATUS_HANDLE status_handle; | ||
397 | + HDEVNOTIFY device_notification_handle; | ||
398 | } GAService; | ||
399 | |||
400 | int ga_install_service(const char *path, const char *logfile, | ||
401 | -- | 123 | -- |
402 | 2.9.4 | 124 | 2.39.1 |
403 | 125 | ||
404 | 126 | diff view generated by jsdifflib |