diff --git a/ndctl/lib/libndctl.c b/ndctl/lib/libndctl.c
index 232c531..95cdc14 100644
--- a/ndctl/lib/libndctl.c
+++ b/ndctl/lib/libndctl.c
@@ -904,6 +904,7 @@ static void *add_bus(void *parent, int id, const char *ctl_base)
 	else {
 		bus->has_of_node = 1;
 		bus_name = "papr";
+		bus->ops = papr_bus_ops;
 	}
 
 	sprintf(path, "%s/device/%s/dsm_mask", ctl_base, bus_name);
diff --git a/ndctl/lib/papr.c b/ndctl/lib/papr.c
index f94f8aa..6ac3d3e 100644
--- a/ndctl/lib/papr.c
+++ b/ndctl/lib/papr.c
@@ -12,6 +12,7 @@
 #include <util/log.h>
 #include <ndctl.h>
 #include <ndctl/libndctl.h>
+#include <ndctl/libndctl-papr.h>
 #include <lib/private.h>
 #include "papr.h"
 
@@ -38,6 +39,33 @@
 /* return the pdsm command */
 #define to_pdsm_cmd(C) ((enum papr_pdsm)to_ndcmd(C)->nd_command)
 
+/**
+ * ndctl_bus_is_papr_cmd_supported - check if command is supported on @bus.
+ * @bus: ndctl_bus instance
+ * @cmd: papr command number (defined as PAPR_PDSM_XXX in papr-pdsm.h)
+ *
+ * Return 1: command is supported. Return 0: command is not supported.
+ *
+ */
+NDCTL_EXPORT int ndctl_bus_is_papr_cmd_supported(struct ndctl_bus *bus,
+						 int cmd)
+{
+	return !!(bus->nfit_dsm_mask & (1ULL << cmd));
+}
+
+static int papr_is_errinj_supported(struct ndctl_bus *bus)
+{
+	if (!ndctl_bus_is_papr_scm(bus))
+		return 0;
+
+	if (ndctl_bus_is_papr_cmd_supported(bus, PAPR_PDSM_INJECT_SET) &&
+	    ndctl_bus_is_papr_cmd_supported(bus, PAPR_PDSM_INJECT_CLEAR) &&
+	    ndctl_bus_is_papr_cmd_supported(bus, PAPR_PDSM_INJECT_GET))
+		return 1;
+
+	return 0;
+}
+
 static bool papr_cmd_is_supported(struct ndctl_dimm *dimm, int cmd)
 {
 	/* Handle this separately to support monitor mode */
@@ -559,3 +587,109 @@ struct ndctl_dimm_ops * const papr_dimm_ops = &(struct ndctl_dimm_ops) {
 		= papr_cmd_smart_threshold_set_ctrl_temperature,
 	.smart_threshold_set_spares = papr_cmd_smart_threshold_set_spares,
 };
+
+static u32 bus_get_firmware_status(struct ndctl_cmd *cmd)
+{
+	struct nd_cmd_bus *cmd_bus = cmd->cmd_bus;
+
+	switch (cmd_bus->gen.nd_command) {
+	case PAPR_PDSM_INJECT_SET:
+		return cmd_bus->err_inj.status;
+	case PAPR_PDSM_INJECT_CLEAR:
+		return cmd_bus->err_inj_clr.status;
+	case PAPR_PDSM_INJECT_GET:
+		return cmd_bus->err_inj_stat.status;
+	}
+
+	return -1U;
+}
+
+static struct ndctl_cmd *papr_bus_cmd_new_err_inj(struct ndctl_bus *bus)
+{
+	size_t size, cmd_length;
+	struct nd_cmd_pkg *pkg;
+	struct ndctl_cmd *cmd;
+
+	cmd_length = sizeof(struct nd_cmd_ars_err_inj);
+	size = sizeof(*cmd) + sizeof(*pkg) + cmd_length;
+	cmd = calloc(1, size);
+	if (!cmd)
+		return NULL;
+
+	cmd->bus = bus;
+	ndctl_cmd_ref(cmd);
+	cmd->type = ND_CMD_CALL;
+	cmd->get_firmware_status = bus_get_firmware_status;
+	cmd->size = size;
+	cmd->status = 1;
+	pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0];
+	pkg->nd_command = PAPR_PDSM_INJECT_SET;
+	pkg->nd_size_in = offsetof(struct nd_cmd_ars_err_inj, status);
+	pkg->nd_size_out = cmd_length - pkg->nd_size_in;
+	pkg->nd_fw_size = pkg->nd_size_out;
+
+	return cmd;
+}
+
+static struct ndctl_cmd *papr_bus_cmd_new_err_inj_clr(struct ndctl_bus *bus)
+{
+	size_t size, cmd_length;
+	struct nd_cmd_pkg *pkg;
+	struct ndctl_cmd *cmd;
+
+	cmd_length = sizeof(struct nd_cmd_ars_err_inj_clr);
+	size = sizeof(*cmd) + sizeof(*pkg) + cmd_length;
+	cmd = calloc(1, size);
+	if (!cmd)
+		return NULL;
+
+	cmd->bus = bus;
+	ndctl_cmd_ref(cmd);
+	cmd->type = ND_CMD_CALL;
+	cmd->get_firmware_status = bus_get_firmware_status;
+	cmd->size = size;
+	cmd->status = 1;
+	pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0];
+	pkg->nd_command = PAPR_PDSM_INJECT_CLEAR;
+	pkg->nd_size_in = offsetof(struct nd_cmd_ars_err_inj_clr, status);
+	pkg->nd_size_out = cmd_length - pkg->nd_size_in;
+	pkg->nd_fw_size = pkg->nd_size_out;
+
+	return cmd;
+}
+
+static struct ndctl_cmd *papr_bus_cmd_new_err_inj_stat(struct ndctl_bus *bus,
+						u32 buf_size)
+{
+	size_t size, cmd_length;
+	struct nd_cmd_pkg *pkg;
+	struct ndctl_cmd *cmd;
+
+
+	cmd_length = sizeof(struct nd_cmd_ars_err_inj_stat);
+	size = sizeof(*cmd) + sizeof(*pkg) + cmd_length + buf_size;
+	cmd = calloc(1, size);
+	if (!cmd)
+		return NULL;
+
+	cmd->bus = bus;
+	ndctl_cmd_ref(cmd);
+	cmd->type = ND_CMD_CALL;
+	cmd->get_firmware_status = bus_get_firmware_status;
+	cmd->size = size;
+	cmd->status = 1;
+	pkg = (struct nd_cmd_pkg *)&cmd->cmd_buf[0];
+	pkg->nd_command = PAPR_PDSM_INJECT_GET;
+	pkg->nd_size_in = 0;
+	pkg->nd_size_out = cmd_length + buf_size;
+	pkg->nd_fw_size = pkg->nd_size_out;
+
+	return cmd;
+}
+
+struct ndctl_bus_ops *const papr_bus_ops = &(struct ndctl_bus_ops) {
+	.new_err_inj = papr_bus_cmd_new_err_inj,
+	.new_err_inj_clr = papr_bus_cmd_new_err_inj_clr,
+	.new_err_inj_stat = papr_bus_cmd_new_err_inj_stat,
+	.err_inj_supported = papr_is_errinj_supported,
+};
diff --git a/ndctl/lib/private.h b/ndctl/lib/private.h
index 0f36c67..96d890b 100644
--- a/ndctl/lib/private.h
+++ b/ndctl/lib/private.h
@@ -383,6 +383,7 @@ struct ndctl_bus_ops {
 };
 
 extern struct ndctl_bus_ops * const nfit_bus_ops;
+extern struct ndctl_bus_ops * const papr_bus_ops;
 
 struct ndctl_cmd *ndctl_bus_cmd_new_err_inj(struct ndctl_bus *bus);
 struct ndctl_cmd *ndctl_bus_cmd_new_err_inj_clr(struct ndctl_bus *bus);
diff --git a/ndctl/libndctl-papr.h b/ndctl/libndctl-papr.h
new file mode 100644
index 0000000..1658d8e
--- /dev/null
+++ b/ndctl/libndctl-papr.h
@@ -0,0 +1,7 @@
+/* SPDX-License-Identifier: LGPL-2.1 */
+
+#ifndef __LIBNDCTL_PAPR_H__
+#define __LIBNDCTL_PAPR_H__
+
+int ndctl_bus_is_papr_cmd_supported(struct ndctl_bus *bus, int cmd);
+#endif
