[sw] main.c SW abstraction

- This abstracts the main.c entry function that can now remain as the
common entity for running all SW tests.

Signed-off-by: Srikrishna Iyer <sriyer@google.com>
diff --git a/sw/device/lib/testing/test_main.h b/sw/device/lib/testing/test_main.h
new file mode 100644
index 0000000..8da3f62
--- /dev/null
+++ b/sw/device/lib/testing/test_main.h
@@ -0,0 +1,18 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#ifndef OPENTITAN_SW_DEVICE_LIB_TESTING_TEST_MAIN_H_
+#define OPENTITAN_SW_DEVICE_LIB_TESTING_TEST_MAIN_H_
+
+/**
+ * Runs the SW test.
+ *
+ * This method is defined externally in a standalone SW test source linked to
+ * `main` as a library. It is a fully contained test in itself.
+ *
+ * @return success or failure of the test in boolean.
+ */
+bool test_main(void);
+
+#endif  // OPENTITAN_SW_DEVICE_LIB_TESTING_TEST_MAIN_H_
diff --git a/sw/device/tests/main.c b/sw/device/tests/main.c
new file mode 100644
index 0000000..79eadda
--- /dev/null
+++ b/sw/device/tests/main.c
@@ -0,0 +1,26 @@
+// Copyright lowRISC contributors.
+// Licensed under the Apache License, Version 2.0, see LICENSE for details.
+// SPDX-License-Identifier: Apache-2.0
+
+#include "sw/device/lib/base/log.h"
+#include "sw/device/lib/base/print.h"
+#include "sw/device/lib/testing/test_main.h"
+#include "sw/device/lib/testing/test_status.h"
+#include "sw/device/lib/uart.h"
+
+int main(int argc, char **argv) {
+  test_status_set(kTestStatusInTest);
+
+  // Initialize the UART to enable logging for non-DV simulation platforms.
+  if (kDeviceType != kDeviceSimDV) {
+    uart_init(kUartBaudrate);
+    base_set_stdout(uart_stdout);
+  }
+
+  // Run the SW test which is fully contained within `test_main()`.
+  bool result = test_main();
+  test_status_set(result ? kTestStatusPassed : kTestStatusFailed);
+
+  // Unreachable code.
+  return 1;
+}
diff --git a/sw/device/tests/meson.build b/sw/device/tests/meson.build
index b91fa38..9d7af2d 100644
--- a/sw/device/tests/meson.build
+++ b/sw/device/tests/meson.build
@@ -2,10 +2,60 @@
 # Licensed under the Apache License, Version 2.0, see LICENSE for details.
 # SPDX-License-Identifier: Apache-2.0
 
-subdir('dif')
+sw_tests = {}
 
-subdir('aes')
+subdir('dif')
 subdir('flash_ctrl')
 subdir('hmac')
 subdir('rv_timer')
 subdir('consecutive_irqs')
+
+aes_test_lib = declare_dependency(
+  link_with: static_library(
+    'aes_test_lib',
+    sources: ['aes_test.c'],
+    dependencies: [
+      sw_lib_aes,
+      sw_lib_uart,
+      sw_lib_mem,
+    ],
+  ),
+)
+
+sw_tests += {
+  'aes_test': aes_test_lib
+}
+
+foreach sw_test_name, sw_test_lib : sw_tests
+  foreach device_name, device_lib : sw_lib_arch_core_devices
+    sw_test_elf = executable(
+      sw_test_name + '_' + device_name,
+      sources: ['main.c'],
+      name_suffix: 'elf',
+      dependencies: [
+        riscv_crt,
+        sw_lib_base_log,
+        sw_test_lib,
+        device_lib,
+        sw_lib_testing_test_status,
+      ],
+    )
+
+    sw_test_embedded = custom_target(
+      sw_test_name + '_' + device_name,
+      command: make_embedded_target,
+      input: sw_test_elf,
+      output: make_embedded_target_outputs,
+      build_by_default: true,
+    )
+
+    custom_target(
+      sw_test_name + '_export_' + device_name,
+      command: export_embedded_target,
+      input: [sw_test_elf, sw_test_embedded],
+      output: sw_test_name + '_export_' + device_name,
+      build_always_stale: true,
+      build_by_default: true,
+    )
+  endforeach
+endforeach