| // Copyright 2015 The Android Open Source Project |
| // |
| // This software is licensed under the terms of the GNU General Public |
| // License version 2, as published by the Free Software Foundation, and |
| // may be copied, distributed, and modified under those terms. |
| // |
| // This program is distributed in the hope that it will be useful, |
| // but WITHOUT ANY WARRANTY; without even the implied warranty of |
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| // GNU General Public License for more details. |
| |
| #include "android/emulation/android_qemud.h" |
| |
| #include "android/utils/looper.h" |
| #include "android/utils/misc.h" |
| #include "android/utils/panic.h" |
| |
| #include "android/emulation/android_pipe_host.h" |
| #include "android/emulation/qemud/android_qemud_client.h" |
| #include "android/emulation/qemud/android_qemud_common.h" |
| #include "android/emulation/qemud/android_qemud_multiplexer.h" |
| #include "android/emulation/qemud/android_qemud_service.h" |
| |
| #include <assert.h> |
| #include <stdlib.h> |
| |
| /* Initializes QEMUD serial interface. |
| */ |
| static void _android_qemud_serial_init(CSerialLine* sl) { |
| // guard against double initialization |
| static CSerialLine* qemud_serial_line = NULL; |
| |
| if (qemud_serial_line != NULL) { |
| return; |
| } |
| |
| qemud_serial_line = sl; |
| qemud_multiplexer_init(qemud_multiplexer, sl); |
| } |
| |
| /*------------------------------------------------------------------------------ |
| * |
| * QEMUD PIPE service callbacks |
| * |
| * ----------------------------------------------------------------------------*/ |
| |
| /* Saves pending pipe message to the snapshot file. */ |
| static void _save_pipe_message(Stream* f, QemudPipeMessage* msg) { |
| stream_put_be32(f, msg->size); |
| stream_put_be32(f, msg->offset); |
| stream_write(f, msg->message, msg->size); |
| } |
| |
| /* Loads pending pipe messages from the snapshot file. |
| * Return: |
| * List of pending pipe messages loaded from snapshot, or NULL if snapshot didn't |
| * contain saved messages. |
| */ |
| static QemudPipeMessage* _load_pipe_message(Stream* f, QemudPipeMessage** last) { |
| QemudPipeMessage* ret = NULL; |
| QemudPipeMessage** next = &ret; |
| *last = NULL; |
| |
| uint32_t size = stream_get_be32(f); |
| while (size != 0) { |
| QemudPipeMessage* wrk = |
| static_cast<QemudPipeMessage*>(android_alloc0(sizeof(*wrk))); |
| *last = *next = wrk; |
| wrk->size = size; |
| wrk->offset = stream_get_be32(f); |
| wrk->message = static_cast<uint8_t*>(malloc(wrk->size)); |
| if (wrk->message == NULL) { |
| APANIC("Unable to allocate buffer for pipe's pending message."); |
| } |
| stream_read(f, wrk->message, wrk->size); |
| next = &wrk->next; |
| *next = NULL; |
| size = stream_get_be32(f); |
| } |
| |
| return ret; |
| } |
| |
| /* This is a callback that gets invoked when guest is connecting to the service. |
| * |
| * Here we will create a new client as well as pipe descriptor representing new |
| * connection. |
| */ |
| static void* |
| _qemudPipe_init(void* hwpipe, void* _looper, const char* args) { |
| QemudMultiplexer* m = qemud_multiplexer; |
| QemudService* sv = m->services; |
| QemudClient* client; |
| QemudPipe* pipe = NULL; |
| char service_name[512]; |
| const char* client_args; |
| size_t srv_name_len; |
| |
| /* 'args' passed in this callback represents name of the service the guest is |
| * connecting to. It can't be NULL. */ |
| if (args == NULL) { |
| D("%s: Missing address!", __FUNCTION__); |
| return NULL; |
| } |
| |
| /* 'args' contain service name, and optional parameters for the client that |
| * is about to be created in this call. The parameters are separated from the |
| * service name wit ':'. Separate service name from the client param. */ |
| client_args = strchr(args, ':'); |
| if (client_args != NULL) { |
| srv_name_len = min(client_args - args, (intptr_t) sizeof(service_name) - 1); |
| client_args++; // Past the ':' |
| if (*client_args == '\0') { |
| /* No actual parameters. */ |
| client_args = NULL; |
| } |
| } else { |
| srv_name_len = min(strlen(args), sizeof(service_name) - 1); |
| } |
| memcpy(service_name, args, srv_name_len); |
| service_name[srv_name_len] = '\0'; |
| |
| /* Lookup registered service by its name. */ |
| while (sv != NULL && strcmp(sv->name, service_name)) { |
| sv = sv->next; |
| } |
| if (sv == NULL) { |
| D("%s: Service '%s' has not been registered!", __FUNCTION__, service_name); |
| return NULL; |
| } |
| |
| /* Create a client for this connection. -1 as a channel ID signals that this |
| * is a pipe client. */ |
| client = qemud_service_connect_client(sv, -1, client_args); |
| if (client != NULL) { |
| pipe = static_cast<QemudPipe*>(android_alloc0(sizeof(*pipe))); |
| pipe->hwpipe = hwpipe; |
| pipe->looper = _looper; |
| pipe->service = sv; |
| pipe->client = client; |
| client->ProtocolSelector.Pipe.qemud_pipe = pipe; |
| } |
| |
| return pipe; |
| } |
| |
| /* Called when the guest wants to close the channel. |
| */ |
| static void |
| _qemudPipe_closeFromGuest(void* opaque) { |
| QemudPipe* pipe = static_cast<QemudPipe*>(opaque); |
| QemudClient* client = pipe->client; |
| D("%s", __FUNCTION__); |
| if (client != NULL) { |
| qemud_client_disconnect(client, 1); |
| } else { |
| D("%s: Unexpected NULL client", __FUNCTION__); |
| } |
| } |
| |
| /* Called when the guest has sent some data to the client. |
| */ |
| static int |
| _qemudPipe_sendBuffers(void* opaque, |
| const AndroidPipeBuffer* buffers, |
| int numBuffers) { |
| QemudPipe* pipe = static_cast<QemudPipe*>(opaque); |
| QemudClient* client = pipe->client; |
| size_t transferred = 0; |
| |
| if (client == NULL) { |
| D("%s: Unexpected NULL client", __FUNCTION__); |
| return -1; |
| } |
| |
| if (numBuffers == 1) { |
| /* Simple case: all data are in one buffer. */ |
| D("%s: %s", __FUNCTION__, quote_bytes((char*) buffers->data, buffers->size)); |
| qemud_client_recv(client, buffers->data, buffers->size); |
| transferred = buffers->size; |
| } else { |
| /* If there are multiple buffers involved, collect all data in one buffer |
| * before calling the high level client. */ |
| uint8_t* msg, * wrk; |
| int n; |
| for (n = 0; n < numBuffers; n++) { |
| transferred += buffers[n].size; |
| } |
| msg = static_cast<uint8_t*>(malloc(transferred)); |
| wrk = msg; |
| for (n = 0; n < numBuffers; n++) { |
| memcpy(wrk, buffers[n].data, buffers[n].size); |
| wrk += buffers[n].size; |
| } |
| D("%s: %s", __FUNCTION__, quote_bytes((char*) msg, transferred)); |
| qemud_client_recv(client, msg, transferred); |
| free(msg); |
| } |
| |
| return transferred; |
| } |
| |
| /* Called when the guest is reading data from the client. |
| */ |
| static int |
| _qemudPipe_recvBuffers(void* opaque, AndroidPipeBuffer* buffers, int numBuffers) { |
| QemudPipe* pipe = static_cast<QemudPipe*>(opaque); |
| QemudClient* client = pipe->client; |
| QemudPipeMessage** msg_list; |
| AndroidPipeBuffer* buff = buffers; |
| AndroidPipeBuffer* endbuff = buffers + numBuffers; |
| size_t sent_bytes = 0; |
| size_t off_in_buff = 0; |
| |
| if (client == NULL) { |
| D("%s: Unexpected NULL client", __FUNCTION__); |
| return -1; |
| } |
| |
| msg_list = &client->ProtocolSelector.Pipe.messages; |
| if (*msg_list == NULL) { |
| /* No data to send. Let it block until we wake it up with |
| * PIPE_WAKE_READ when service sends data to the client. */ |
| return PIPE_ERROR_AGAIN; |
| } |
| |
| /* Fill in goldfish buffers while they are still available, and there are |
| * messages in the client's message list. */ |
| while (buff != endbuff && *msg_list != NULL) { |
| QemudPipeMessage* msg = *msg_list; |
| /* Message data fiting the current pipe's buffer. */ |
| size_t to_copy = min(msg->size - msg->offset, buff->size - off_in_buff); |
| memcpy(buff->data + off_in_buff, msg->message + msg->offset, to_copy); |
| /* Update offsets. */ |
| off_in_buff += to_copy; |
| msg->offset += to_copy; |
| sent_bytes += to_copy; |
| if (msg->size == msg->offset) { |
| /* We're done with the current message. Go to the next one. */ |
| *msg_list = msg->next; |
| free(msg); |
| } |
| if (off_in_buff == buff->size) { |
| /* Current pipe buffer is full. Continue with the next one. */ |
| buff++; |
| off_in_buff = 0; |
| } |
| } |
| |
| if (!*msg_list) { |
| client->ProtocolSelector.Pipe.last_msg = NULL; |
| } |
| |
| D("%s: -> %u (of %u)", __FUNCTION__, sent_bytes, buffers->size); |
| |
| return sent_bytes; |
| } |
| |
| static unsigned |
| _qemudPipe_poll(void* opaque) { |
| QemudPipe* pipe = static_cast<QemudPipe*>(opaque); |
| QemudClient* client = pipe->client; |
| unsigned ret = 0; |
| |
| if (client != NULL) { |
| ret |= PIPE_POLL_OUT; |
| if (client->ProtocolSelector.Pipe.messages != NULL) { |
| ret |= PIPE_POLL_IN; |
| } |
| } else { |
| D("%s: Unexpected NULL client", __FUNCTION__); |
| } |
| |
| return ret; |
| } |
| |
| static void |
| _qemudPipe_wakeOn(void* opaque, int flags) { |
| QemudPipe* qemud_pipe = (QemudPipe*) opaque; |
| QemudClient* c = qemud_pipe->client; |
| D("%s: -> %X", __FUNCTION__, flags); |
| if (flags & PIPE_WAKE_READ) { |
| if (c->ProtocolSelector.Pipe.messages != NULL) { |
| android_pipe_host_signal_wake( |
| c->ProtocolSelector.Pipe.qemud_pipe->hwpipe, |
| PIPE_WAKE_READ); |
| } |
| } |
| } |
| |
| static void _qemudPipe_save(void* opaque, Stream* f) { |
| QemudPipe* qemud_pipe = (QemudPipe*) opaque; |
| QemudClient* c = qemud_pipe->client; |
| QemudPipeMessage* msg = c->ProtocolSelector.Pipe.messages; |
| |
| /* save generic information */ |
| qemud_service_save_name(f, c->service); |
| stream_put_string(f, c->param); |
| |
| /* Save pending messages. */ |
| while (msg != NULL) { |
| _save_pipe_message(f, msg); |
| msg = msg->next; |
| } |
| /* End of pending messages. */ |
| stream_put_be32(f, 0); |
| |
| /* save client-specific state */ |
| if (c->clie_save) |
| c->clie_save(f, c, c->clie_opaque); |
| |
| /* save framing configuration */ |
| stream_put_be32(f, c->framing); |
| if (c->framing) { |
| stream_put_be32(f, c->need_header); |
| /* header sink always connected to c->header0, no need to save */ |
| stream_put_be32(f, FRAME_HEADER_SIZE); |
| stream_write(f, c->header0, FRAME_HEADER_SIZE); |
| /* payload sink */ |
| qemud_sink_save(f, c->payload); |
| stream_write(f, c->payload->buff, c->payload->size); |
| } |
| } |
| |
| static void* _qemudPipe_load(void* hwpipe, |
| void* pipeOpaque, |
| const char* args, |
| Stream* f) { |
| QemudPipe* qemud_pipe = NULL; |
| char* param; |
| char* service_name = qemud_service_load_name(f); |
| if (service_name == NULL) |
| return NULL; |
| /* get service instance for the loading client*/ |
| QemudService* sv = qemud_service_find(qemud_multiplexer->services, |
| service_name); |
| if (sv == NULL) { |
| D("%s: load failed: unknown service \"%s\"\n", |
| __FUNCTION__, service_name); |
| return NULL; |
| } |
| |
| /* Load saved parameters. */ |
| param = stream_get_string(f); |
| |
| /* re-connect client */ |
| QemudClient* c = qemud_service_connect_client(sv, -1, param); |
| if (c == NULL) |
| return NULL; |
| |
| /* Load pending messages. */ |
| c->ProtocolSelector.Pipe.messages = |
| _load_pipe_message(f, &c->ProtocolSelector.Pipe.last_msg); |
| |
| /* load client-specific state */ |
| if (c->clie_load && c->clie_load(f, c, c->clie_opaque)) { |
| /* load failure */ |
| return NULL; |
| } |
| |
| /* load framing configuration */ |
| c->framing = stream_get_be32(f); |
| if (c->framing) { |
| |
| /* header buffer */ |
| c->need_header = stream_get_be32(f); |
| int header_size = stream_get_be32(f); |
| if (header_size > FRAME_HEADER_SIZE) { |
| D("%s: load failed: payload buffer requires %d bytes, %d available\n", |
| __FUNCTION__, header_size, FRAME_HEADER_SIZE); |
| return NULL; |
| } |
| int ret; |
| if ((ret = stream_read(f, c->header0, header_size)) != header_size) { |
| D("%s: frame header buffer load failed: expected %d bytes, got %d\n", |
| __FUNCTION__, header_size, ret); |
| return NULL; |
| } |
| |
| /* payload sink */ |
| if ((ret = qemud_sink_load(f, c->payload))) |
| return NULL; |
| |
| /* replace payload buffer by saved data */ |
| if (c->payload->buff) { |
| AFREE(c->payload->buff); |
| } |
| |
| /* +1 for terminating zero */ |
| c->payload->buff = static_cast<uint8_t*>( |
| _android_array_alloc(sizeof(*c->payload->buff), c->payload->size + 1)); |
| if ((ret = stream_read(f, c->payload->buff, c->payload->size)) != |
| c->payload->size) { |
| D("%s: frame payload buffer load failed: expected %d bytes, got %d\n", |
| __FUNCTION__, c->payload->size, ret); |
| AFREE(c->payload->buff); |
| return NULL; |
| } |
| } |
| |
| /* Associate the client with the pipe. */ |
| qemud_pipe = static_cast<QemudPipe*>(android_alloc0(sizeof(*qemud_pipe))); |
| qemud_pipe->hwpipe = hwpipe; |
| qemud_pipe->looper = pipeOpaque; |
| qemud_pipe->service = sv; |
| qemud_pipe->client = c; |
| c->ProtocolSelector.Pipe.qemud_pipe = qemud_pipe; |
| |
| return qemud_pipe; |
| } |
| |
| /* QEMUD pipe functions. |
| */ |
| static const AndroidPipeFuncs _qemudPipe_funcs = { |
| _qemudPipe_init, |
| _qemudPipe_closeFromGuest, |
| _qemudPipe_sendBuffers, |
| _qemudPipe_recvBuffers, |
| _qemudPipe_poll, |
| _qemudPipe_wakeOn, |
| _qemudPipe_save, |
| _qemudPipe_load, |
| }; |
| |
| /* Initializes QEMUD pipe interface. |
| */ |
| static void _android_qemud_pipe_init(void) { |
| static bool _qemud_pipe_initialized = false; |
| |
| if (!_qemud_pipe_initialized) { |
| android_pipe_add_type("qemud", looper_getForThread(), &_qemudPipe_funcs); |
| _qemud_pipe_initialized = true; |
| } |
| } |
| |
| static bool isInited = false; |
| |
| void android_qemud_init(CSerialLine* sl) { |
| D("%s", __FUNCTION__); |
| /* We don't know in advance whether the guest system supports qemud pipes, |
| * so we will initialize both qemud machineries, the legacy (over serial |
| * port), and the new one (over qemu pipe). Then we let the guest to connect |
| * via one, or the other. */ |
| _android_qemud_serial_init(sl); |
| _android_qemud_pipe_init(); |
| isInited = true; |
| } |
| |
| QemudService* qemud_service_register(const char* service_name, |
| int max_clients, |
| void* serv_opaque, |
| QemudServiceConnect serv_connect, |
| QemudServiceSave serv_save, |
| QemudServiceLoad serv_load) { |
| assert(isInited); |
| |
| QemudService* const sv = |
| qemud_service_new(service_name, |
| max_clients, |
| serv_opaque, |
| serv_connect, |
| serv_save, |
| serv_load, |
| &qemud_multiplexer->services); |
| D("Registered QEMUD service %s", service_name); |
| return sv; |
| } |