diff --git a/core/opal-msg.c b/core/opal-msg.c index 907a9e0af340..af1ec7d000d5 100644 --- a/core/opal-msg.c +++ b/core/opal-msg.c @@ -25,6 +25,7 @@ struct opal_msg_entry { struct list_node link; void (*consumed)(void *data, int status); + bool extended; void *data; struct opal_msg msg; }; @@ -39,37 +40,47 @@ int _opal_queue_msg(enum opal_msg_type msg_type, void *data, size_t params_size, const void *params) { struct opal_msg_entry *entry; + uint64_t entry_size; + + if ((params_size + OPAL_MSG_HDR_SIZE) > OPAL_MSG_SIZE) { + prlog(PR_DEBUG, "param_size (0x%x) > opal_msg param size (0x%x)\n", + (u32)params_size, (u32)(OPAL_MSG_SIZE - OPAL_MSG_HDR_SIZE)); + return OPAL_PARAMETER; + } lock(&opal_msg_lock); - entry = list_pop(&msg_free_list, struct opal_msg_entry, link); - if (!entry) { - prerror("No available node in the free list, allocating\n"); - entry = zalloc(sizeof(struct opal_msg_entry)); + if (params_size > OPAL_MSG_FIXED_PARAMS_SIZE) { + entry_size = sizeof(struct opal_msg_entry) + params_size; + entry_size -= OPAL_MSG_FIXED_PARAMS_SIZE; + entry = zalloc(entry_size); + if (entry) + entry->extended = true; + } else { + entry = list_pop(&msg_free_list, struct opal_msg_entry, link); if (!entry) { - prerror("Allocation failed\n"); - unlock(&opal_msg_lock); - return OPAL_RESOURCE; + prerror("No available node in the free list, allocating\n"); + entry = zalloc(sizeof(struct opal_msg_entry)); } } + if (!entry) { + prerror("Allocation failed\n"); + unlock(&opal_msg_lock); + return OPAL_RESOURCE; + } entry->consumed = consumed; entry->data = data; entry->msg.msg_type = cpu_to_be32(msg_type); - - if (params_size > OPAL_MSG_FIXED_PARAMS_SIZE) { - prerror("Discarding extra parameters\n"); - params_size = OPAL_MSG_FIXED_PARAMS_SIZE; - } + entry->msg.size = cpu_to_be32(params_size); memcpy(entry->msg.params, params, params_size); list_add_tail(&msg_pending_list, &entry->link); opal_update_pending_evt(OPAL_EVENT_MSG_PENDING, OPAL_EVENT_MSG_PENDING); - unlock(&opal_msg_lock); - return 0; + return OPAL_SUCCESS; } static int64_t opal_get_msg(uint64_t *buffer, uint64_t size) @@ -77,6 +88,8 @@ static int64_t opal_get_msg(uint64_t *buffer, uint64_t size) struct opal_msg_entry *entry; void (*callback)(void *data, int status); void *data; + uint64_t msg_size; + int rc = OPAL_SUCCESS; if (size < sizeof(struct opal_msg) || !buffer) return OPAL_PARAMETER; @@ -92,20 +105,37 @@ static int64_t opal_get_msg(uint64_t *buffer, uint64_t size) return OPAL_RESOURCE; } - memcpy(buffer, &entry->msg, sizeof(entry->msg)); + msg_size = OPAL_MSG_HDR_SIZE + be32_to_cpu(entry->msg.size); + if (size < msg_size) { + /* Send partial data to Linux */ + prlog(PR_NOTICE, "Sending partial data [msg_type : 0x%x, " + "msg_size : 0x%x, buf_size : 0x%x]\n", + be32_to_cpu(entry->msg.msg_type), + (u32)msg_size, (u32)size); + + entry->msg.size = cpu_to_be32(size - OPAL_MSG_HDR_SIZE); + msg_size = size; + rc = OPAL_PARTIAL; + } + + memcpy((void *)buffer, (void *)&entry->msg, msg_size); callback = entry->consumed; data = entry->data; - list_add(&msg_free_list, &entry->link); + if (entry->extended) + free(entry); + else + list_add(&msg_free_list, &entry->link); + if (list_empty(&msg_pending_list)) opal_update_pending_evt(OPAL_EVENT_MSG_PENDING, 0); unlock(&opal_msg_lock); if (callback) - callback(data, OPAL_SUCCESS); + callback(data, rc); - return OPAL_SUCCESS; + return rc; } opal_call(OPAL_GET_MSG, opal_get_msg, 2); diff --git a/core/test/run-msg.c b/core/test/run-msg.c index 08e1a019bc09..f5f948acee36 100644 --- a/core/test/run-msg.c +++ b/core/test/run-msg.c @@ -132,13 +132,13 @@ int main(void) assert(r == 0); assert(list_count(&msg_pending_list) == ++npending); - assert(list_count(&msg_free_list) == --nfree); + assert(list_count(&msg_free_list) == nfree); r = opal_get_msg(m_ptr, sizeof(m)); - assert(r == 0); + assert(r == OPAL_PARTIAL); assert(list_count(&msg_pending_list) == --npending); - assert(list_count(&msg_free_list) == ++nfree); + assert(list_count(&msg_free_list) == nfree); assert(m.params[0] == 0); assert(m.params[1] == 1); diff --git a/doc/opal-api/opal-messages.rst b/doc/opal-api/opal-messages.rst index 7843b697c007..c7b2e5c8c9a0 100644 --- a/doc/opal-api/opal-messages.rst +++ b/doc/opal-api/opal-messages.rst @@ -11,7 +11,7 @@ An opal_msg is: :: struct opal_msg { __be32 msg_type; - __be32 reserved; + __be32 size; __be64 params[8]; }; @@ -21,8 +21,9 @@ define all eight parameters, the value in the undefined parameters is undefined, although can safely be memcpy()d or otherwise moved. In the device tree, there's an opal-msg-size property of the OPAL node that -says the size of a struct opal-msg. In the future, OPAL may support larger -messages. See ``OPAL_GET_MESSAGE`` documentation for details. +says the size of a struct opal-msg. Kernel will use this property to allocate +memory for opal_msg structure. See ``OPAL_GET_MESSAGE`` documentation for +details. :: ibm,opal { diff --git a/include/opal-api.h b/include/opal-api.h index 2853fa86079e..008ce1092238 100644 --- a/include/opal-api.h +++ b/include/opal-api.h @@ -558,7 +558,7 @@ enum opal_msg_type { struct opal_msg { __be32 msg_type; - __be32 reserved; + __be32 size; __be64 params[8]; };