diff --git a/include/libvfio-user.h b/include/libvfio-user.h index 76181ad2..c1bccf5a 100644 --- a/include/libvfio-user.h +++ b/include/libvfio-user.h @@ -120,6 +120,10 @@ vfu_create_ctx(vfu_trans_t trans, const char *path, * mandatory to be called before vfu_attach_ctx(). * @vfu_ctx: the libvfio-user context * + * Devices advertising PCIe Function Level Reset capability must + * register a reset callback via vfu_setup_device_reset_cb() + * before realization, otherwise realization fails with EINVAL. + * * @returns: 0 on success, -1 on error. Sets errno. */ int diff --git a/lib/libvfio-user.c b/lib/libvfio-user.c index 407061a7..7f083a19 100644 --- a/lib/libvfio-user.c +++ b/lib/libvfio-user.c @@ -1798,7 +1798,18 @@ vfu_realize_ctx(vfu_ctx_t *vfu_ctx) if (vfu_ctx->pci.nr_caps != 0) { vfu_ctx->pci.config_space->hdr.sts.cl = 0x1; } + if (vfu_ctx->pci_cap_exp_off >= 0) { + struct pxcap *px; + px = (struct pxcap *)pci_config_space_ptr(vfu_ctx, + vfu_ctx->pci_cap_exp_off); + + if (px->pxdcap.flrc && vfu_ctx->reset == NULL) { + vfu_log(vfu_ctx, LOG_ERR, + "FLR capability requires a reset callback"); + return ERROR_INT(EINVAL); + } + } vfu_ctx->realized = true; return 0; diff --git a/samples/lspci.c b/samples/lspci.c index 010cd7a1..ad1fed5f 100644 --- a/samples/lspci.c +++ b/samples/lspci.c @@ -38,6 +38,15 @@ #include "libvfio-user.h" +static int +lspci_reset_cb(vfu_ctx_t *vfu_ctx, vfu_reset_type_t type) +{ + (void)vfu_ctx; + (void)type; + + return 0; +} + int main(void) { int i, j; @@ -61,6 +70,9 @@ int main(void) if (vfu_ctx == NULL) { err(EXIT_FAILURE, "failed to create libvfio-user context"); } + if (vfu_setup_device_reset_cb(vfu_ctx, lspci_reset_cb) < 0) { + err(EXIT_FAILURE, "vfu_setup_device_reset_cb() failed"); + } if (vfu_pci_init(vfu_ctx, VFU_PCI_TYPE_EXPRESS, PCI_HEADER_TYPE_NORMAL, 0) < 0) { err(EXIT_FAILURE, "vfu_pci_init() failed"); diff --git a/test/py/test_pci_caps.py b/test/py/test_pci_caps.py index 5a3521b5..0fdd4af8 100644 --- a/test/py/test_pci_caps.py +++ b/test/py/test_pci_caps.py @@ -342,6 +342,29 @@ def test_pci_cap_write_px(mock_quiesce, mock_reset): expect=errno.EINVAL) +@patch('libvfio_user.quiesce_cb') +def test_pci_cap_write_px_no_reset_cb(mock_quiesce): + """ + FLR must fail if no reset callback exists. + """ + + global ctx + + # recreate context without reset callback + vfu_destroy_ctx(ctx) + + ctx = vfu_create_ctx(flags=LIBVFIO_USER_FLAG_ATTACH_NB) + assert ctx is not None + + vfu_setup_device_quiesce_cb(ctx) + + setup_pci_dev(realize=False) + setup_flrc(ctx) + ret = vfu_realize_ctx(ctx) + assert ret == -1 + assert c.get_errno() == errno.EINVAL + + def test_pci_cap_write_msi(): setup_pci_dev(realize=True) client = connect_client(ctx)