* openUBMC is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
* http://license.coscl.org.cn/MulanPSL2
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
*/
#include "common.h"
#include "transfer/spdm_dbus.h"
#include <dbus/dbus.h>
#include <glib.h>
static DBusConnection* g_dbus_conn = NULL;
static int32_t init_dbus_env(void)
{
#define DBUS_SESSION_BUS_ADDRESS "DBUS_SESSION_BUS_ADDRESS"
#define DBUS_SESSION_BUS_PID "DBUS_SESSION_BUS_PID"
#define DBUS_ENV_FILE_PATH "/dev/shm/dbus/.dbus"
#define ADDRESS_IDX 1
#define PID_IDX 2
if (g_getenv(DBUS_SESSION_BUS_ADDRESS) != NULL && g_getenv(DBUS_SESSION_BUS_PID) != NULL) {
return RET_OK;
}
gchar* env_content = NULL;
gsize file_len = 0;
GError* err = NULL;
if (g_file_get_contents(DBUS_ENV_FILE_PATH, &env_content, &file_len, &err) == FALSE) {
debug_log(DLOG_ERROR, "[spdm] get dbus env failed, error is %s", err->message);
g_error_free(err);
return RET_ERR;
}
GMatchInfo* match_info = NULL;
GRegex* regex = g_regex_new("DBUS_SESSION_BUS_ADDRESS=(.+?)\nDBUS_SESSION_BUS_PID=(\\d+)\n", G_REGEX_DEFAULT,
G_REGEX_MATCH_DEFAULT, NULL);
g_regex_match(regex, env_content, G_REGEX_MATCH_DEFAULT, &match_info);
if (g_match_info_matches(match_info) == FALSE) {
g_match_info_free(match_info);
g_regex_unref(regex);
g_free(env_content);
return RET_ERR;
}
gchar* address = g_match_info_fetch(match_info, ADDRESS_IDX);
gchar* pid = g_match_info_fetch(match_info, PID_IDX);
g_setenv(DBUS_SESSION_BUS_ADDRESS, address, 1);
g_setenv(DBUS_SESSION_BUS_PID, pid, 1);
g_free(address);
g_free(pid);
g_match_info_free(match_info);
g_regex_unref(regex);
g_free(env_content);
return RET_OK;
}
static int32_t init_dbus_connection(void)
{
DBusError error = {0};
if (g_dbus_conn != NULL) {
return RET_OK;
}
gint32 ret = init_dbus_env();
if (ret != RET_OK) {
return ret;
}
dbus_error_init(&error);
g_dbus_conn = dbus_bus_get_private(DBUS_BUS_SESSION, &error);
if (g_dbus_conn == NULL || dbus_error_is_set(&error)) {
debug_log(DLOG_ERROR, "[spdm] Open dbus session failed, Error name:%s, Error message:%s.", error.name,
error.message);
dbus_error_free(&error);
return RET_ERR;
}
dbus_connection_set_exit_on_disconnect(g_dbus_conn, FALSE);
return RET_OK;
}
static void append_user_context(DBusMessage* call, const gchar* user_name, const gchar* ip, const gchar* interface)
{
typedef struct {
const char* key;
const char* value;
} DBUS_STRINF_MAP;
DBUS_STRINF_MAP map[] = {{"Interface", interface}, {"UserName", user_name}, {"ClientAddr", ip}};
gsize i, n = sizeof(map) / sizeof(map[0]);
DBusMessageIter iter;
DBusMessageIter dict_iter;
DBusMessageIter entry_iter;
dbus_message_iter_init_append(call, &iter);
dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING
DBUS_TYPE_STRING_AS_STRING DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
&dict_iter);
for (i = 0; i < n; ++i) {
dbus_message_iter_open_container(&dict_iter, DBUS_TYPE_DICT_ENTRY, NULL, &entry_iter);
dbus_message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &map[i].key);
dbus_message_iter_append_basic(&entry_iter, DBUS_TYPE_STRING, &map[i].value);
dbus_message_iter_close_container(&dict_iter, &entry_iter);
}
dbus_message_iter_close_container(&iter, &dict_iter);
}
static int32_t dbus_call_transmit(const string& object_path, const uint8_t* request, size_t request_size,
uint8_t* response, size_t* response_size)
{
DBusMessage* call = dbus_message_new_method_call(COMPUTE_SERVICE, object_path.c_str(), HARDWARE_SPDM_INTERFACE,
SPDM_TRANSMIT_FUNCTION);
if (call == NULL) {
debug_log(DLOG_ERROR, "[spdm] Make new method call %s failed.", SPDM_TRANSMIT_FUNCTION);
return RET_ERR;
}
append_user_context(call, "SPDM", "N/A", "Busctl");
uint32_t timeout_ms = DEFAULT_TIMEOUT_MS;
if (dbus_message_append_args(call, DBUS_TYPE_UINT32, &request_size, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &request,
request_size, DBUS_TYPE_UINT32, &timeout_ms, DBUS_TYPE_INVALID) == false) {
dbus_message_unref(call);
debug_log(DLOG_ERROR, "[spdm] Append message failed.");
return RET_ERR;
}
uint8_t retry_times = 0;
DBusMessage* reply;
DBusError error = {0};
dbus_error_init(&error);
int32_t ret = RET_OK;
while (retry_times < MAX_RETRY_TIMES) {
retry_times++;
reply = dbus_connection_send_with_reply_and_block(g_dbus_conn, call, DEFAULT_TIMEOUT_MS, &error);
if (reply && !dbus_error_is_set(&error)) {
ret = RET_OK;
break;
}
debug_log(DLOG_ERROR, "[spdm] Dbus call failed: %s - %s", error.name, error.message);
if (strcmp(error.name, TIMEOUT_ERROR_MSG) == 0) {
dbus_error_free(&error);
ret = RET_ERR_TIMEOUT;
sleep(RETRY_WAIT_TIME);
continue;
} else if (strcmp(error.name, NOT_SUPPORT_ERROR_MSG) == 0) {
dbus_error_free(&error);
ret = RET_ERR_INVALID_CMD;
break;
}
dbus_error_free(&error);
ret = RET_ERR;
break;
}
dbus_message_unref(call);
if (ret != RET_OK) {
return ret;
}
DBusMessageIter reply_iter;
if (!dbus_message_iter_init(reply, &reply_iter)) {
debug_log(DLOG_ERROR, "[spdm] reply has no arguments");
dbus_message_unref(reply);
return RET_ERR;
}
if (dbus_message_iter_get_arg_type(&reply_iter) != DBUS_TYPE_ARRAY) {
debug_log(DLOG_ERROR, "[spdm] reply type mismatch, expected array, got %d",
dbus_message_iter_get_arg_type(&reply_iter));
dbus_message_unref(reply);
return RET_ERR;
}
DBusMessageIter reply_array_iter;
dbus_message_iter_recurse(&reply_iter, &reply_array_iter);
size_t rsp_len = 0;
while (dbus_message_iter_get_arg_type(&reply_array_iter) == DBUS_TYPE_BYTE) {
uint8_t byte;
dbus_message_iter_get_basic(&reply_array_iter, &byte);
if (rsp_len < *response_size) {
response[rsp_len] = byte;
}
rsp_len++;
dbus_message_iter_next(&reply_array_iter);
}
dbus_message_unref(reply);
if (rsp_len > *response_size) {
debug_log(DLOG_ERROR, "[spdm] response buffer too small: %zu > %zu", rsp_len, *response_size);
*response_size = 0;
return RET_ERR;
}
*response_size = rsp_len;
return RET_OK;
}
int32_t dbus_sync_send_recv(const string& object_path, const uint8_t* request, size_t request_size, uint8_t* response,
size_t* response_size)
{
gint32 ret = init_dbus_connection();
if (ret != RET_OK) {
debug_log(DLOG_ERROR, "[spdm] Init DBus failed, ret: %d", ret);
return ret;
}
return dbus_call_transmit(object_path, request, request_size, response, response_size);
}