Skip to content

Commit 587c54c

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 79dad2d commit 587c54c

5 files changed

Lines changed: 215 additions & 8 deletions

File tree

docs/mctpd.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,48 @@ reports as a bridge.
248248

249249
Bridge endpoints should be initialised with `AssignEndpoint` instead.
250250

251+
### Endpoint interface: `au.com.codeconstruct.MCTP.Endpoint1` interface
252+
253+
When the interface `Role` is `Endpoint`, the MCTP interface object also hosts
254+
the `au.com.codeconstruct.MCTP.Endpoint1` D-Bus interface. This exposes
255+
endpoint-role functions on the per-interface D-Bus path.
256+
257+
```
258+
NAME TYPE SIGNATURE RESULT/VALUE FLAGS
259+
au.com.codeconstruct.MCTP.Interface1 interface - - -
260+
.NetworkId property u 1 emits-change
261+
.Role property s "Endpoint" emits-change writable
262+
au.com.codeconstruct.MCTP.Endpoint1 interface - - -
263+
.SetBusOwner method ay - -
264+
```
265+
266+
#### `.SetBusOwner`: `ay`
267+
268+
Sets the physical address of the bus owner for this interface. For transport
269+
types that know the bus owner physaddr (or do not need one to address the bus
270+
owner), this would be populated automatically by the transport binding. For
271+
other cases, this method allows the address to be supplied by an external
272+
management application.
273+
274+
On transport types that require a Discovery Notify (PCIe and I3C), setting the
275+
bus owner address will automatically trigger a Discovery Notify to the bus
276+
owner if the interface is in Endpoint role and not yet discovered.
277+
278+
`SetBusOwner <hwaddr>`
279+
280+
- `<hwaddr>` Physical hardware address of the bus owner. For MCTP-over-I3C
281+
this is the 6-byte Provisional ID (PID) of the bus owner.
282+
283+
An example for MCTP-over-I3C, setting the bus owner's 6-byte PID
284+
`00 6c 90 01 23 45`:
285+
286+
```shell
287+
busctl call au.com.codeconstruct.MCTP1 \
288+
/au/com/codeconstruct/mctp1/interfaces/mctpi3c0 \
289+
au.com.codeconstruct.MCTP.Endpoint1 \
290+
SetBusOwner ay 6 0x00 0x6c 0x90 0x01 0x23 0x45
291+
```
292+
251293
## Network objects: `/au/com/codeconstruct/networks/<net>`
252294

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

src/mctpd.c

Lines changed: 128 additions & 8 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
{
@@ -4246,6 +4251,98 @@ static int method_register_vdm_type_support(sd_bus_message *call, void *data,
42464251
return rc;
42474252
}
42484253

4254+
static bool link_supports_discovery_notify(const struct link *link)
4255+
{
4256+
return link->phys_binding == MCTP_PHYS_BINDING_PCIE_VDM ||
4257+
link->phys_binding == MCTP_PHYS_BINDING_I3C;
4258+
}
4259+
4260+
static void link_attempt_discovery_notify(struct link *link)
4261+
{
4262+
struct mctp_ctrl_resp_discovery_notify *resp = NULL;
4263+
struct mctp_ctrl_cmd_discovery_notify req = { 0 };
4264+
struct mctp_ctrl_cmd cmd = { 0 };
4265+
struct ctx *ctx = link->ctx;
4266+
unsigned int retry;
4267+
uint8_t iid;
4268+
int rc;
4269+
4270+
if (!link_supports_discovery_notify(link))
4271+
return;
4272+
if (link->role != ENDPOINT_ROLE_ENDPOINT)
4273+
return;
4274+
if (link->discovered == DISCOVERY_DISCOVERED)
4275+
return;
4276+
4277+
for (retry = 0; retry < 4; retry++) {
4278+
iid = mctp_next_iid(ctx);
4279+
mctp_ctrl_msg_hdr_init_req(&req.ctrl_hdr, iid,
4280+
MCTP_CTRL_CMD_DISCOVERY_NOTIFY);
4281+
mctp_ctrl_cmd_init_from_req_type(&cmd, req);
4282+
4283+
rc = endpoint_query_phys(ctx, &link->bus_owner, &cmd);
4284+
if (rc < 0) {
4285+
mctp_ctrl_cmd_free(&cmd);
4286+
break;
4287+
}
4288+
4289+
rc = mctp_ctrl_validate_response(
4290+
&cmd, sizeof(*resp), dest_phys_tostr(&link->bus_owner),
4291+
iid, MCTP_CTRL_CMD_DISCOVERY_NOTIFY);
4292+
mctp_ctrl_cmd_free(&cmd);
4293+
if (rc == 0)
4294+
return;
4295+
/* retry on non-fatal completion code errors */
4296+
if (rc != -EBUSY)
4297+
break;
4298+
}
4299+
4300+
if (rc < 0)
4301+
warnx("Discovery Notify on %s failed: %s", link->path,
4302+
strerror(-rc));
4303+
}
4304+
4305+
static int method_set_bus_owner(sd_bus_message *call, void *data,
4306+
sd_bus_error *berr)
4307+
{
4308+
struct link *link = data;
4309+
struct ctx *ctx = link->ctx;
4310+
dest_phys dest = { 0 };
4311+
int rc;
4312+
4313+
dest.ifindex = link->ifindex;
4314+
if (dest.ifindex <= 0)
4315+
return sd_bus_error_setf(berr, SD_BUS_ERROR_INVALID_ARGS,
4316+
"Unknown MCTP interface");
4317+
4318+
rc = message_read_hwaddr(call, &dest);
4319+
if (rc < 0) {
4320+
set_berr(ctx, rc, berr);
4321+
return rc;
4322+
}
4323+
4324+
rc = validate_dest_phys(ctx, &dest);
4325+
if (rc < 0)
4326+
return sd_bus_error_setf(berr, SD_BUS_ERROR_INVALID_ARGS,
4327+
"Bad physaddr");
4328+
4329+
link->bus_owner = dest;
4330+
link_attempt_discovery_notify(link);
4331+
return sd_bus_reply_method_return(call, "");
4332+
}
4333+
4334+
// clang-format off
4335+
static const sd_bus_vtable bus_link_endpoint_vtable[] = {
4336+
SD_BUS_VTABLE_START(0),
4337+
SD_BUS_METHOD_WITH_ARGS("SetBusOwner",
4338+
SD_BUS_ARGS("ay", physaddr),
4339+
SD_BUS_NO_RESULT,
4340+
method_set_bus_owner,
4341+
0),
4342+
SD_BUS_VTABLE_END,
4343+
};
4344+
// clang-format on
4345+
42494346
// clang-format off
42504347
static const sd_bus_vtable bus_link_owner_vtable[] = {
42514348
SD_BUS_VTABLE_START(0),
@@ -4458,14 +4555,22 @@ static int link_set_role(sd_event_source *ev, void *userdata)
44584555
sd_event_source_unref(link->role_defer);
44594556
link->role_defer = NULL;
44604557

4461-
if (link->role != ENDPOINT_ROLE_BUS_OWNER)
4462-
return 0;
4463-
4464-
rc = sd_bus_add_object_vtable(link->ctx->bus, &link->slot_busowner,
4465-
link->path, CC_MCTP_DBUS_IFACE_BUSOWNER,
4466-
bus_link_owner_vtable, link);
4467-
if (rc)
4468-
warnx("adding link owner vtable failed: %d", rc);
4558+
if (link->role == ENDPOINT_ROLE_BUS_OWNER) {
4559+
rc = sd_bus_add_object_vtable(link->ctx->bus,
4560+
&link->slot_busowner, link->path,
4561+
CC_MCTP_DBUS_IFACE_BUSOWNER,
4562+
bus_link_owner_vtable, link);
4563+
if (rc)
4564+
warnx("adding link owner vtable failed: %d", rc);
4565+
} else if (link->role == ENDPOINT_ROLE_ENDPOINT) {
4566+
rc = sd_bus_add_object_vtable(link->ctx->bus,
4567+
&link->slot_endpoint, link->path,
4568+
CC_MCTP_DBUS_IFACE_ENDPOINT,
4569+
bus_link_endpoint_vtable, link);
4570+
if (rc)
4571+
warnx("adding link endpoint vtable failed: %d", rc);
4572+
link_attempt_discovery_notify(link);
4573+
}
44694574

44704575
return 0;
44714576
}
@@ -4956,6 +5061,7 @@ static void free_link(struct link *link)
49565061
sd_event_source_disable_unref(link->role_defer);
49575062
sd_bus_slot_unref(link->slot_iface);
49585063
sd_bus_slot_unref(link->slot_busowner);
5064+
sd_bus_slot_unref(link->slot_endpoint);
49595065
free(link->path);
49605066
free(link->sysfs_path);
49615067
free(link);
@@ -5025,6 +5131,8 @@ static int rename_interface(struct ctx *ctx, struct link *link, int ifindex)
50255131
link->slot_iface = NULL;
50265132
sd_bus_slot_unref(link->slot_busowner);
50275133
link->slot_busowner = NULL;
5134+
sd_bus_slot_unref(link->slot_endpoint);
5135+
link->slot_endpoint = NULL;
50285136
free(link->path);
50295137

50305138
/* set new path and re-add */
@@ -5038,6 +5146,11 @@ static int rename_interface(struct ctx *ctx, struct link *link, int ifindex)
50385146
link->path,
50395147
CC_MCTP_DBUS_IFACE_BUSOWNER,
50405148
bus_link_owner_vtable, link);
5149+
} else if (link->role == ENDPOINT_ROLE_ENDPOINT) {
5150+
sd_bus_add_object_vtable(link->ctx->bus, &link->slot_endpoint,
5151+
link->path,
5152+
CC_MCTP_DBUS_IFACE_ENDPOINT,
5153+
bus_link_endpoint_vtable, link);
50415154
}
50425155

50435156
emit_interface_added(link);
@@ -5411,6 +5524,11 @@ static int add_interface(struct ctx *ctx, int ifindex)
54115524
link->path,
54125525
CC_MCTP_DBUS_IFACE_BUSOWNER,
54135526
bus_link_owner_vtable, link);
5527+
} else if (link->role == ENDPOINT_ROLE_ENDPOINT) {
5528+
sd_bus_add_object_vtable(link->ctx->bus, &link->slot_endpoint,
5529+
link->path,
5530+
CC_MCTP_DBUS_IFACE_ENDPOINT,
5531+
bus_link_endpoint_vtable, link);
54145532
}
54155533

54165534
if (link->phys_binding == MCTP_PHYS_BINDING_PCIE_VDM) {
@@ -5423,6 +5541,8 @@ static int add_interface(struct ctx *ctx, int ifindex)
54235541
link->published = false;
54245542
}
54255543

5544+
link_attempt_discovery_notify(link);
5545+
54265546
return rc;
54275547

54285548
err_free:

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
@@ -537,6 +537,11 @@ async def handle_mctp_control(self, sock, addr, data):
537537
)
538538
await sock.send(raddr, data)
539539

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

tests/test_mctpd_endpoint.py

Lines changed: 32 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,34 @@ 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+
class TestDiscoveryNotify:
225+
"""Discovery Notify for I3C endpoints with a 6-byte bus owner PID."""
226+
227+
# Example 6-byte I3C Provisional ID of the bus owner
228+
BO_PID = bytes([0x00, 0x6c, 0x90, 0x01, 0x23, 0x45])
229+
230+
@pytest.fixture
231+
async def iface(self):
232+
return System.Interface(
233+
"mctp0", 1, 1, bytes([0x1D]), 68, 254, True, PhysicalBinding.I3C
234+
)
235+
236+
@pytest.fixture
237+
async def sysnet(self, iface):
238+
system = System()
239+
await system.add_interface(iface)
240+
network = Network()
241+
network.add_endpoint(Endpoint(iface, self.BO_PID, eid=8))
242+
return Sysnet(system, network)
243+
244+
async def test_discovery_notify_on_set_bus_owner(self, dbus, mctpd):
245+
"""Discovery Notify is sent automatically when the 6-byte I3C bus owner PID is set."""
246+
iface = mctpd.system.interfaces[0]
247+
bo = mctpd.network.endpoints[0]
248+
249+
ep = await mctpd_mctp_iface_endpoint_obj(dbus, iface)
250+
# Set the bus owner's 6-byte I3C PID; this satisfies all conditions
251+
# and triggers Discovery Notify to the bus owner immediately.
252+
await ep.call_set_bus_owner(bo.lladdr)

0 commit comments

Comments
 (0)