diff options
m--------- | dtc | 0 | ||||
-rw-r--r-- | io/channel-websock.c | 264 | ||||
-rw-r--r-- | pc-bios/README | 2 | ||||
-rw-r--r-- | pc-bios/slof.bin | bin | 898232 -> 902120 bytes | |||
-rw-r--r-- | qemu-options.hx | 38 | ||||
m--------- | roms/SLOF | 0 |
6 files changed, 237 insertions, 67 deletions
diff --git a/dtc b/dtc -Subproject ec02b34c05be04f249ffaaca4b666f5246877de +Subproject fa8bc7f928ac25f23532afc8beb2073efc8fb06 diff --git a/io/channel-websock.c b/io/channel-websock.c index e47279a1ae..8fabadea2f 100644 --- a/io/channel-websock.c +++ b/io/channel-websock.c @@ -33,11 +33,16 @@ #define QIO_CHANNEL_WEBSOCK_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" #define QIO_CHANNEL_WEBSOCK_GUID_LEN strlen(QIO_CHANNEL_WEBSOCK_GUID) -#define QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL "Sec-WebSocket-Protocol" -#define QIO_CHANNEL_WEBSOCK_HEADER_VERSION "Sec-WebSocket-Version" -#define QIO_CHANNEL_WEBSOCK_HEADER_KEY "Sec-WebSocket-Key" +#define QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL "sec-websocket-protocol" +#define QIO_CHANNEL_WEBSOCK_HEADER_VERSION "sec-websocket-version" +#define QIO_CHANNEL_WEBSOCK_HEADER_KEY "sec-websocket-key" +#define QIO_CHANNEL_WEBSOCK_HEADER_UPGRADE "upgrade" +#define QIO_CHANNEL_WEBSOCK_HEADER_HOST "host" +#define QIO_CHANNEL_WEBSOCK_HEADER_CONNECTION "connection" #define QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY "binary" +#define QIO_CHANNEL_WEBSOCK_CONNECTION_UPGRADE "Upgrade" +#define QIO_CHANNEL_WEBSOCK_UPGRADE_WEBSOCKET "websocket" #define QIO_CHANNEL_WEBSOCK_HANDSHAKE_RESPONSE \ "HTTP/1.1 101 Switching Protocols\r\n" \ @@ -49,6 +54,9 @@ #define QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM "\r\n" #define QIO_CHANNEL_WEBSOCK_HANDSHAKE_END "\r\n\r\n" #define QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION "13" +#define QIO_CHANNEL_WEBSOCK_HTTP_METHOD "GET" +#define QIO_CHANNEL_WEBSOCK_HTTP_PATH "/" +#define QIO_CHANNEL_WEBSOCK_HTTP_VERSION "HTTP/1.1" /* The websockets packet header is variable length * depending on the size of the payload... */ @@ -99,6 +107,13 @@ struct QEMU_PACKED QIOChannelWebsockHeader { } u; }; +typedef struct QIOChannelWebsockHTTPHeader QIOChannelWebsockHTTPHeader; + +struct QIOChannelWebsockHTTPHeader { + char *name; + char *value; +}; + enum { QIO_CHANNEL_WEBSOCK_OPCODE_CONTINUATION = 0x0, QIO_CHANNEL_WEBSOCK_OPCODE_TEXT_FRAME = 0x1, @@ -108,25 +123,130 @@ enum { QIO_CHANNEL_WEBSOCK_OPCODE_PONG = 0xA }; -static char *qio_channel_websock_handshake_entry(const char *handshake, - size_t handshake_len, - const char *name) -{ - char *begin, *end, *ret = NULL; - char *line = g_strdup_printf("%s%s: ", - QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM, - name); - begin = g_strstr_len(handshake, handshake_len, line); - if (begin != NULL) { - begin += strlen(line); - end = g_strstr_len(begin, handshake_len - (begin - handshake), - QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM); - if (end != NULL) { - ret = g_strndup(begin, end - begin); +static size_t +qio_channel_websock_extract_headers(char *buffer, + QIOChannelWebsockHTTPHeader *hdrs, + size_t nhdrsalloc, + Error **errp) +{ + char *nl, *sep, *tmp; + size_t nhdrs = 0; + + /* + * First parse the HTTP protocol greeting of format: + * + * $METHOD $PATH $VERSION + * + * e.g. + * + * GET / HTTP/1.1 + */ + + nl = strstr(buffer, QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM); + if (!nl) { + error_setg(errp, "Missing HTTP header delimiter"); + return 0; + } + *nl = '\0'; + + tmp = strchr(buffer, ' '); + if (!tmp) { + error_setg(errp, "Missing HTTP path delimiter"); + return 0; + } + *tmp = '\0'; + + if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_METHOD)) { + error_setg(errp, "Unsupported HTTP method %s", buffer); + return 0; + } + + buffer = tmp + 1; + tmp = strchr(buffer, ' '); + if (!tmp) { + error_setg(errp, "Missing HTTP version delimiter"); + return 0; + } + *tmp = '\0'; + + if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_PATH)) { + error_setg(errp, "Unexpected HTTP path %s", buffer); + return 0; + } + + buffer = tmp + 1; + + if (!g_str_equal(buffer, QIO_CHANNEL_WEBSOCK_HTTP_VERSION)) { + error_setg(errp, "Unsupported HTTP version %s", buffer); + return 0; + } + + buffer = nl + strlen(QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM); + + /* + * Now parse all the header fields of format + * + * $NAME: $VALUE + * + * e.g. + * + * Cache-control: no-cache + */ + do { + QIOChannelWebsockHTTPHeader *hdr; + + nl = strstr(buffer, QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM); + if (nl) { + *nl = '\0'; + } + + sep = strchr(buffer, ':'); + if (!sep) { + error_setg(errp, "Malformed HTTP header"); + return 0; + } + *sep = '\0'; + sep++; + while (*sep == ' ') { + sep++; + } + + if (nhdrs >= nhdrsalloc) { + error_setg(errp, "Too many HTTP headers"); + return 0; + } + + hdr = &hdrs[nhdrs++]; + hdr->name = buffer; + hdr->value = sep; + + /* Canonicalize header name for easier identification later */ + for (tmp = hdr->name; *tmp; tmp++) { + *tmp = g_ascii_tolower(*tmp); + } + + if (nl) { + buffer = nl + strlen(QIO_CHANNEL_WEBSOCK_HANDSHAKE_DELIM); + } + } while (nl != NULL); + + return nhdrs; +} + +static const char * +qio_channel_websock_find_header(QIOChannelWebsockHTTPHeader *hdrs, + size_t nhdrs, + const char *name) +{ + size_t i; + + for (i = 0; i < nhdrs; i++) { + if (g_str_equal(hdrs[i].name, name)) { + return hdrs[i].value; } } - g_free(line); - return ret; + + return NULL; } @@ -166,58 +286,90 @@ static int qio_channel_websock_handshake_send_response(QIOChannelWebsock *ioc, } static int qio_channel_websock_handshake_process(QIOChannelWebsock *ioc, - const char *line, - size_t size, + char *buffer, Error **errp) { - int ret = -1; - char *protocols = qio_channel_websock_handshake_entry( - line, size, QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL); - char *version = qio_channel_websock_handshake_entry( - line, size, QIO_CHANNEL_WEBSOCK_HEADER_VERSION); - char *key = qio_channel_websock_handshake_entry( - line, size, QIO_CHANNEL_WEBSOCK_HEADER_KEY); + QIOChannelWebsockHTTPHeader hdrs[32]; + size_t nhdrs = G_N_ELEMENTS(hdrs); + const char *protocols = NULL, *version = NULL, *key = NULL, + *host = NULL, *connection = NULL, *upgrade = NULL; + nhdrs = qio_channel_websock_extract_headers(buffer, hdrs, nhdrs, errp); + if (!nhdrs) { + return -1; + } + + protocols = qio_channel_websock_find_header( + hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_PROTOCOL); if (!protocols) { error_setg(errp, "Missing websocket protocol header data"); - goto cleanup; + return -1; } + version = qio_channel_websock_find_header( + hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_VERSION); if (!version) { error_setg(errp, "Missing websocket version header data"); - goto cleanup; + return -1; } + key = qio_channel_websock_find_header( + hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_KEY); if (!key) { error_setg(errp, "Missing websocket key header data"); - goto cleanup; + return -1; + } + + host = qio_channel_websock_find_header( + hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_HOST); + if (!host) { + error_setg(errp, "Missing websocket host header data"); + return -1; + } + + connection = qio_channel_websock_find_header( + hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_CONNECTION); + if (!connection) { + error_setg(errp, "Missing websocket connection header data"); + return -1; + } + + upgrade = qio_channel_websock_find_header( + hdrs, nhdrs, QIO_CHANNEL_WEBSOCK_HEADER_UPGRADE); + if (!upgrade) { + error_setg(errp, "Missing websocket upgrade header data"); + return -1; } if (!g_strrstr(protocols, QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY)) { error_setg(errp, "No '%s' protocol is supported by client '%s'", QIO_CHANNEL_WEBSOCK_PROTOCOL_BINARY, protocols); - goto cleanup; + return -1; } if (!g_str_equal(version, QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION)) { error_setg(errp, "Version '%s' is not supported by client '%s'", QIO_CHANNEL_WEBSOCK_SUPPORTED_VERSION, version); - goto cleanup; + return -1; } if (strlen(key) != QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN) { error_setg(errp, "Key length '%zu' was not as expected '%d'", strlen(key), QIO_CHANNEL_WEBSOCK_CLIENT_KEY_LEN); - goto cleanup; + return -1; } - ret = qio_channel_websock_handshake_send_response(ioc, key, errp); + if (!g_strrstr(connection, QIO_CHANNEL_WEBSOCK_CONNECTION_UPGRADE)) { + error_setg(errp, "No connection upgrade requested '%s'", connection); + return -1; + } - cleanup: - g_free(protocols); - g_free(version); - g_free(key); - return ret; + if (!g_str_equal(upgrade, QIO_CHANNEL_WEBSOCK_UPGRADE_WEBSOCKET)) { + error_setg(errp, "Incorrect upgrade method '%s'", upgrade); + return -1; + } + + return qio_channel_websock_handshake_send_response(ioc, key, errp); } static int qio_channel_websock_handshake_read(QIOChannelWebsock *ioc, @@ -248,10 +400,10 @@ static int qio_channel_websock_handshake_read(QIOChannelWebsock *ioc, return 0; } } + *handshake_end = '\0'; if (qio_channel_websock_handshake_process(ioc, (char *)ioc->encinput.buffer, - ioc->encinput.offset, errp) < 0) { return -1; } @@ -570,21 +722,24 @@ static ssize_t qio_channel_websock_read_wire(QIOChannelWebsock *ioc, ioc->encinput.offset += ret; } - if (ioc->payload_remain == 0) { - ret = qio_channel_websock_decode_header(ioc, errp); + while (ioc->encinput.offset != 0) { + if (ioc->payload_remain == 0) { + ret = qio_channel_websock_decode_header(ioc, errp); + if (ret < 0) { + return ret; + } + if (ret == 0) { + ioc->io_eof = TRUE; + break; + } + } + + ret = qio_channel_websock_decode_payload(ioc, errp); if (ret < 0) { return ret; } - if (ret == 0) { - return 0; - } } - - ret = qio_channel_websock_decode_payload(ioc, errp); - if (ret < 0) { - return ret; - } - return ret; + return 1; } @@ -642,9 +797,6 @@ static gboolean qio_channel_websock_flush(QIOChannel *ioc, if (ret < 0) { goto cleanup; } - if (ret == 0) { - wioc->io_eof = TRUE; - } } cleanup: diff --git a/pc-bios/README b/pc-bios/README index 47a913f9c7..dcead369bf 100644 --- a/pc-bios/README +++ b/pc-bios/README @@ -17,7 +17,7 @@ - SLOF (Slimline Open Firmware) is a free IEEE 1275 Open Firmware implementation for certain IBM POWER hardware. The sources are at https://github.com/aik/SLOF, and the image currently in qemu is - built from git tag qemu-slof-20161019. + built from git tag qemu-slof-20170303. - sgabios (the Serial Graphics Adapter option ROM) provides a means for legacy x86 software to communicate with an attached serial console as diff --git a/pc-bios/slof.bin b/pc-bios/slof.bin Binary files differindex 30ce7ac384..0408723dbf 100644 --- a/pc-bios/slof.bin +++ b/pc-bios/slof.bin diff --git a/qemu-options.hx b/qemu-options.hx index c85f77d1d8..229243831b 100644 --- a/qemu-options.hx +++ b/qemu-options.hx @@ -144,16 +144,34 @@ STEXI @item -numa node[,mem=@var{size}][,cpus=@var{firstcpu}[-@var{lastcpu}]][,nodeid=@var{node}] @itemx -numa node[,memdev=@var{id}][,cpus=@var{firstcpu}[-@var{lastcpu}]][,nodeid=@var{node}] @findex -numa -Simulate a multi node NUMA system. If @samp{mem}, @samp{memdev} -and @samp{cpus} are omitted, resources are split equally. Also, note -that the -@option{numa} option doesn't allocate any of the specified -resources. That is, it just assigns existing resources to NUMA nodes. This -means that one still has to use the @option{-m}, @option{-smp} options -to allocate RAM and VCPUs respectively, and possibly @option{-object} -to specify the memory backend for the @samp{memdev} suboption. - -@samp{mem} and @samp{memdev} are mutually exclusive. Furthermore, if one -node uses @samp{memdev}, all of them have to use it. +Define a NUMA node and assign RAM and VCPUs to it. + +@var{firstcpu} and @var{lastcpu} are CPU indexes. Each +@samp{cpus} option represent a contiguous range of CPU indexes +(or a single VCPU if @var{lastcpu} is omitted). A non-contiguous +set of VCPUs can be represented by providing multiple @samp{cpus} +options. If @samp{cpus} is omitted on all nodes, VCPUs are automatically +split between them. + +For example, the following option assigns VCPUs 0, 1, 2 and 5 to +a NUMA node: +@example +-numa node,cpus=0-2,cpus=5 +@end example + +@samp{mem} assigns a given RAM amount to a node. @samp{memdev} +assigns RAM from a given memory backend device to a node. If +@samp{mem} and @samp{memdev} are omitted in all nodes, RAM is +split equally between them. + +@samp{mem} and @samp{memdev} are mutually exclusive. Furthermore, +if one node uses @samp{memdev}, all of them have to use it. + +Note that the -@option{numa} option doesn't allocate any of the +specified resources, it just assigns existing resources to NUMA +nodes. This means that one still has to use the @option{-m}, +@option{-smp} options to allocate RAM and VCPUs respectively. + ETEXI DEF("add-fd", HAS_ARG, QEMU_OPTION_add_fd, diff --git a/roms/SLOF b/roms/SLOF -Subproject efd65f49929d7db775b26066d538c8120ae3db9 +Subproject 66d250ef0fd06bb88b7399b9563b5008201f2d6 |