Commit db747c08 authored by Fabio Baltieri's avatar Fabio Baltieri
Browse files

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: default avatarFabio Baltieri <fabiobaltieri@google.com>
parent c55650c2
Loading
Loading
Loading
Loading
+56 −23
Changes for scripts/build/check_init_priorities.py: 56 added lines, 23 removed lines.
Original line number Diff line number Diff line
@@ -105,8 +105,14 @@ class ZephyrInitLevels:
    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 @@ class ZephyrInitLevels:
                    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 @@ class ZephyrInitLevels:

        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 @@ class ZephyrInitLevels:
                    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 @@ class ZephyrInitLevels:
                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.

+67 −33
Changes for scripts/build/check_init_priorities_test.py: 67 added lines, 33 removed lines.
Original line number Diff line number Diff line
@@ -89,7 +89,7 @@ class testZephyrInitLevels(unittest.TestCase):
        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 @@ class testZephyrInitLevels(unittest.TestCase):
        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 @@ class testZephyrInitLevels(unittest.TestCase):
            "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):
@@ -191,11 +191,59 @@ class testZephyrInitLevels(unittest.TestCase):
        self.assertEqual(obj._initlevel_pointer(0x108, 0, 0), 2)
        self.assertEqual(obj._initlevel_pointer(0x108, 1, 0), 3)

    @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 @@ class testZephyrInitLevels(unittest.TestCase):
                }

        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):