scripts: check_init_priorities: handle init and device decoupling

Fix check_init_priorities.py to handle the decoupling between SYS_INIT
and devices.

Signed-off-by: Fabio Baltieri <fabiobaltieri@google.com>
diff --git a/scripts/build/check_init_priorities.py b/scripts/build/check_init_priorities.py
index 582d7dd..288090b 100755
--- a/scripts/build/check_init_priorities.py
+++ b/scripts/build/check_init_priorities.py
@@ -105,8 +105,14 @@
     def __init__(self, file_path):
         self.file_path = file_path
         self._elf = ELFFile(open(file_path, "rb"))
+
+        self.initlevels = {}
+        for level in _DEVICE_INIT_LEVELS:
+            self.initlevels[level] = []
+
         self._load_objects()
         self._load_level_addr()
+        self._process_devices()
         self._process_initlevels()
 
     def _load_objects(self):
@@ -124,27 +130,37 @@
                     self._objects[sym.entry.st_value] = (
                             sym.name, sym.entry.st_size, sym.entry.st_shndx)
 
-    def _load_level_addr(self):
+    def _find_level_addr(self, prefix, levels):
         """Find the address associated with known init levels."""
-        self._init_level_addr = {}
+        addrs = {}
+        end = None
 
         for section in self._elf.iter_sections():
             if not isinstance(section, SymbolTableSection):
                 continue
 
             for sym in section.iter_symbols():
-                for level in _DEVICE_INIT_LEVELS:
-                    name = f"__init_{level}_start"
+                for level in levels:
+                    name = f"{prefix}_{level}_start"
                     if sym.name == name:
-                        self._init_level_addr[level] = sym.entry.st_value
-                    elif sym.name == "__init_end":
-                        self._init_level_end = sym.entry.st_value
+                        addrs[level] = sym.entry.st_value
+                    elif sym.name == f"{prefix}_end":
+                        end = sym.entry.st_value
 
-        if len(self._init_level_addr) != len(_DEVICE_INIT_LEVELS):
-            raise ValueError(f"Missing init symbols, found: {self._init_level_addr}")
+        if len(addrs) != len(levels):
+            raise ValueError(f"Missing level symbols, found: {addrs}")
 
-        if not self._init_level_end:
-            raise ValueError(f"Missing init section end symbol")
+        if not end:
+            raise ValueError(f"Missing level section end symbol")
+
+        return addrs, end
+
+    def _load_level_addr(self):
+        """Load the address level for both init and device sections."""
+        self._init_level_addr, self._init_level_end = self._find_level_addr(
+                "__init", _DEVICE_INIT_LEVELS)
+        self._device_level_addr, self._device_level_end = self._find_level_addr(
+                "_device_list", _DEVICE_INIT_LEVELS)
 
     def _device_ord_from_name(self, sym_name):
         """Find a device ordinal from a symbol name."""
@@ -185,19 +201,16 @@
 
         return int.from_bytes(data[start:stop], byteorder="little")
 
-    def _process_initlevels(self):
-        """Process the init level and find the init functions and devices."""
+    def _process_devices(self):
+        """Process the init level and find the devices."""
         self.devices = {}
-        self.initlevels = {}
 
         for i, level in enumerate(_DEVICE_INIT_LEVELS):
-            start = self._init_level_addr[level]
+            start = self._device_level_addr[level]
             if i + 1 == len(_DEVICE_INIT_LEVELS):
-                stop = self._init_level_end
+                stop = self._device_level_end
             else:
-                stop = self._init_level_addr[_DEVICE_INIT_LEVELS[i + 1]]
-
-            self.initlevels[level] = []
+                stop = self._device_level_addr[_DEVICE_INIT_LEVELS[i + 1]]
 
             priority = 0
             addr = start
@@ -206,12 +219,11 @@
                     raise ValueError(f"no symbol at addr {addr:08x}")
                 obj, size, shidx = self._objects[addr]
 
-                arg0_name = self._object_name(self._initlevel_pointer(addr, 0, shidx))
-                arg1_name = self._object_name(self._initlevel_pointer(addr, 1, shidx))
+                init_name = self._object_name(self._initlevel_pointer(addr, 5, shidx))
 
-                self.initlevels[level].append(f"{obj}: {arg0_name}({arg1_name})")
+                self.initlevels[level].append(f"DEVICE   {init_name}({obj})")
 
-                ordinal = self._device_ord_from_name(arg1_name)
+                ordinal = self._device_ord_from_name(obj)
                 if ordinal:
                     prio = Priority(level, priority)
                     self.devices[ordinal] = prio
@@ -219,6 +231,27 @@
                 addr += size
                 priority += 1
 
+    def _process_initlevels(self):
+        """Process the init level and find the init functions."""
+        for i, level in enumerate(_DEVICE_INIT_LEVELS):
+            start = self._init_level_addr[level]
+            if i + 1 == len(_DEVICE_INIT_LEVELS):
+                stop = self._init_level_end
+            else:
+                stop = self._init_level_addr[_DEVICE_INIT_LEVELS[i + 1]]
+
+            addr = start
+            while addr < stop:
+                if addr not in self._objects:
+                    raise ValueError(f"no symbol at addr {addr:08x}")
+                _, size, shidx = self._objects[addr]
+
+                init_name = self._object_name(self._initlevel_pointer(addr, 0, shidx))
+
+                self.initlevels[level].append(f"SYS_INIT {init_name}()")
+
+                addr += size
+
 class Validator():
     """Validates the initialization priorities.
 
diff --git a/scripts/build/check_init_priorities_test.py b/scripts/build/check_init_priorities_test.py
index 6e40422..c48d6c1 100755
--- a/scripts/build/check_init_priorities_test.py
+++ b/scripts/build/check_init_priorities_test.py
@@ -89,7 +89,7 @@
         self.assertDictEqual(obj._objects, {0xaa: ("a", 4, 1), 0xbb: ("b", 8, 2)})
 
     @mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None)
-    def test_load_level_addr(self, mock_zilinit):
+    def test_find_level_addr(self, mock_zilinit):
         mock_elf = mock.Mock()
 
         sts = mock.Mock(spec=SymbolTableSection)
@@ -97,40 +97,40 @@
         mock_elf.iter_sections.return_value = [sts, rel]
 
         s0 = mock.Mock()
-        s0.name = "__init_EARLY_start"
+        s0.name = "prefix_EARLY_start"
         s0.entry.st_value = 0x00
 
         s1 = mock.Mock()
-        s1.name = "__init_PRE_KERNEL_1_start"
+        s1.name = "prefix_PRE_KERNEL_1_start"
         s1.entry.st_value = 0x11
 
         s2 = mock.Mock()
-        s2.name = "__init_PRE_KERNEL_2_start"
+        s2.name = "prefix_PRE_KERNEL_2_start"
         s2.entry.st_value = 0x22
 
         s3 = mock.Mock()
-        s3.name = "__init_POST_KERNEL_start"
+        s3.name = "prefix_POST_KERNEL_start"
         s3.entry.st_value = 0x33
 
         s4 = mock.Mock()
-        s4.name = "__init_APPLICATION_start"
+        s4.name = "prefix_APPLICATION_start"
         s4.entry.st_value = 0x44
 
         s5 = mock.Mock()
-        s5.name = "__init_SMP_start"
+        s5.name = "prefix_SMP_start"
         s5.entry.st_value = 0x55
 
         s6 = mock.Mock()
-        s6.name = "__init_end"
+        s6.name = "prefix_end"
         s6.entry.st_value = 0x66
 
         sts.iter_symbols.return_value = [s0, s1, s2, s3, s4, s5, s6]
 
         obj = check_init_priorities.ZephyrInitLevels("")
         obj._elf = mock_elf
-        obj._load_level_addr()
+        addrs, end = obj._find_level_addr("prefix", check_init_priorities._DEVICE_INIT_LEVELS)
 
-        self.assertDictEqual(obj._init_level_addr, {
+        self.assertDictEqual(addrs, {
             "EARLY": 0x00,
             "PRE_KERNEL_1": 0x11,
             "PRE_KERNEL_2": 0x22,
@@ -138,7 +138,7 @@
             "APPLICATION": 0x44,
             "SMP": 0x55,
             })
-        self.assertEqual(obj._init_level_end, 0x66)
+        self.assertEqual(end, 0x66)
 
     @mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None)
     def test_device_ord_from_name(self, mock_zilinit):
@@ -194,8 +194,56 @@
     @mock.patch("check_init_priorities.ZephyrInitLevels._object_name")
     @mock.patch("check_init_priorities.ZephyrInitLevels._initlevel_pointer")
     @mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None)
+    def test_process_devices(self, mock_zilinit, mock_ip, mock_on):
+        obj = check_init_priorities.ZephyrInitLevels("")
+        obj.initlevels = {
+                "PRE_KERNEL_2": [],
+                "POST_KERNEL": [],
+                }
+        obj._device_level_addr = {
+            "EARLY": 0x00,
+            "PRE_KERNEL_1": 0x00,
+            "PRE_KERNEL_2": 0x00,
+            "POST_KERNEL": 0x08,
+            "APPLICATION": 0x0c,
+            "SMP": 0x0c,
+            }
+        obj._device_level_end = 0x0c
+        obj._objects = {
+                0x00: ("__device_dts_ord_11", 4, 0),
+                0x04: ("__device_dts_ord_22", 4, 0),
+                0x08: ("__device_dts_ord_33", 4, 0),
+                }
+
+        mock_ip.side_effect = lambda *args: args
+        mock_on.side_effect = lambda *args: f"dev_init_fn_{args[0][0]}"
+
+        obj._process_devices()
+
+        self.assertDictEqual(obj.initlevels, {
+            "PRE_KERNEL_2": [
+                "DEVICE   dev_init_fn_0(__device_dts_ord_11)",
+                "DEVICE   dev_init_fn_4(__device_dts_ord_22)",
+                ],
+            "POST_KERNEL": [
+                "DEVICE   dev_init_fn_8(__device_dts_ord_33)",
+                ],
+            })
+        self.assertDictEqual(obj.devices, {
+            11: check_init_priorities.Priority("PRE_KERNEL_2", 0),
+            22: check_init_priorities.Priority("PRE_KERNEL_2", 1),
+            33: check_init_priorities.Priority("POST_KERNEL", 0),
+            })
+
+    @mock.patch("check_init_priorities.ZephyrInitLevels._object_name")
+    @mock.patch("check_init_priorities.ZephyrInitLevels._initlevel_pointer")
+    @mock.patch("check_init_priorities.ZephyrInitLevels.__init__", return_value=None)
     def test_process_initlevels(self, mock_zilinit, mock_ip, mock_on):
         obj = check_init_priorities.ZephyrInitLevels("")
+        obj.initlevels = {
+                "PRE_KERNEL_2": [],
+                "POST_KERNEL": [],
+                }
         obj._init_level_addr = {
             "EARLY": 0x00,
             "PRE_KERNEL_1": 0x00,
@@ -212,32 +260,18 @@
                 }
 
         mock_ip.side_effect = lambda *args: args
-
-        def mock_obj_name(*args):
-            if args[0] == (0, 0, 0):
-                return "i0"
-            elif args[0] == (0, 1, 0):
-                return "__device_dts_ord_11"
-            elif args[0] == (4, 0, 0):
-                return "i1"
-            elif args[0] == (4, 1, 0):
-                return "__device_dts_ord_22"
-            return f"name_{args[0][0]}_{args[0][1]}"
-        mock_on.side_effect = mock_obj_name
+        mock_on.side_effect = lambda *args: f"init_fn_{args[0][0]}"
 
         obj._process_initlevels()
 
         self.assertDictEqual(obj.initlevels, {
-            "EARLY": [],
-            "PRE_KERNEL_1": [],
-            "PRE_KERNEL_2": ["a: i0(__device_dts_ord_11)", "b: i1(__device_dts_ord_22)"],
-            "POST_KERNEL": ["c: name_8_0(name_8_1)"],
-            "APPLICATION": [],
-            "SMP": [],
-            })
-        self.assertDictEqual(obj.devices, {
-            11: check_init_priorities.Priority("PRE_KERNEL_2", 0),
-            22: check_init_priorities.Priority("PRE_KERNEL_2", 1),
+            "PRE_KERNEL_2": [
+                "SYS_INIT init_fn_0()",
+                "SYS_INIT init_fn_4()",
+                ],
+            "POST_KERNEL": [
+                "SYS_INIT init_fn_8()",
+                ],
             })
 
 class testValidator(unittest.TestCase):