xref: /openbmc/qemu/qga/channel-win32.c (revision 7286d62d4e259be8cecf3dc2deea80ecc14489a5)
14459bf38SPeter Maydell #include "qemu/osdep.h"
27868e26eSMichael Roth #include <windows.h>
37868e26eSMichael Roth #include <io.h>
4dc03272dSMichael S. Tsirkin #include "guest-agent-core.h"
5dc03272dSMichael S. Tsirkin #include "channel.h"
67868e26eSMichael Roth 
77868e26eSMichael Roth typedef struct GAChannelReadState {
87868e26eSMichael Roth     guint thread_id;
97868e26eSMichael Roth     uint8_t *buf;
107868e26eSMichael Roth     size_t buf_size;
117868e26eSMichael Roth     size_t cur; /* current buffer start */
127868e26eSMichael Roth     size_t pending; /* pending buffered bytes to read */
137868e26eSMichael Roth     OVERLAPPED ov;
147868e26eSMichael Roth     bool ov_pending; /* whether on async read is outstanding */
157868e26eSMichael Roth } GAChannelReadState;
167868e26eSMichael Roth 
177868e26eSMichael Roth struct GAChannel {
187868e26eSMichael Roth     HANDLE handle;
197868e26eSMichael Roth     GAChannelCallback cb;
207868e26eSMichael Roth     gpointer user_data;
217868e26eSMichael Roth     GAChannelReadState rstate;
227868e26eSMichael Roth     GIOCondition pending_events; /* TODO: use GAWatch.pollfd.revents */
237868e26eSMichael Roth     GSource *source;
247868e26eSMichael Roth };
257868e26eSMichael Roth 
267868e26eSMichael Roth typedef struct GAWatch {
277868e26eSMichael Roth     GSource source;
287868e26eSMichael Roth     GPollFD pollfd;
297868e26eSMichael Roth     GAChannel *channel;
307868e26eSMichael Roth     GIOCondition events_mask;
317868e26eSMichael Roth } GAWatch;
327868e26eSMichael Roth 
337868e26eSMichael Roth /*
347868e26eSMichael Roth  * Called by glib prior to polling to set up poll events if polling is needed.
357868e26eSMichael Roth  *
367868e26eSMichael Roth  */
ga_channel_prepare(GSource * source,gint * timeout_ms)377868e26eSMichael Roth static gboolean ga_channel_prepare(GSource *source, gint *timeout_ms)
387868e26eSMichael Roth {
397868e26eSMichael Roth     GAWatch *watch = (GAWatch *)source;
407868e26eSMichael Roth     GAChannel *c = (GAChannel *)watch->channel;
417868e26eSMichael Roth     GAChannelReadState *rs = &c->rstate;
427868e26eSMichael Roth     DWORD count_read, count_to_read = 0;
437868e26eSMichael Roth     bool success;
447868e26eSMichael Roth     GIOCondition new_events = 0;
457868e26eSMichael Roth 
467868e26eSMichael Roth     g_debug("prepare");
477868e26eSMichael Roth     /* go ahead and submit another read if there's room in the buffer
487868e26eSMichael Roth      * and no previous reads are outstanding
497868e26eSMichael Roth      */
507868e26eSMichael Roth     if (!rs->ov_pending) {
517868e26eSMichael Roth         if (rs->cur + rs->pending >= rs->buf_size) {
527868e26eSMichael Roth             if (rs->cur) {
537868e26eSMichael Roth                 memmove(rs->buf, rs->buf + rs->cur, rs->pending);
547868e26eSMichael Roth                 rs->cur = 0;
557868e26eSMichael Roth             }
567868e26eSMichael Roth         }
577868e26eSMichael Roth         count_to_read = rs->buf_size - rs->cur - rs->pending;
587868e26eSMichael Roth     }
597868e26eSMichael Roth 
607868e26eSMichael Roth     if (rs->ov_pending || count_to_read <= 0) {
617868e26eSMichael Roth             goto out;
627868e26eSMichael Roth     }
637868e26eSMichael Roth 
647868e26eSMichael Roth     /* submit the read */
657868e26eSMichael Roth     success = ReadFile(c->handle, rs->buf + rs->cur + rs->pending,
667868e26eSMichael Roth                        count_to_read, &count_read, &rs->ov);
677868e26eSMichael Roth     if (success) {
687868e26eSMichael Roth         rs->pending += count_read;
697868e26eSMichael Roth         rs->ov_pending = false;
707868e26eSMichael Roth     } else {
717868e26eSMichael Roth         if (GetLastError() == ERROR_IO_PENDING) {
727868e26eSMichael Roth             rs->ov_pending = true;
737868e26eSMichael Roth         } else {
747868e26eSMichael Roth             new_events |= G_IO_ERR;
757868e26eSMichael Roth         }
767868e26eSMichael Roth     }
777868e26eSMichael Roth 
787868e26eSMichael Roth out:
79cb8d4c8fSStefan Weil     /* don't block forever, iterate the main loop every once in a while */
807868e26eSMichael Roth     *timeout_ms = 500;
817868e26eSMichael Roth     /* if there's data in the read buffer, or another event is pending,
827868e26eSMichael Roth      * skip polling and issue user cb.
837868e26eSMichael Roth      */
847868e26eSMichael Roth     if (rs->pending) {
857868e26eSMichael Roth         new_events |= G_IO_IN;
867868e26eSMichael Roth     }
877868e26eSMichael Roth     c->pending_events |= new_events;
887868e26eSMichael Roth     return !!c->pending_events;
897868e26eSMichael Roth }
907868e26eSMichael Roth 
917868e26eSMichael Roth /*
927868e26eSMichael Roth  * Called by glib after an outstanding read request is completed.
937868e26eSMichael Roth  */
ga_channel_check(GSource * source)947868e26eSMichael Roth static gboolean ga_channel_check(GSource *source)
957868e26eSMichael Roth {
967868e26eSMichael Roth     GAWatch *watch = (GAWatch *)source;
977868e26eSMichael Roth     GAChannel *c = (GAChannel *)watch->channel;
987868e26eSMichael Roth     GAChannelReadState *rs = &c->rstate;
997868e26eSMichael Roth     DWORD count_read, error;
1007868e26eSMichael Roth     BOOL success;
1017868e26eSMichael Roth 
1027868e26eSMichael Roth     GIOCondition new_events = 0;
1037868e26eSMichael Roth 
1047868e26eSMichael Roth     g_debug("check");
1057868e26eSMichael Roth 
1067868e26eSMichael Roth     /* failing this implies we issued a read that completed immediately,
1077868e26eSMichael Roth      * yet no data was placed into the buffer (and thus we did not skip
1087868e26eSMichael Roth      * polling). but since EOF is not obtainable until we retrieve an
1097868e26eSMichael Roth      * overlapped result, it must be the case that there was data placed
1107868e26eSMichael Roth      * into the buffer, or an error was generated by Readfile(). in either
1117868e26eSMichael Roth      * case, we should've skipped the polling for this round.
1127868e26eSMichael Roth      */
1137868e26eSMichael Roth     g_assert(rs->ov_pending);
1147868e26eSMichael Roth 
1157868e26eSMichael Roth     success = GetOverlappedResult(c->handle, &rs->ov, &count_read, FALSE);
1167868e26eSMichael Roth     if (success) {
1177868e26eSMichael Roth         g_debug("thread: overlapped result, count_read: %d", (int)count_read);
1187868e26eSMichael Roth         rs->pending += count_read;
1197868e26eSMichael Roth         new_events |= G_IO_IN;
1207868e26eSMichael Roth     } else {
1217868e26eSMichael Roth         error = GetLastError();
1227868e26eSMichael Roth         if (error == 0 || error == ERROR_HANDLE_EOF ||
1237868e26eSMichael Roth             error == ERROR_NO_SYSTEM_RESOURCES ||
1247868e26eSMichael Roth             error == ERROR_OPERATION_ABORTED) {
1257868e26eSMichael Roth             /* note: On WinXP SP3 with rhel6ga virtio-win-1.1.16 vioser drivers,
1267868e26eSMichael Roth              * ENSR seems to be synonymous with when we'd normally expect
1277868e26eSMichael Roth              * ERROR_HANDLE_EOF. So treat it as such. Microsoft's
1287868e26eSMichael Roth              * recommendation for ERROR_NO_SYSTEM_RESOURCES is to
1297868e26eSMichael Roth              * retry the read, so this happens to work out anyway. On newer
1307868e26eSMichael Roth              * virtio-win driver, this seems to be replaced with EOA, so
1317868e26eSMichael Roth              * handle that in the same fashion.
1327868e26eSMichael Roth              */
1337868e26eSMichael Roth             new_events |= G_IO_HUP;
1347868e26eSMichael Roth         } else if (error != ERROR_IO_INCOMPLETE) {
1357868e26eSMichael Roth             g_critical("error retrieving overlapped result: %d", (int)error);
1367868e26eSMichael Roth             new_events |= G_IO_ERR;
1377868e26eSMichael Roth         }
1387868e26eSMichael Roth     }
1397868e26eSMichael Roth 
1407868e26eSMichael Roth     if (new_events) {
1417868e26eSMichael Roth         rs->ov_pending = 0;
1427868e26eSMichael Roth     }
1437868e26eSMichael Roth     c->pending_events |= new_events;
1447868e26eSMichael Roth 
1457868e26eSMichael Roth     return !!c->pending_events;
1467868e26eSMichael Roth }
1477868e26eSMichael Roth 
1487868e26eSMichael Roth /*
1497868e26eSMichael Roth  * Called by glib after either prepare or check routines signal readiness
1507868e26eSMichael Roth  */
ga_channel_dispatch(GSource * source,GSourceFunc unused,gpointer user_data)1517868e26eSMichael Roth static gboolean ga_channel_dispatch(GSource *source, GSourceFunc unused,
1527868e26eSMichael Roth                                     gpointer user_data)
1537868e26eSMichael Roth {
1547868e26eSMichael Roth     GAWatch *watch = (GAWatch *)source;
1557868e26eSMichael Roth     GAChannel *c = (GAChannel *)watch->channel;
1567868e26eSMichael Roth     GAChannelReadState *rs = &c->rstate;
1577868e26eSMichael Roth     gboolean success;
1587868e26eSMichael Roth 
1597868e26eSMichael Roth     g_debug("dispatch");
1607868e26eSMichael Roth     success = c->cb(watch->pollfd.revents, c->user_data);
1617868e26eSMichael Roth 
1627868e26eSMichael Roth     if (c->pending_events & G_IO_ERR) {
1637868e26eSMichael Roth         g_critical("channel error, removing source");
1647868e26eSMichael Roth         return false;
1657868e26eSMichael Roth     }
1667868e26eSMichael Roth 
1677868e26eSMichael Roth     /* TODO: replace rs->pending with watch->revents */
1687868e26eSMichael Roth     c->pending_events &= ~G_IO_HUP;
1697868e26eSMichael Roth     if (!rs->pending) {
1707868e26eSMichael Roth         c->pending_events &= ~G_IO_IN;
1717868e26eSMichael Roth     } else {
1727868e26eSMichael Roth         c->pending_events = 0;
1737868e26eSMichael Roth     }
1747868e26eSMichael Roth     return success;
1757868e26eSMichael Roth }
1767868e26eSMichael Roth 
ga_channel_finalize(GSource * source)1777868e26eSMichael Roth static void ga_channel_finalize(GSource *source)
1787868e26eSMichael Roth {
1797868e26eSMichael Roth     g_debug("finalize");
1807868e26eSMichael Roth }
1817868e26eSMichael Roth 
1827868e26eSMichael Roth GSourceFuncs ga_channel_watch_funcs = {
1837868e26eSMichael Roth     ga_channel_prepare,
1847868e26eSMichael Roth     ga_channel_check,
1857868e26eSMichael Roth     ga_channel_dispatch,
1867868e26eSMichael Roth     ga_channel_finalize
1877868e26eSMichael Roth };
1887868e26eSMichael Roth 
ga_channel_create_watch(GAChannel * c)1897868e26eSMichael Roth static GSource *ga_channel_create_watch(GAChannel *c)
1907868e26eSMichael Roth {
1917868e26eSMichael Roth     GSource *source = g_source_new(&ga_channel_watch_funcs, sizeof(GAWatch));
1927868e26eSMichael Roth     GAWatch *watch = (GAWatch *)source;
1937868e26eSMichael Roth 
1947868e26eSMichael Roth     watch->channel = c;
1957868e26eSMichael Roth     watch->pollfd.fd = (gintptr) c->rstate.ov.hEvent;
1967868e26eSMichael Roth     g_source_add_poll(source, &watch->pollfd);
1977868e26eSMichael Roth 
1987868e26eSMichael Roth     return source;
1997868e26eSMichael Roth }
2007868e26eSMichael Roth 
ga_channel_read(GAChannel * c,char * buf,size_t size,gsize * count)2017868e26eSMichael Roth GIOStatus ga_channel_read(GAChannel *c, char *buf, size_t size, gsize *count)
2027868e26eSMichael Roth {
2037868e26eSMichael Roth     GAChannelReadState *rs = &c->rstate;
2047868e26eSMichael Roth     GIOStatus status;
2057868e26eSMichael Roth     size_t to_read = 0;
2067868e26eSMichael Roth 
2077868e26eSMichael Roth     if (c->pending_events & G_IO_ERR) {
2087868e26eSMichael Roth         return G_IO_STATUS_ERROR;
2097868e26eSMichael Roth     }
2107868e26eSMichael Roth 
2117868e26eSMichael Roth     *count = to_read = MIN(size, rs->pending);
2127868e26eSMichael Roth     if (to_read) {
2137868e26eSMichael Roth         memcpy(buf, rs->buf + rs->cur, to_read);
2147868e26eSMichael Roth         rs->cur += to_read;
2157868e26eSMichael Roth         rs->pending -= to_read;
2167868e26eSMichael Roth         status = G_IO_STATUS_NORMAL;
2177868e26eSMichael Roth     } else {
2187868e26eSMichael Roth         status = G_IO_STATUS_AGAIN;
2197868e26eSMichael Roth     }
2207868e26eSMichael Roth 
2217868e26eSMichael Roth     return status;
2227868e26eSMichael Roth }
2237868e26eSMichael Roth 
ga_channel_write(GAChannel * c,const char * buf,size_t size,size_t * count)2247868e26eSMichael Roth static GIOStatus ga_channel_write(GAChannel *c, const char *buf, size_t size,
2257868e26eSMichael Roth                                   size_t *count)
2267868e26eSMichael Roth {
2277868e26eSMichael Roth     GIOStatus status;
2287868e26eSMichael Roth     OVERLAPPED ov = {0};
2297868e26eSMichael Roth     BOOL ret;
2307868e26eSMichael Roth     DWORD written;
2317868e26eSMichael Roth 
2327868e26eSMichael Roth     ov.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
2337868e26eSMichael Roth     ret = WriteFile(c->handle, buf, size, &written, &ov);
2347868e26eSMichael Roth     if (!ret) {
2357868e26eSMichael Roth         if (GetLastError() == ERROR_IO_PENDING) {
2367868e26eSMichael Roth             /* write is pending */
2377868e26eSMichael Roth             ret = GetOverlappedResult(c->handle, &ov, &written, TRUE);
2387868e26eSMichael Roth             if (!ret) {
2397868e26eSMichael Roth                 if (!GetLastError()) {
2407868e26eSMichael Roth                     status = G_IO_STATUS_AGAIN;
2417868e26eSMichael Roth                 } else {
2427868e26eSMichael Roth                     status = G_IO_STATUS_ERROR;
2437868e26eSMichael Roth                 }
2447868e26eSMichael Roth             } else {
2457868e26eSMichael Roth                 /* write is complete */
2467868e26eSMichael Roth                 status = G_IO_STATUS_NORMAL;
2477868e26eSMichael Roth                 *count = written;
2487868e26eSMichael Roth             }
2497868e26eSMichael Roth         } else {
2507868e26eSMichael Roth             status = G_IO_STATUS_ERROR;
2517868e26eSMichael Roth         }
2527868e26eSMichael Roth     } else {
2537868e26eSMichael Roth         /* write returned immediately */
2547868e26eSMichael Roth         status = G_IO_STATUS_NORMAL;
2557868e26eSMichael Roth         *count = written;
2567868e26eSMichael Roth     }
2577868e26eSMichael Roth 
258b71706d1SJeff Cody     if (ov.hEvent) {
259b71706d1SJeff Cody         CloseHandle(ov.hEvent);
260b71706d1SJeff Cody         ov.hEvent = NULL;
261b71706d1SJeff Cody     }
2627868e26eSMichael Roth     return status;
2637868e26eSMichael Roth }
2647868e26eSMichael Roth 
ga_channel_write_all(GAChannel * c,const char * buf,size_t size)2657868e26eSMichael Roth GIOStatus ga_channel_write_all(GAChannel *c, const char *buf, size_t size)
2667868e26eSMichael Roth {
267c7e775e4SDong Xu Wang     GIOStatus status = G_IO_STATUS_NORMAL;
268e853ea1cSMichael Roth     size_t count = 0;
2697868e26eSMichael Roth 
2707868e26eSMichael Roth     while (size) {
2717868e26eSMichael Roth         status = ga_channel_write(c, buf, size, &count);
2727868e26eSMichael Roth         if (status == G_IO_STATUS_NORMAL) {
2737868e26eSMichael Roth             size -= count;
2747868e26eSMichael Roth             buf += count;
2757868e26eSMichael Roth         } else if (status != G_IO_STATUS_AGAIN) {
2767868e26eSMichael Roth             break;
2777868e26eSMichael Roth         }
2787868e26eSMichael Roth     }
2797868e26eSMichael Roth 
2807868e26eSMichael Roth     return status;
2817868e26eSMichael Roth }
2827868e26eSMichael Roth 
ga_channel_open(GAChannel * c,GAChannelMethod method,const gchar * path)2837868e26eSMichael Roth static gboolean ga_channel_open(GAChannel *c, GAChannelMethod method,
2847868e26eSMichael Roth                                 const gchar *path)
2857868e26eSMichael Roth {
286a749f42dSMiki Mishael     COMMTIMEOUTS comTimeOut = {0};
287a749f42dSMiki Mishael     gchar newpath[MAXPATHLEN] = {0};
288a749f42dSMiki Mishael     comTimeOut.ReadIntervalTimeout = 1;
289a749f42dSMiki Mishael 
290a749f42dSMiki Mishael     if (method != GA_CHANNEL_VIRTIO_SERIAL && method != GA_CHANNEL_ISA_SERIAL) {
2917868e26eSMichael Roth         g_critical("unsupported communication method");
2927868e26eSMichael Roth         return false;
2937868e26eSMichael Roth     }
2947868e26eSMichael Roth 
295a749f42dSMiki Mishael     if (method == GA_CHANNEL_ISA_SERIAL) {
296a749f42dSMiki Mishael         snprintf(newpath, sizeof(newpath), "\\\\.\\%s", path);
297a749f42dSMiki Mishael     } else {
298a749f42dSMiki Mishael         g_strlcpy(newpath, path, sizeof(newpath));
299a749f42dSMiki Mishael     }
300a749f42dSMiki Mishael 
301a749f42dSMiki Mishael     c->handle = CreateFile(newpath, GENERIC_READ | GENERIC_WRITE, 0, NULL,
3027868e26eSMichael Roth                            OPEN_EXISTING,
3037868e26eSMichael Roth                            FILE_FLAG_NO_BUFFERING | FILE_FLAG_OVERLAPPED, NULL);
3047868e26eSMichael Roth     if (c->handle == INVALID_HANDLE_VALUE) {
305d1eddab8SPhilippe Mathieu-Daudé         g_autofree gchar *emsg = g_win32_error_message(GetLastError());
306d1eddab8SPhilippe Mathieu-Daudé         g_critical("error opening path %s: %s", newpath, emsg);
3077868e26eSMichael Roth         return false;
3087868e26eSMichael Roth     }
3097868e26eSMichael Roth 
310*0697e9edSAlexChen     if (method == GA_CHANNEL_ISA_SERIAL
311*0697e9edSAlexChen             && !SetCommTimeouts(c->handle, &comTimeOut)) {
312672db778SPhilippe Mathieu-Daudé         g_autofree gchar *emsg = g_win32_error_message(GetLastError());
313672db778SPhilippe Mathieu-Daudé         g_critical("error setting timeout for com port: %s", emsg);
314a749f42dSMiki Mishael         CloseHandle(c->handle);
315a749f42dSMiki Mishael         return false;
316a749f42dSMiki Mishael     }
317a749f42dSMiki Mishael 
3187868e26eSMichael Roth     return true;
3197868e26eSMichael Roth }
3207868e26eSMichael Roth 
ga_channel_new(GAChannelMethod method,const gchar * path,int listen_fd,GAChannelCallback cb,gpointer opaque)3217868e26eSMichael Roth GAChannel *ga_channel_new(GAChannelMethod method, const gchar *path,
32226de2296SStefan Hajnoczi                           int listen_fd, GAChannelCallback cb, gpointer opaque)
3237868e26eSMichael Roth {
324f3a06403SMarkus Armbruster     GAChannel *c = g_new0(GAChannel, 1);
3257868e26eSMichael Roth     SECURITY_ATTRIBUTES sec_attrs;
3267868e26eSMichael Roth 
3277868e26eSMichael Roth     if (!ga_channel_open(c, method, path)) {
3287868e26eSMichael Roth         g_critical("error opening channel");
3297868e26eSMichael Roth         g_free(c);
3307868e26eSMichael Roth         return NULL;
3317868e26eSMichael Roth     }
3327868e26eSMichael Roth 
3337868e26eSMichael Roth     c->cb = cb;
3347868e26eSMichael Roth     c->user_data = opaque;
3357868e26eSMichael Roth 
3367868e26eSMichael Roth     sec_attrs.nLength = sizeof(SECURITY_ATTRIBUTES);
3377868e26eSMichael Roth     sec_attrs.lpSecurityDescriptor = NULL;
3387868e26eSMichael Roth     sec_attrs.bInheritHandle = false;
3397868e26eSMichael Roth 
3407868e26eSMichael Roth     c->rstate.buf_size = QGA_READ_COUNT_DEFAULT;
3417868e26eSMichael Roth     c->rstate.buf = g_malloc(QGA_READ_COUNT_DEFAULT);
3427868e26eSMichael Roth     c->rstate.ov.hEvent = CreateEvent(&sec_attrs, FALSE, FALSE, NULL);
3437868e26eSMichael Roth 
3447868e26eSMichael Roth     c->source = ga_channel_create_watch(c);
3457868e26eSMichael Roth     g_source_attach(c->source, NULL);
3467868e26eSMichael Roth     return c;
3477868e26eSMichael Roth }
3487868e26eSMichael Roth 
ga_channel_free(GAChannel * c)3497868e26eSMichael Roth void ga_channel_free(GAChannel *c)
3507868e26eSMichael Roth {
3517868e26eSMichael Roth     if (c->source) {
3527868e26eSMichael Roth         g_source_destroy(c->source);
3537868e26eSMichael Roth     }
3547868e26eSMichael Roth     if (c->rstate.ov.hEvent) {
3557868e26eSMichael Roth         CloseHandle(c->rstate.ov.hEvent);
3567868e26eSMichael Roth     }
3577868e26eSMichael Roth     g_free(c->rstate.buf);
3587868e26eSMichael Roth     g_free(c);
3597868e26eSMichael Roth }
360