Reduce code duplication in secure partitions
Open, NormalPublic

Description

This task is a follow-up on the discussion in this Gerrit review.

The TF-A Tests repository currently provides several secure partitions involved in SPM testing (Cactus, Ivy, Quark). They fulfill slightly different testing purposes. Their structure is quite similar and we could probably come up with a way to reduce code duplication. A design proposal has been made on the aforementioned Gerrit review.

Copying it here for convenience:

------8<-------
Instead, I am proposing that we come up with a data-driven framework, whereby our different secure partitions provide a description of themselves. Things like, their names, the secure services they provide, their layout, and so on.

I've tried to quickly draft this below. I've not sorted out all details but hopefully you get the idea.

The secure partition code (Ivy, Cactus, Cactus_MM, Quark) would be something like:

 struct sp ivy_sp = {
	.name = "Ivy",
	.setup = ivy_setup,
	// ...
 };
 static void print_magic(const struct *sp_args, struct sp_ret *ret) {
	INFO("IVY: Magic: 0x%x\n", IVY_MAGIC_NUMBER);
	ret->ret0 = SPRT_SUCCESS;
 }
 static void get_magic(const struct *sp_args, struct sp_ret *ret) {
	ret->ret0 = SPRT_SUCCESS;
	ret->ret1 = IVY_MAGIC_NUMBER;
 }
 static void sleep_ms(const struct *sp_args, struct sp_ret *ret) {
	sp_sleep(sp_args[2]);
	ret->ret0 = SPRT_SUCCESS;
 }
static void ivy_setup(void) {
	console_init(PL011_UART3_BASE,
		     PL011_UART3_CLK_IN_HZ,
		     PL011_BAUDRATE);
	register_service(IVY_PRINT_MAGIC, print_magic);
	register_service(IVY_GET_MAGIC, get_magic);
	register_service(IVY_SLEEP_MS, sleep_ms);
}

And then we could factorize the SPRT communication bits in the secure partition framework, like so:

 static void sp_message_handler(struct sprt_queue_entry_message *message) {
	struct sp_ret ret;
	if (message->type == SPRT_MSG_TYPE_SERVICE_REQUEST) {
		void (*handler) (const struct *sp_args, struct sp_ret *ret) =
			get_service_handler(message->args[1]);
		if (handler == NULL) {
			NOTICE("%s: Unhandled Service ID 0x%x\n", sp->name,
			       (unsigned int)message->args[1]);
			ret.ret0 = SPRT_NOT_SUPPORTED;
			break;
		}
		handler(message->args, &ret);
	} else {
		NOTICE("%s: Unhandled Service type 0x%x\n", sp->name,
		       (unsigned int)message->type);
		ret0 = SPRT_NOT_SUPPORTED;
	}
	sprt_message_end(message, ret.ret0, ret.ret1, ret.ret2, ret.ret3);
 }
 void __dead2 sp_main(void) {
	struct sp *sp;
	sp->setup();
	/*
	 * Handle secure service requests.
	 */
	sprt_initialize_queues((void *)sp->spm_buf_base);
	while (1) {
		struct sprt_queue_entry_message message;
		/*
		 * Try to fetch a message from the blocking requests queue. If
		 * it is empty, try to fetch from the non-blocking requests
		 * queue. Repeat until both of them are empty.
		 */
		while (1) {
			int err = sprt_get_next_message(&message,
					SPRT_QUEUE_NUM_BLOCKING);
			if (err == -ENOENT) {
				err = sprt_get_next_message(&message,
						SPRT_QUEUE_NUM_NON_BLOCKING);
				if (err == -ENOENT) {
					break;
				} else {
					assert(err == 0);
					sp_message_handler(&message);
				}
			} else {
				assert(err == 0);
				sp_message_handler(&message);
			}
		}
		sprt_wait_for_messages();
	}
 }

------8<-------

sandrine-bailleux-arm triaged this task as Normal priority.