Linux: Get Hardware Model Name

system-information

I am writing an application that works like Neofetch when a -w option is passed. It shows some of the the system information like memory, swap, cpu, battery usages, hostname, local ip, kernel version etc.

I am wondering how to get the "Host" like in Neofetch. For example:

                   -`                    sourav@archlinux-arm 
                  .o+`                   -------------------- 
                 `ooo/                   OS: Arch Linux armv7l 
                `+oooo:                  Host: Raspberry Pi 3 Model B Rev 1.2 
               `+oooooo:                 Kernel: 4.19.108-1-ARCH 
               -+oooooo+:                Uptime: 10 mins 
             `/:-:++oooo+:               Packages: 804 (pacman) 
            `/++++/+++++++:              Shell: bash 5.0.16 
           `/++++++++++++++:             Resolution: 1366x768 
          `/+++ooooooooooooo/`           DE: Xfce 
         ./ooosssso++osssssso+`          WM: Xfwm4 
        .oossssso-````/ossssss+`         WM Theme: XFCE_Colour_Lite_Pink 
       -osssssso.      :ssssssso.        Theme: XFCE_Colour_Lite_Pink [GTK2], X 
      :osssssss/        osssso+++.       Icons: Papirus [GTK2], Tela-orange [GT 
     /ossssssss/        +ssssooo/-       Terminal: tilix 
   `/ossssso+/:-        -:/+osssso+-     CPU: BCM2835 (4) @ 1.350GHz 
  `+sso+:-`                 `.-/+oso:    Memory: 333MiB / 901MiB 
 `++:.                           `-/+/
 .`                                 `/                           

I get an information like this.
On my laptop:

                   -`                    sourav@archlinux 
                  .o+`                   ---------------- 
                 `ooo/                   OS: Arch Linux x86_64 
                `+oooo:                  Host: Inspiron 5567 
               `+oooooo:                 Kernel: 5.5.10-arch1-1 
               -+oooooo+:                Uptime: 3 hours 
             `/:-:++oooo+:               Packages: 1163 (pacman) 
            `/++++/+++++++:              Shell: bash 5.0.16 
           `/++++++++++++++:             Resolution: 1920x1080 
          `/+++ooooooooooooo/`           DE: Xfce 
         ./ooosssso++osssssso+`          WM: Xfwm4 
        .oossssso-````/ossssss+`         WM Theme: XFCE_Colour_Lite_Ruby 
       -osssssso.      :ssssssso.        Theme: XFCE_Colour_Lite_Purple [GTK2 
      :osssssss/        osssso+++.       Icons: Papirus [GTK2/3] 
     /ossssssss/        +ssssooo/-       Terminal: tilix 
   `/ossssso+/:-        -:/+osssso+-     CPU: Intel i3-6006U (4) @ 2.000GHz 
  `+sso+:-`                 `.-/+oso:    GPU: Intel Skylake GT2 [HD Graphics  
 `++:.                           `-/+/   Memory: 2814MiB / 3755MiB 
 .`                                 `/

My question is related to this question, but it doesn't answer my question because my raspberry pi can't run dmidecode, (no /sys/devices/virtual/dmi/ either), no lshw installed. Also, the /etc/hostname are not the computers' model name, instead they are just archlinux-arm and archlinux. The uname -a or cat /proc/version doesn't have the 'Rapsberry Pi' string on the raspberry pi.

Is there a way to get the hardware name like neofetch without using any dependency which should also run on most hardware?

Best Answer

There is no a portable, reliable, generic method to retrieve hardware model name in Linux. Let me describe 2 different cases: ARM-based Raspberry Pi with Raspbian installed and MIPS-based TP-LINK router with OpenWRT installed.

Raspberry Pi has an ARM CPU and ARM devices commonly use device-tree to describe hardware and Wikipedia article even mentions that it's mandatory since 2012. Device-tree structure is exposed to userspace and can be used for retrieving a model name by cating /proc/device-tree/model where /proc/device-tree itself is a symlink to /sys/firmware/devicetree/base like that (notice that there is no newline at the end of the device-tree files so we create a helper function called catn that cats the file and adds a newline):

pi@raspberrypi:~$ catn () { cat $1 && echo; }
pi@raspberrypi:~$ catn /proc/device-tree/model
Raspberry Pi 3 Model B Rev 1.2
pi@raspberrypi:~$ catn /sys/firmware/devicetree/base/model
Raspberry Pi 3 Model B Rev 1.2

or by manually dumping /sys/firmware/fdt flattened device-tree blob with dtc:

pi@raspberrypi:~$ sudo dtc /sys/firmware/fdt 2>/dev/null | grep model
    compatible = "raspberrypi,3-model-b\0brcm,bcm2837";
    model = "Raspberry Pi 3 Model B Rev 1.2";

If an official Raspberry Pi Linux fork is in use model is also written to /proc/cpuinfo:

pi@raspberrypi:~$ grep "^Model" /proc/cpuinfo
Model           : Raspberry Pi 3 Model B Rev 1.2

Also notice that the full name of the board - Raspberry Pi 3 Model B Rev 1.2 is constructed by low-level firmware and that you will not find a full string like that anywhere in the Linux kernel code:

pi@raspberrypi:~$ strings /boot/start.elf  | grep 'Raspberry Pi '
Raspberry Pi %s Rev %s
Raspberry Pi Bootcode

model is a standard device-tree property described in the DTSpec.

Other architectures such as RISC-V also use device tree to describe hardware but I don't have any RISC-V board to check.

There is no /proc/device-tree, no /sys/firmware/devicetree/base and no /sys/firmware/fdt on my TP-LINK router - it means that it either doesn't come with device-tree at all or that some appropriate Linux kernel config options have been disabled and device-tree is not exposed to the userspace. The former is, however, more likely as there is /tmp/sysinfo instead:

~ $ cat /tmp/sysinfo/board_name
tl-wdr4300
~ $ cat /tmp/sysinfo/model
TP-Link TL-WDR3600 v1

These values are generated by ar71xx.sh script which is rather long but you can see that name is assigned in line 1313:

*"TL-WDR3600/4300/4310")
        name="tl-wdr4300"
    ;;

based on TL-WDR4900 v2 which is in turn taken from machine field from /proc/cpuinfo:

machine=$(awk 'BEGIN{FS="[ \t]+:[ \t]"} /machine/ {print $2}' /proc/cpuinfo)

and is then assigned to AR71XX_BOARD_NAME and written to /tmp/sysinfo/board_name by the end of the script.

The full value of machine field in /proc/cpuinfo on this router is:

~ $ grep "^machine" /proc/cpuinfo
machine                 : TP-LINK TL-WDR3600/4300/4310

But Neofetch is not looking for /tmp/sysinfo/board_name, it's looking for /tmp/sysinfo/model. It's not taken from /proc/cpuinfo but read from the firmware flash partition:

~ $ cat /proc/mtd
dev:    size   erasesize  name
mtd0: 00020000 00010000 "u-boot"
mtd1: 0010c5a4 00010000 "kernel"
mtd2: 006c3a5c 00010000 "rootfs"
mtd3: 00490000 00010000 "rootfs_data"
mtd4: 00010000 00010000 "art"
mtd5: 007d0000 00010000 "firmware"
~ $ dd if=/dev/mtdblock5 bs=4 count=1 skip=16 2>/dev/null | hexdump -v -n 4 -e '1/1 "%02x"'  && echo
36000001

Model is assigned in line 321:

"360000"*)
    model="TP-Link TL-WDR3600"
    ;;

Of course it's hard to imagine that a generic program such as Neofetch would have so much knowledge about each firmware, its flash layout etc. I could however imagine a MIPS-based implementation that wouldn't support device-tree and wouldn't provide any useful hardware model information in /tmp/sysinfo and anywhere else and in such cases /proc/cpuinfo could be used as a last resort to get any information about hardware.

Related Question