Skip to content

Commit a525f60

Browse files
committed
mctpd: Add AttemptDiscoveryNotify D-Bus method for endpoint role
Expose AttemptDiscoveryNotify on au.com.codeconstruct.MCTP.Endpoint1 on the per-interface D-Bus path when the link role is ENDPOINT_ROLE_ENDPOINT. The method accepts a physical hardware address and sends a physical-addressed Discovery Notify control message (opcode 0x0D) to the target, allowing an endpoint to proactively trigger discovery by the bus owner. Also fix mctp_ctrl_validate_response to use strict less-than when comparing exp_size against sizeof(struct mctp_ctrl_resp). The previous <= incorrectly rejected responses whose complete structure is exactly the base response size, such as Discovery Notify. Signed-off-by: Nidhin MS <nidhin.ms@intel.com>
1 parent 4e04aa7 commit a525f60

5 files changed

Lines changed: 205 additions & 9 deletions

File tree

docs/mctpd.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,63 @@ reports as a bridge.
217217

218218
Bridge endpoints should be initialised with `AssignEndpoint` instead.
219219

220+
### Endpoint interface: `au.com.codeconstruct.MCTP.Endpoint1` interface
221+
222+
When the interface `Role` is `Endpoint`, the MCTP interface object also hosts
223+
the `au.com.codeconstruct.MCTP.Endpoint1` D-Bus interface. This exposes
224+
endpoint-role functions on the per-interface D-Bus path.
225+
226+
```
227+
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
228+
au.com.codeconstruct.MCTP.Interface1 interface - - -
229+
.NetworkId property u 1 emits-change
230+
.Role property s "Endpoint" emits-change writable
231+
au.com.codeconstruct.MCTP.Endpoint1 interface - - -
232+
.SetBusOwner method ay - -
233+
.AttemptDiscoveryNotify method - - -
234+
```
235+
236+
#### `.SetBusOwner`: `ay`
237+
238+
Sets the physical address of the bus owner for this interface. This address
239+
is used by `AttemptDiscoveryNotify` when sending a Discovery Notify message.
240+
241+
For transport types that know the bus owner physaddr (or do not need one to
242+
address the bus owner), this would be populated automatically by the transport
243+
binding. For other cases, this method allows the address to be supplied by an
244+
external management application.
245+
246+
`SetBusOwner <hwaddr>`
247+
248+
- `<hwaddr>` Physical hardware address of the bus owner. For MCTP-over-I3C
249+
this is the 6-byte Provisional ID (PID) of the bus owner.
250+
251+
An example for MCTP-over-I3C, setting the bus owner's 6-byte PID
252+
`00 6c 90 01 23 45`:
253+
254+
```shell
255+
busctl call au.com.codeconstruct.MCTP1 \
256+
/au/com/codeconstruct/mctp1/interfaces/mctpi3c0 \
257+
au.com.codeconstruct.MCTP.Endpoint1 \
258+
SetBusOwner ay 6 0x00 0x6c 0x90 0x01 0x23 0x45
259+
```
260+
261+
#### `.AttemptDiscoveryNotify`
262+
263+
Some physical transports (such as MCTP-over-I3C) require the endpoint to
264+
send a Discovery Notify to announce its presence before the bus owner will
265+
enumerate it. On those interfaces this method can be called before the
266+
endpoint will be assigned an EID.
267+
268+
The bus owner physical address must have been set beforehand via `SetBusOwner`.
269+
270+
```shell
271+
busctl call au.com.codeconstruct.MCTP1 \
272+
/au/com/codeconstruct/mctp1/interfaces/mctpi3c0 \
273+
au.com.codeconstruct.MCTP.Endpoint1 \
274+
AttemptDiscoveryNotify
275+
```
276+
220277
## Network objects: `/au/com/codeconstruct/networks/<net>`
221278

222279
These objects represent MCTP networks which have been added use `mctp link`

src/mctpd.c

Lines changed: 122 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -147,8 +147,12 @@ struct link {
147147
char *path;
148148
sd_bus_slot *slot_iface;
149149
sd_bus_slot *slot_busowner;
150+
sd_bus_slot *slot_endpoint;
150151
sd_event_source *role_defer;
151152

153+
/* Physical address of the bus owner, set via SetBusOwner. */
154+
dest_phys bus_owner;
155+
152156
struct ctx *ctx;
153157
};
154158

@@ -385,6 +389,7 @@ static const sd_bus_vtable bus_endpoint_obmc_vtable[];
385389
static const sd_bus_vtable bus_endpoint_cc_vtable[];
386390
static const sd_bus_vtable bus_endpoint_bridge[];
387391
static const sd_bus_vtable bus_endpoint_uuid_vtable[];
392+
static const sd_bus_vtable bus_link_endpoint_vtable[];
388393

389394
__attribute__((format(printf, 1, 2))) static void bug_warn(const char *fmt, ...)
390395
{
@@ -1658,7 +1663,7 @@ static int mctp_ctrl_validate_response(struct mctp_ctrl_cmd *cmd,
16581663
{
16591664
struct mctp_ctrl_resp *rsp;
16601665

1661-
if (exp_size <= sizeof(*rsp)) {
1666+
if (exp_size < sizeof(*rsp)) {
16621667
warnx("invalid expected response size!");
16631668
return -EINVAL;
16641669
}
@@ -4017,6 +4022,94 @@ static int method_register_vdm_type_support(sd_bus_message *call, void *data,
40174022
return rc;
40184023
}
40194024

4025+
static int method_set_bus_owner(sd_bus_message *call, void *data,
4026+
sd_bus_error *berr)
4027+
{
4028+
struct link *link = data;
4029+
struct ctx *ctx = link->ctx;
4030+
dest_phys dest = { 0 };
4031+
int rc;
4032+
4033+
dest.ifindex = link->ifindex;
4034+
if (dest.ifindex <= 0)
4035+
return sd_bus_error_setf(berr, SD_BUS_ERROR_INVALID_ARGS,
4036+
"Unknown MCTP interface");
4037+
4038+
rc = message_read_hwaddr(call, &dest);
4039+
if (rc < 0) {
4040+
set_berr(ctx, rc, berr);
4041+
return rc;
4042+
}
4043+
4044+
rc = validate_dest_phys(ctx, &dest);
4045+
if (rc < 0)
4046+
return sd_bus_error_setf(berr, SD_BUS_ERROR_INVALID_ARGS,
4047+
"Bad physaddr");
4048+
4049+
link->bus_owner = dest;
4050+
return sd_bus_reply_method_return(call, "");
4051+
}
4052+
4053+
static int method_attempt_discovery_notify(sd_bus_message *call, void *data,
4054+
sd_bus_error *berr)
4055+
{
4056+
struct mctp_ctrl_resp_discovery_notify *resp = NULL;
4057+
struct mctp_ctrl_cmd_discovery_notify req = { 0 };
4058+
struct mctp_ctrl_cmd cmd = { 0 };
4059+
struct link *link = data;
4060+
struct ctx *ctx = link->ctx;
4061+
unsigned int retry;
4062+
uint8_t iid;
4063+
int rc;
4064+
4065+
if (link->bus_owner.hwaddr_len == 0)
4066+
return sd_bus_error_setf(berr, SD_BUS_ERROR_INVALID_ARGS,
4067+
"Bus owner address not set");
4068+
4069+
for (retry = 0; retry < 4; retry++) {
4070+
iid = mctp_next_iid(ctx);
4071+
mctp_ctrl_msg_hdr_init_req(&req.ctrl_hdr, iid,
4072+
MCTP_CTRL_CMD_DISCOVERY_NOTIFY);
4073+
mctp_ctrl_cmd_init_from_req_type(&cmd, req);
4074+
4075+
rc = endpoint_query_phys(ctx, &link->bus_owner, &cmd);
4076+
if (rc < 0) {
4077+
mctp_ctrl_cmd_free(&cmd);
4078+
break;
4079+
}
4080+
4081+
rc = mctp_ctrl_validate_response(
4082+
&cmd, sizeof(*resp), dest_phys_tostr(&link->bus_owner),
4083+
iid, MCTP_CTRL_CMD_DISCOVERY_NOTIFY);
4084+
mctp_ctrl_cmd_free(&cmd);
4085+
if (rc == 0)
4086+
return sd_bus_reply_method_return(call, "");
4087+
/* retry on non-fatal completion code errors */
4088+
if (rc != -EBUSY)
4089+
break;
4090+
}
4091+
4092+
set_berr(ctx, rc, berr);
4093+
return rc;
4094+
}
4095+
4096+
// clang-format off
4097+
static const sd_bus_vtable bus_link_endpoint_vtable[] = {
4098+
SD_BUS_VTABLE_START(0),
4099+
SD_BUS_METHOD_WITH_ARGS("SetBusOwner",
4100+
SD_BUS_ARGS("ay", physaddr),
4101+
SD_BUS_NO_RESULT,
4102+
method_set_bus_owner,
4103+
0),
4104+
SD_BUS_METHOD_WITH_ARGS("AttemptDiscoveryNotify",
4105+
SD_BUS_NO_ARGS,
4106+
SD_BUS_NO_RESULT,
4107+
method_attempt_discovery_notify,
4108+
0),
4109+
SD_BUS_VTABLE_END,
4110+
};
4111+
// clang-format on
4112+
40204113
// clang-format off
40214114
static const sd_bus_vtable bus_link_owner_vtable[] = {
40224115
SD_BUS_VTABLE_START(0),
@@ -4214,14 +4307,21 @@ static int link_set_role(sd_event_source *ev, void *userdata)
42144307
sd_event_source_unref(link->role_defer);
42154308
link->role_defer = NULL;
42164309

4217-
if (link->role != ENDPOINT_ROLE_BUS_OWNER)
4218-
return 0;
4219-
4220-
rc = sd_bus_add_object_vtable(link->ctx->bus, &link->slot_busowner,
4221-
link->path, CC_MCTP_DBUS_IFACE_BUSOWNER,
4222-
bus_link_owner_vtable, link);
4223-
if (rc)
4224-
warnx("adding link owner vtable failed: %d", rc);
4310+
if (link->role == ENDPOINT_ROLE_BUS_OWNER) {
4311+
rc = sd_bus_add_object_vtable(link->ctx->bus,
4312+
&link->slot_busowner, link->path,
4313+
CC_MCTP_DBUS_IFACE_BUSOWNER,
4314+
bus_link_owner_vtable, link);
4315+
if (rc)
4316+
warnx("adding link owner vtable failed: %d", rc);
4317+
} else if (link->role == ENDPOINT_ROLE_ENDPOINT) {
4318+
rc = sd_bus_add_object_vtable(link->ctx->bus,
4319+
&link->slot_endpoint, link->path,
4320+
CC_MCTP_DBUS_IFACE_ENDPOINT,
4321+
bus_link_endpoint_vtable, link);
4322+
if (rc)
4323+
warnx("adding link endpoint vtable failed: %d", rc);
4324+
}
42254325

42264326
return 0;
42274327
}
@@ -4712,6 +4812,7 @@ static void free_link(struct link *link)
47124812
sd_event_source_disable_unref(link->role_defer);
47134813
sd_bus_slot_unref(link->slot_iface);
47144814
sd_bus_slot_unref(link->slot_busowner);
4815+
sd_bus_slot_unref(link->slot_endpoint);
47154816
free(link->path);
47164817
free(link->sysfs_path);
47174818
free(link);
@@ -4781,6 +4882,8 @@ static int rename_interface(struct ctx *ctx, struct link *link, int ifindex)
47814882
link->slot_iface = NULL;
47824883
sd_bus_slot_unref(link->slot_busowner);
47834884
link->slot_busowner = NULL;
4885+
sd_bus_slot_unref(link->slot_endpoint);
4886+
link->slot_endpoint = NULL;
47844887
free(link->path);
47854888

47864889
/* set new path and re-add */
@@ -4794,6 +4897,11 @@ static int rename_interface(struct ctx *ctx, struct link *link, int ifindex)
47944897
link->path,
47954898
CC_MCTP_DBUS_IFACE_BUSOWNER,
47964899
bus_link_owner_vtable, link);
4900+
} else if (link->role == ENDPOINT_ROLE_ENDPOINT) {
4901+
sd_bus_add_object_vtable(link->ctx->bus, &link->slot_endpoint,
4902+
link->path,
4903+
CC_MCTP_DBUS_IFACE_ENDPOINT,
4904+
bus_link_endpoint_vtable, link);
47974905
}
47984906

47994907
emit_interface_added(link);
@@ -5167,6 +5275,11 @@ static int add_interface(struct ctx *ctx, int ifindex)
51675275
link->path,
51685276
CC_MCTP_DBUS_IFACE_BUSOWNER,
51695277
bus_link_owner_vtable, link);
5278+
} else if (link->role == ENDPOINT_ROLE_ENDPOINT) {
5279+
sd_bus_add_object_vtable(link->ctx->bus, &link->slot_endpoint,
5280+
link->path,
5281+
CC_MCTP_DBUS_IFACE_ENDPOINT,
5282+
bus_link_endpoint_vtable, link);
51705283
}
51715284

51725285
if (link->phys_binding == MCTP_PHYS_BINDING_PCIE_VDM) {

tests/mctp_test_utils.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,14 @@ async def mctpd_mctp_iface_control_obj(dbus, iface):
2121
return await obj.get_interface("au.com.codeconstruct.MCTP.Interface1")
2222

2323

24+
async def mctpd_mctp_iface_endpoint_obj(dbus, iface):
25+
obj = await dbus.get_proxy_object(
26+
'au.com.codeconstruct.MCTP1',
27+
'/au/com/codeconstruct/mctp1/interfaces/' + iface.name,
28+
)
29+
return await obj.get_interface('au.com.codeconstruct.MCTP.Endpoint1')
30+
31+
2432
async def mctpd_mctp_endpoint_obj(dbus, path, iface):
2533
obj = await dbus.get_proxy_object(
2634
'au.com.codeconstruct.MCTP1',

tests/mctpenv/__init__.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -536,6 +536,11 @@ async def handle_mctp_control(self, sock, addr, data):
536536
)
537537
await sock.send(raddr, data)
538538

539+
elif opcode == 0x0D:
540+
# Discovery Notify — respond with completion_code = 0
541+
data = bytes(hdr + [0x00])
542+
await sock.send(raddr, data)
543+
539544
else:
540545
await sock.send(
541546
raddr, bytes(hdr + [0x05])

tests/test_mctpd_endpoint.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
import asyncdbus
33
from mctp_test_utils import (
44
mctpd_mctp_iface_control_obj,
5+
mctpd_mctp_iface_endpoint_obj,
56
mctpd_mctp_endpoint_control_obj,
67
)
78
from mctpenv import (
@@ -218,3 +219,15 @@ async def test_simple(self, dbus, mctpd):
218219
mctpd.network.mctp_socket, MCTPControlCommand(True, 0, 0x0B)
219220
)
220221
assert rsp.hex(' ') == '00 0b 05'
222+
223+
224+
async def test_attempt_discovery_notify(dbus, mctpd):
225+
"""AttemptDiscoveryNotify sends Discovery Notify to the bus owner."""
226+
iface = mctpd.system.interfaces[0]
227+
bo = mctpd.network.endpoints[0]
228+
229+
ep = await mctpd_mctp_iface_endpoint_obj(dbus, iface)
230+
# set the bus owner address first
231+
await ep.call_set_bus_owner(bo.lladdr)
232+
# mock bus owner responds with completion_code = 0x00
233+
await ep.call_attempt_discovery_notify()

0 commit comments

Comments
 (0)