|
4 | 4 | #include <linux/device.h>
|
5 | 5 | #include <cxl/mailbox.h>
|
6 | 6 | #include <cxl/features.h>
|
| 7 | +#include <uapi/fwctl/cxl.h> |
7 | 8 | #include "cxl.h"
|
8 | 9 | #include "core.h"
|
9 | 10 | #include "cxlmem.h"
|
@@ -349,11 +350,127 @@ static void cxlctl_close_uctx(struct fwctl_uctx *uctx)
|
349 | 350 | {
|
350 | 351 | }
|
351 | 352 |
|
| 353 | +static void *cxlctl_get_supported_features(struct cxl_features_state *cxlfs, |
| 354 | + const struct fwctl_rpc_cxl *rpc_in, |
| 355 | + size_t *out_len) |
| 356 | +{ |
| 357 | + const struct cxl_mbox_get_sup_feats_in *feat_in; |
| 358 | + struct cxl_mbox_get_sup_feats_out *feat_out; |
| 359 | + struct cxl_feat_entry *pos; |
| 360 | + size_t out_size; |
| 361 | + int requested; |
| 362 | + u32 count; |
| 363 | + u16 start; |
| 364 | + int i; |
| 365 | + |
| 366 | + if (rpc_in->op_size != sizeof(*feat_in)) |
| 367 | + return ERR_PTR(-EINVAL); |
| 368 | + |
| 369 | + feat_in = &rpc_in->get_sup_feats_in; |
| 370 | + count = le32_to_cpu(feat_in->count); |
| 371 | + start = le16_to_cpu(feat_in->start_idx); |
| 372 | + requested = count / sizeof(*pos); |
| 373 | + |
| 374 | + /* |
| 375 | + * Make sure that the total requested number of entries is not greater |
| 376 | + * than the total number of supported features allowed for userspace. |
| 377 | + */ |
| 378 | + if (start >= cxlfs->entries->num_features) |
| 379 | + return ERR_PTR(-EINVAL); |
| 380 | + |
| 381 | + requested = min_t(int, requested, cxlfs->entries->num_features - start); |
| 382 | + |
| 383 | + out_size = sizeof(struct fwctl_rpc_cxl_out) + |
| 384 | + struct_size(feat_out, ents, requested); |
| 385 | + |
| 386 | + struct fwctl_rpc_cxl_out *rpc_out __free(kvfree) = |
| 387 | + kvzalloc(out_size, GFP_KERNEL); |
| 388 | + if (!rpc_out) |
| 389 | + return ERR_PTR(-ENOMEM); |
| 390 | + |
| 391 | + rpc_out->size = struct_size(feat_out, ents, requested); |
| 392 | + feat_out = &rpc_out->get_sup_feats_out; |
| 393 | + if (requested == 0) { |
| 394 | + feat_out->num_entries = cpu_to_le16(requested); |
| 395 | + feat_out->supported_feats = |
| 396 | + cpu_to_le16(cxlfs->entries->num_features); |
| 397 | + rpc_out->retval = CXL_MBOX_CMD_RC_SUCCESS; |
| 398 | + *out_len = out_size; |
| 399 | + return no_free_ptr(rpc_out); |
| 400 | + } |
| 401 | + |
| 402 | + for (i = start, pos = &feat_out->ents[0]; |
| 403 | + i < cxlfs->entries->num_features; i++, pos++) { |
| 404 | + if (i - start == requested) |
| 405 | + break; |
| 406 | + |
| 407 | + memcpy(pos, &cxlfs->entries->ent[i], sizeof(*pos)); |
| 408 | + /* |
| 409 | + * If the feature is exclusive, set the set_feat_size to 0 to |
| 410 | + * indicate that the feature is not changeable. |
| 411 | + */ |
| 412 | + if (is_cxl_feature_exclusive(pos)) { |
| 413 | + u32 flags; |
| 414 | + |
| 415 | + pos->set_feat_size = 0; |
| 416 | + flags = le32_to_cpu(pos->flags); |
| 417 | + flags &= ~CXL_FEATURE_F_CHANGEABLE; |
| 418 | + pos->flags = cpu_to_le32(flags); |
| 419 | + } |
| 420 | + } |
| 421 | + |
| 422 | + feat_out->num_entries = cpu_to_le16(requested); |
| 423 | + feat_out->supported_feats = cpu_to_le16(cxlfs->entries->num_features); |
| 424 | + rpc_out->retval = CXL_MBOX_CMD_RC_SUCCESS; |
| 425 | + *out_len = out_size; |
| 426 | + |
| 427 | + return no_free_ptr(rpc_out); |
| 428 | +} |
| 429 | + |
| 430 | +static bool cxlctl_validate_hw_command(struct cxl_features_state *cxlfs, |
| 431 | + const struct fwctl_rpc_cxl *rpc_in, |
| 432 | + enum fwctl_rpc_scope scope, |
| 433 | + u16 opcode) |
| 434 | +{ |
| 435 | + struct cxl_mailbox *cxl_mbox = &cxlfs->cxlds->cxl_mbox; |
| 436 | + |
| 437 | + switch (opcode) { |
| 438 | + case CXL_MBOX_OP_GET_SUPPORTED_FEATURES: |
| 439 | + if (cxl_mbox->feat_cap < CXL_FEATURES_RO) |
| 440 | + return false; |
| 441 | + if (scope >= FWCTL_RPC_CONFIGURATION) |
| 442 | + return true; |
| 443 | + return false; |
| 444 | + default: |
| 445 | + return false; |
| 446 | + } |
| 447 | +} |
| 448 | + |
| 449 | +static void *cxlctl_handle_commands(struct cxl_features_state *cxlfs, |
| 450 | + const struct fwctl_rpc_cxl *rpc_in, |
| 451 | + size_t *out_len, u16 opcode) |
| 452 | +{ |
| 453 | + switch (opcode) { |
| 454 | + case CXL_MBOX_OP_GET_SUPPORTED_FEATURES: |
| 455 | + return cxlctl_get_supported_features(cxlfs, rpc_in, out_len); |
| 456 | + default: |
| 457 | + return ERR_PTR(-EOPNOTSUPP); |
| 458 | + } |
| 459 | +} |
| 460 | + |
352 | 461 | static void *cxlctl_fw_rpc(struct fwctl_uctx *uctx, enum fwctl_rpc_scope scope,
|
353 | 462 | void *in, size_t in_len, size_t *out_len)
|
354 | 463 | {
|
355 |
| - /* Place holder */ |
356 |
| - return ERR_PTR(-EOPNOTSUPP); |
| 464 | + struct fwctl_device *fwctl_dev = uctx->fwctl; |
| 465 | + struct cxl_memdev *cxlmd = fwctl_to_memdev(fwctl_dev); |
| 466 | + struct cxl_features_state *cxlfs = to_cxlfs(cxlmd->cxlds); |
| 467 | + const struct fwctl_rpc_cxl *rpc_in = in; |
| 468 | + u16 opcode = rpc_in->opcode; |
| 469 | + |
| 470 | + if (!cxlctl_validate_hw_command(cxlfs, rpc_in, scope, opcode)) |
| 471 | + return ERR_PTR(-EINVAL); |
| 472 | + |
| 473 | + return cxlctl_handle_commands(cxlfs, rpc_in, out_len, opcode); |
357 | 474 | }
|
358 | 475 |
|
359 | 476 | static const struct fwctl_ops cxlctl_ops = {
|
|
0 commit comments