I have an embedded Linux system with 2 built-in serial ports, and a PCI board that adds 8 more.

The kernel enumerates the devices in a way that the port numbers end up getting interleaved, for instance (from dmesg):

[    5.964467] 0000:03:00.0: ttyS4 at MMIO 0xd0600000 (irq = 105, base_baud = 7812500) is a XR17V35X
[    5.964934] 0000:03:00.0: ttyS5 at MMIO 0xd0600400 (irq = 105, base_baud = 7812500) is a XR17V35X
[    5.965213] 0000:03:00.0: ttyS6 at MMIO 0xd0600800 (irq = 105, base_baud = 7812500) is a XR17V35X
[    5.965519] 0000:03:00.0: ttyS7 at MMIO 0xd0600c00 (irq = 105, base_baud = 7812500) is a XR17V35X
[    5.965879] 0000:03:00.0: ttyS8 at MMIO 0xd0601000 (irq = 105, base_baud = 7812500) is a XR17V35X
[    5.966755] 0000:00:1e.3: ttyS9 at MMIO 0xd091c000 (irq = 18, base_baud = 2764800) is a 16550A
[    5.967123] 0000:03:00.0: ttyS10 at MMIO 0xd0601400 (irq = 105, base_baud = 7812500) is a XR17V35X
[    5.967411] 0000:03:00.0: ttyS11 at MMIO 0xd0601800 (irq = 105, base_baud = 7812500) is a XR17V35X
[    5.967705] 0000:03:00.0: ttyS12 at MMIO 0xd0601c00 (irq = 105, base_baud = 7812500) is a XR17V35X
[    5.976690] 0000:00:1e.4: ttyS13 at MMIO 0xd091a000 (irq = 19, base_baud = 2764800) is a 16550A

Here the built-in ports got assigned the names ttyS9 and ttyS13 and the expansion ports got ttyS4-8 and ttyS10-12. On the next reboot, the assignment will vary.

Because the port names get shuffled around, I don't have a stable identifier for my kernel command line option console=ttyS4,115200 or for getty.

I can write udev rules that create stable symlinks which might work for getty but don't work for the kernel's command line.

I have tried using the MMIO address for the Linux command line, as in console=uart,mmio32,0xd091c000,115200 and while the system boots, I don't seem to be receiving any kernel output.

How can I stabilize these identifiers?

2 Answers 2


I'm not sure but if you haven't tried already, perhaps try changing the biosdevname=0|1 argument for the kernel ?

Its value is usually changed in GRUB_CMDLINE_LINUX prior to running grub2-mkconfig on pc's, that part may well be different in your embedded situation ofcourse.

  • Is this option specifically for network interface devices? It doesn't seem to apply (and I checked the kernel sources and it is not documented and doesn't appear in the Linux source).
    – rgov
    Mar 2, 2021 at 5:36
  • I'm not sure. Network interface naming is indeed the thing i have used it for in the past, and likely what most people use it for; but the name isn't particularly network-specific so flipping that would have been my first attempt in your situation.
    – Raxi
    Mar 3, 2021 at 3:28

My partial workaround (not an ideal solution) is to have a udev helper script that looks at the kernel's device name, e.g., ttyS5, and looks up the device's place in the PCI device tree by resolving the symlink /sys/class/tty/ttyS5. Then it looks at all other TTY devices on the same node, and takes the index of this device amongst its peers. The assumption is that the order of devices within this node is stable, even if the device names are not.

#!/usr/bin/env python3
import argparse
import os

# Parse arguments
parser = argparse.ArgumentParser()
args = parser.parse_args()

# Find peer devices of this one
sysfspath = os.path.realpath(f"/sys/class/tty/{args.kernel_dev}")
peer_devices = os.listdir(os.path.dirname(sysfspath))
peer_devices.sort(key=lambda d: (len(d), d))  # natural sort

# Print the index of this device among its peers
print(peer_devices.index(args.kernel_dev) + 1)

The udev rule then becomes:

... PROGRAM="/usr/bin/serial_device_index %k", SYMLINK+="ttyEXP%c" ...

This solution may need to be tweaked for other hardware configurations.

What I don't like

This just makes stable symlink names, it doesn't make stable names which might be useful for things like assigning a device to the kernel's console parameter, or using Ubuntu's getty login console systemd services.

  • Actually, stable symlink names are the preferred method to deal with this - it's the same principle udev uses for event layer input devices, harddisks, etc. And use udevadm to have a look at the attributes provided inside udev (which ultimately come from /sys), you may be able to do this with a simple rule instead of a script.
    – dirkt
    Mar 2, 2021 at 6:44

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .