diff --git a/src/mctpd.c b/src/mctpd.c index 88a8506..2bfb69c 100644 --- a/src/mctpd.c +++ b/src/mctpd.c @@ -38,6 +38,9 @@ #define MCTP_DBUS_IFACE "xyz.openbmc_project.MCTP" #define MCTP_DBUS_IFACE_ENDPOINT "xyz.openbmc_project.MCTP.Endpoint" #define OPENBMC_IFACE_COMMON_UUID "xyz.openbmc_project.Common.UUID" +#define OPENBMC_MCTP_CONFIG_IFACE "xyz.openbmc_project.Configuration.MCTPEndpoint" +#define OPENBMC_ASSOCIATION_IFACE "xyz.openbmc_project.Association.Definitions" +#define OPENBMC_ENTITY_MANAGER "xyz.openbmc_project.EntityManager" // an arbitrary constant for use with sd_id128_get_machine_app_specific() static const char* mctpd_appid = "67369c05-4b97-4b7e-be72-65cfd8639f10"; @@ -71,6 +74,13 @@ struct ctx; // all local peers have the same phys static const dest_phys local_phys = { .ifindex = 0 }; +struct association { + char *forward; + char *reverse; + char *object_path; +}; +typedef struct association association; + struct peer { int net; mctp_eid_t eid; @@ -110,6 +120,9 @@ struct peer { // From Get Endpoint UUID. A malloced 16 bytes */ uint8_t *uuid; + // the association is an struct of (forward, reverse, object_path) + association *association; + // Stuff the ctx pointer into peer for tidier parameter passing struct ctx *ctx; }; @@ -214,6 +227,18 @@ static peer * find_peer_by_addr(ctx *ctx, mctp_eid_t eid, int net) return NULL; } +static peer * find_peer_by_association(ctx *ctx, const char *object_path) +{ + for (size_t i = 0; i < ctx->size_peers; i++) { + peer *peer = &ctx->peers[i]; + if (peer->state != REMOTE) + continue; + if (strcmp(peer->association->object_path, object_path) == 0) + return peer; + } + return NULL; +} + /* Returns a deferred free pointer */ static const char* dest_phys_tostr(const dest_phys *dest) { @@ -1243,6 +1268,12 @@ static int remove_peer(peer *peer) n->peeridx[peer->eid] = -1; free(peer->message_types); free(peer->uuid); + if (peer->association) { + free(peer->association->forward); + free(peer->association->reverse); + free(peer->association->object_path); + free(peer->association); + } memset(peer, 0x0, sizeof(struct peer)); return 0; } @@ -1666,6 +1697,164 @@ static int message_read_hwaddr(sd_bus_message *call, dest_phys* dest) return 0; } +static int setup_static_eid(ctx *ctx) +{ + int rc; + sd_bus_error error = SD_BUS_ERROR_NULL; + sd_bus_message *message = NULL; + char **s; + + rc = sd_bus_call_method(ctx->bus, + "xyz.openbmc_project.ObjectMapper", "/xyz/openbmc_project/object_mapper", + "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", + &error, &message, "sias", "/xyz/openbmc_project/", 0, 1, OPENBMC_MCTP_CONFIG_IFACE); + if (rc < 0) { + warnx("Failed to issue method call: %s\n", error.message); + goto out; + } + + sd_bus_message_read_strv(message, &s); + message = sd_bus_message_unref(message); + + // check if there is configuration found + if (!s) + goto out; + + + int i = 0; + size_t size = 0; + dest_phys desti = {0}, *dest = &desti; + peer *peer = NULL; + int net; + struct sockaddr_mctp_ext addr; + struct mctp_ctrl_cmd_get_eid req = {0}; + struct mctp_ctrl_resp_get_eid *resp = NULL; + char *peer_path = NULL; + + req.ctrl_hdr.rq_dgram_inst = RQDI_REQ; + req.ctrl_hdr.command_code = MCTP_CTRL_CMD_GET_ENDPOINT_ID; + + while(s[i] != NULL) { + rc = sd_bus_get_property(ctx->bus, + OPENBMC_ENTITY_MANAGER, s[i], + OPENBMC_MCTP_CONFIG_IFACE, "Address", + &error, &message, "t"); + if (rc < 0) { + warnx("Failed to get property: %s\n", error.message); + goto out; + } + + const uint64_t data = 0; + sd_bus_message_read(message, "t", &data); + + size = sizeof(uint8_t); + memset(dest->hwaddr, 0x0, MAX_ADDR_LEN); + memcpy(dest->hwaddr, &data, size); + dest->hwaddr_len = size; + + message = sd_bus_message_unref(message); + + rc = sd_bus_get_property(ctx->bus, + OPENBMC_ENTITY_MANAGER, s[i], + OPENBMC_MCTP_CONFIG_IFACE, "Bus", + &error, &message, "t"); + if (rc < 0) { + warnx("Failed to get property: %s", error.message); + goto out; + } + uint64_t bus = 0; + char *ifname = NULL; + sd_bus_message_read(message, "t", &bus); + ifname = malloc(11); // length for "mctpi2cxxx" + snprintf(ifname, 11, "mctpi2c%d", (uint8_t)bus); + dfree(ifname); + + dest->ifindex = mctp_nl_ifindex_byname(ctx->nl, ifname); + if (dest->ifindex <= 0) { + warnx("Unknown ifname: %s", ifname); + i++; + continue; + } + + rc = validate_dest_phys(ctx, dest); + if (rc < 0) { + warnx("Bad phys: 0x%02x", dest->hwaddr[0]); + i++; + continue; + } + + net = mctp_nl_net_byindex(ctx->nl, dest->ifindex); + if (net < 1) { + warnx("No net for ifindex"); + i++; + continue; + } + + message = sd_bus_message_unref(message); + + rc = sd_bus_get_property(ctx->bus, + OPENBMC_ENTITY_MANAGER, s[i], + OPENBMC_MCTP_CONFIG_IFACE, "EndpointId", + &error, &message, "t"); + if (rc < 0) { + warnx("Failed to get property: %s\n", error.message); + goto out; + } + uint64_t eid = 0; + sd_bus_message_read(message, "t", &eid); + + rc = add_peer(ctx, dest, (mctp_eid_t)eid, net, &peer); + if (rc < 0) { + warnx("Failed to add peer for EID %d", (mctp_eid_t)eid); + i++; + continue; + } + + add_peer_route(peer); + + uint8_t* buf = NULL; + size_t buf_size = 0; + rc = endpoint_query_peer(peer, MCTP_CTRL_HDR_MSG_TYPE, + &req, sizeof(req), &buf, &buf_size, &addr); + if (rc < 0) { + warnx("Response timeout for EID %d.", (mctp_eid_t)eid); + remove_peer(peer); + free(buf); + i++; + continue; + } + free(buf); + + rc = query_peer_properties(peer); + + // The s[i] is like ../chassis/device so we need to get its parent path + // for the chassis path. + char object_path[100]; + strcpy(object_path, s[i]); + char *ret = strrchr(object_path, '/'); + *ret = '\0'; + + peer->association = malloc(sizeof(struct association)); + peer->association->forward = strdup("chassis"); + peer->association->reverse = strdup("mctp_endpoints"); + peer->association->object_path = strdup(object_path); + + rc = publish_peer(peer, true); + if (rc < 0) { + warnx("Error publishing remote eid %d net %d", (mctp_eid_t)eid, net); + i++; + continue; + } + + message = sd_bus_message_unref(message); + i++; + } + +out: + sd_bus_error_free(&error); + return 0; +} + /* SetupEndpoint method tries the following in order: - request Get Endpoint ID to add to the known table, return that - request Set Endpoint ID, return that */ @@ -1832,6 +2021,18 @@ static int method_learn_endpoint(sd_bus_message *call, void *data, sd_bus_error return rc; } +static int method_setup_endpoint_by_config(sd_bus_message *call, void *data, sd_bus_error *berr) +{ + int rc = 0; + ctx *ctx = data; + rc = setup_static_eid(ctx); + + rc = sd_bus_reply_method_return(call, ""); +out: + set_berr(ctx, rc, berr); + return rc; +} + // Query various properties of a peer. // To be called when a new peer is discovered/assigned, once an EID is known // and routable. @@ -2274,6 +2475,11 @@ static const sd_bus_vtable bus_mctpd_vtable[] = { SD_BUS_PARAM(found), method_learn_endpoint, 0), + SD_BUS_METHOD_WITH_ARGS("SetupEndpointByConfig", + SD_BUS_NO_ARGS, + SD_BUS_NO_RESULT, + method_setup_endpoint_by_config, + 0), SD_BUS_VTABLE_END, }; @@ -2337,6 +2543,9 @@ static int bus_endpoint_get_prop(sd_bus *bus, } else if (strcmp(property, "UUID") == 0 && peer->uuid) { const char *s = dfree(bytes_to_uuid(peer->uuid)); rc = sd_bus_message_append(reply, "s", s); + } else if (strcmp(property, "Associations") == 0 && peer->association) { + rc = sd_bus_message_append(reply, "a(sss)", + 1, peer->association->forward, peer->association->reverse, peer->association->object_path); } else { printf("Unknown property '%s' for %s iface %s\n", property, path, interface); rc = -ENOENT; @@ -2390,6 +2599,16 @@ static const sd_bus_vtable bus_endpoint_cc_vtable[] = { SD_BUS_VTABLE_END }; +static const sd_bus_vtable bus_endpoint_association_vtable[] = { + SD_BUS_VTABLE_START(0), + SD_BUS_PROPERTY("Associations", + "a(sss)", + bus_endpoint_get_prop, + 0, + SD_BUS_VTABLE_PROPERTY_CONST), + SD_BUS_VTABLE_END +}; + static int bus_endpoint_find(sd_bus *bus, const char *path, const char *interface, void *userdata, void **ret_found, sd_bus_error *ret_error) @@ -2425,6 +2644,26 @@ static int bus_endpoint_find_uuid(sd_bus *bus, const char *path, return 0; } +/* Association.Definitions interface is only added for peers that get EID + configuration from entity-manager */ +static int bus_endpoint_find_association(sd_bus *bus, const char *path, + const char *interface, void *userdata, void **ret_found, + sd_bus_error *ret_error) +{ + ctx *ctx = userdata; + peer *peer = NULL; + int rc; + + rc = peer_from_path(ctx, path, &peer); + if (rc >= 0 && peer->published) { + if (peer->association) { + *ret_found = peer; + return 1; + } + } + return 0; +} + static char* net_path(int net) { size_t l; @@ -2584,6 +2823,92 @@ static int mctpd_dbus_enumerate(sd_bus *bus, const char* path, return rc; } +int on_interface_added(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) +{ + int rc = 0; + bool update = false; + ctx *ctx = userdata; + char *object_path, *iface; + + rc = sd_bus_message_read(m, "o", &object_path); + if (rc < 0) + goto out; + + rc = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "{sa{sv}}"); + if (rc < 0) + goto out; + + for(;;) { + rc = sd_bus_message_enter_container(m, SD_BUS_TYPE_DICT_ENTRY, "sa{sv}"); + if (rc == 0) + break; + + rc = sd_bus_message_read(m, "s", &iface); + if (rc == 0) + break; + + if (strcmp(iface,OPENBMC_MCTP_CONFIG_IFACE) != 0) { + sd_bus_message_skip(m, "a{sv}"); + } + else { + sd_bus_message_skip(m, "a{sv}"); + update = true; + } + sd_bus_message_exit_container(m); + } + sd_bus_message_exit_container(m); + //TODO: parse message from signal instead of recanning + if (update == true) + setup_static_eid(ctx); + +out: + return rc; +} + +int on_interface_removed(sd_bus_message *m, void *userdata, sd_bus_error *ret_error) +{ + int rc = 0; + bool update = false; + ctx *ctx = userdata; + char *object_path, *iface; + peer *peer = NULL; + + rc = sd_bus_message_read(m, "o", &object_path); + if (rc < 0) + goto out; + + rc = sd_bus_message_enter_container(m, SD_BUS_TYPE_ARRAY, "s"); + if (rc < 0) + goto out; + + for(;;) { + rc = sd_bus_message_read(m, "s", &iface); + if (rc == 0) + break; + if(strcmp(iface,OPENBMC_MCTP_CONFIG_IFACE) == 0) { + update = true; + } + } + + if (update == true) { + char temp[100]; + strcpy(temp, object_path); + char *ret = strrchr(temp, '/'); + *ret = '\0'; + + for(;;) { + peer = find_peer_by_association(ctx, temp); + if (peer) + remove_peer(peer); + else + break; + } + } + +out: + return rc; +} + static int setup_bus(ctx *ctx) { int rc; @@ -2655,6 +2980,17 @@ static int setup_bus(ctx *ctx) goto out; } + rc = sd_bus_add_fallback_vtable(ctx->bus, NULL, + MCTP_DBUS_PATH, + OPENBMC_ASSOCIATION_IFACE, + bus_endpoint_association_vtable, + bus_endpoint_find_association, + ctx); + if (rc < 0) { + warnx("Failed adding association D-Bus interface: %s", strerror(-rc)); + goto out; + } + rc = sd_bus_add_object_manager(ctx->bus, NULL, MCTP_DBUS_PATH); if (rc < 0) { warnx("Adding object manager failed: %s", strerror(-rc)); @@ -2668,6 +3004,36 @@ static int setup_bus(ctx *ctx) goto out; } + rc = sd_bus_match_signal( + ctx->bus, + NULL, + NULL, + "/xyz/openbmc_project/inventory", + "org.freedesktop.DBus.ObjectManager", + "InterfacesAdded", + on_interface_added, + ctx + ); + if (rc < 0) { + warnx("Failed to add match signal for InterfacesAdded"); + goto out; + } + + rc = sd_bus_match_signal( + ctx->bus, + NULL, + NULL, + "/xyz/openbmc_project/inventory", + "org.freedesktop.DBus.ObjectManager", + "InterfacesRemoved", + on_interface_removed, + ctx + ); + if (rc < 0) { + warnx("Failed to add match signal for InterfacesRemoved"); + goto out; + } + rc = 0; out: return rc; @@ -3192,6 +3558,8 @@ int main(int argc, char **argv) if (rc < 0 && !ctx->testing) return 1; + rc = setup_static_eid(ctx); + // TODO add net argument? rc = listen_control_msg(ctx, MCTP_NET_ANY); if (rc < 0) { @@ -3210,7 +3578,6 @@ int main(int argc, char **argv) if (rc < 0) return 1; - rc = sd_event_loop(ctx->event); sd_event_unref(ctx->event); if (rc < 0) {