با قسمت نوزدهم از دوره ورود به دنیای امبدد لینوکس که دوره مقدماتی آموزش امبدد لینوکس می باشد همراه ما باشید.
در قسمت قبل به بررسی نحوه کانفیگ و کامپایل کرنل لینوکس برای رزبری پای پرداختیم و در این قسمت به سراغ device tree میریم و با آن بیشتر آشنا میشویم.
شاید برای شما مفید باشد: آموزش رزبری پای از 0 تا 100
دیوایس تری Device Tree
تا حالا اسم این بزرگوار رو زیاد شنیدین، حالا ببینیم چی هست این درخت دستگاه!
ایده به خیلی قبلترها و PowerPC ها برمیگرده.
چرا باید برای هر برد جدا کرنل لینوکس رو build کنیم؟
نمیشه یه کرنل لینوکس باشه و روی خیلی از بردها قابلاستفاده باشه؟
توی فصل دوم یاد گرفتیم که معماری پردازنده مهم هست برای تولچین و با دانستن معماری میتوانیم برنامه اجرایی داشته باشیم که روی پردازندههایی که توسط شرکتهای مختلف ساختهشدن قابلاجرا باشه.
– حالا چرا این برنامه اجرایی خود کرنل لینوکس نباشه؟ چه مشکلی پیش میاد مگه؟
+ درایورها و سختافزارهای هر برد و پردازنده متفاوت هستن.
– خب اطلاعات مربوط به اونا رو جدا به کرنل لینوکس میدهیم
+ سلام دیوایس تری!
فایلهای دیوایس تری در حقیقت توضیحات استاتیک از سختافزار و وسایل جانبی کامیپوتر یا برد ما هستن که توسط کامپایلر خودشون کامپایل میشن و تبدیل به فایلی قابلفهم توسط لینوکس میشن.
دیوایس تری Device Tree معمولاً توسط بوت لودر توی رم لود میشه و آدرسش به کرنل لینوکس داده میشه ولی میتونه به خود کرنل لینوکس هم بچسبه.
سینتکس دیوایس تری از بوت لودر Open Boot مشتق شده و میتونید جزییات کاملش رو اینجا ببینید.
اگه یه سر به فولدر زیر بندازین فایلهای زیادی با پسوند dts و dtsi میبینید:
1 | arch/$ARCH/boot/dts |
حتی بعضی از شرکتها توی این فولدر خودشون یه فولدر دارن و توی اون این فایلهای دیوایس تری هست.
فایلهایی با پسوند dts فایلهای سورس دیوایس تری هستن و فایلهایی با پسوند dtsi فایلهای سرایند یا همان هدر خودمون هستند که توی بقیه dts ها استفاده شدن.
ممکنه توی بعضی از این فایلها ببینید هدرهای C فراخوانی شده، توی بعضیها ببینید dts فراخوانی شده. مهم نیست همه رو میتونیم انجام بدیم و محدودیت نداریم.
برای فراخوانی فایل هم ممکنه این دو مدل syntax رو ببینید:
1 2 | #include “at00.dtsi” /include/ “at00.dtsi” |
هر دوشون درست هستن و یه کار رو میکنن.
قبل از اینکه بریم و فایل dts برد خودمون رو ببینیم باید با ساختار include توی دیوایس تری Device Tree آشنا بشیم.
قواعد فراخوانی در درخت دستگاه
فرض کنیم فایل at00.dts رو باز کردیم و اینا توش هست:
1 2 3 | #include “at00-1.dtsi” #include “at00-2.dtsi” #include “at00-3.dts” |
توی هر کدوم از این سه تا فایل هم یک سری دستگاهها با یه سری ویژگی تعریف شدن که بینشون دستگاههای مشترک زیاد هست. حتی بعضیهاشون توی فایل at00.dts باز هستن با یه ویژگیهای جدید یا متفاوت با قبلیها.
ازنظر کرنل لینوکس و بعد از کامپیال شدن فایل دیوایس تری اون دستگاه تمام ویژگیهایی که توی این فایلها هست رو داره و آگه ویژگی توی چند تا فایل با مقادیر مختلف وجود داره اولویت با آخرین فایل هست.
بررسی دیوایس تری Device Tree
بیاید فایل دیوایس تری Device Tree رسپبریپای رو بازکنیم ببینم چی توش هست:
1 2 | cd ~/EmbeddedLinux/RPI3BP/kernel/linux gedit arch/arm64/boot/dts/broadcom/bcm2837-rpi-3-b-plus.dts |
بهبه چقدر زیبا! پر از خالی!
1 2 | // SPDX-License-Identifier: GPL-2.0 #include "arm/bcm2837-rpi-3-b-plus.dts" |
این یعنی ما باید بریم و این فایل رو باز کنیم:
1 | gedit arch/arm/bcm2837-rpi-3-b-plus.dts |
که وجود نداره! ولی چون بچههای باهوشی هستیم میدونیم باید این فایل رو باز کنیم:
1 | gedit arch/arm/boot/dts/bcm2837-rpi-3-b-plus.dts |
اولش یه سری فراخوانی داریم که یعنی خیلی از قسمتها توی فایلهای دیگر تعریف شدن. فایل دیوایس تری پر از نود هست و نود اصلی با اسلش مشخص میشه. هر نود یه سری زیرمجموعه داره و اون زیرمجموعهها یه سری پارامتر دارن که با فرمت name=”value” مشخص شدن.
نود اصلی (ریشه) یه متغیر داره به نام compatible، این متغیر رو ممکنه واسه خیلی نودها ببینید و خب خیلی مهم هست.
توی فایلهای درایور لینوکس یه استارکچر هست به نام of_device_id کرنل لینوکس با استفاده از این متغیر compatible توی درایورهای مختلف به دنبال سازگارترین درایور میگرده و همون رو استفاده میکنه.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | / SPDX-License-Identifier: GPL-2.0 /dts-v1/; #include "bcm2837.dtsi" #include "bcm2836-rpi.dtsi" #include "bcm283x-rpi-lan7515.dtsi" #include "bcm283x-rpi-usb-host.dtsi" / { compatible = "raspberrypi,3-model-b-plus", "brcm,bcm2837"; model = "Raspberry Pi 3 Model B+"; chosen { /* 8250 auxiliary UART instead of pl011 */ stdout-path = "serial1:115200n8"; }; memory@0 { device_type = "memory"; reg = <0 0x40000000>; }; leds { led-act { gpios = <&gpio 29 GPIO_ACTIVE_HIGH>; }; led-pwr { label = "PWR"; gpios = <&expgpio 2 GPIO_ACTIVE_LOW>; default-state = "keep"; linux,default-trigger = "default-on"; }; }; wifi_pwrseq: wifi-pwrseq { compatible = "mmc-pwrseq-simple"; reset-gpios = <&expgpio 1 GPIO_ACTIVE_LOW>; }; }; |
یه متغیر دیگه که مهم هست reg هست. reg معمولاً دو تا مقدار داره مقدار اول آدرس فیزیکی اون نود هست و مقدار دوم سایزش:
1 | gedit arch/arm/boot/dts/bcm2837.dtsi |
نود cpus
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | cpus: cpus { #address-cells = <1>; #size-cells = <0>; enable-method = "brcm,bcm2836-smp"; // for ARM 32-bit cpu0: cpu@0 { device_type = "cpu"; compatible = "arm,cortex-a53"; reg = <0>; enable-method = "spin-table"; cpu-release-addr = <0x0 0x000000d8>; }; cpu1: cpu@1 { device_type = "cpu"; compatible = "arm,cortex-a53"; reg = <1>; enable-method = "spin-table"; cpu-release-addr = <0x0 0x000000e0>; }; cpu2: cpu@2 { device_type = "cpu"; compatible = "arm,cortex-a53"; reg = <2>; enable-method = "spin-table"; cpu-release-addr = <0x0 0x000000e8>; }; cpu3: cpu@3 { device_type = "cpu"; compatible = "arm,cortex-a53"; reg = <3>; enable-method = "spin-table"; cpu-release-addr = <0x0 0x000000f0>; }; }; |
این نود میگه ما یه cpu داریم که خودش چهارتا زیر مجموعه داره.
1 2 | #address-cells = <1>; #size-cells = <0>; |
این دو متغیر در نود مادر مشخص میکنند که برای متغیر reg در نودهای فرزند وضعیت آدرس و سایز چه جوری هست.
آدرس میتونه ۳۲ یا ۶۴ بیتی باشه و سایز هم میتونه بدون مقدار، ۳۲ و یا ۶۴ بیتی باشه و مقادیر این دو متغیر ۰ و ۱ و ۲ میتونه باشه.
آگه جایی این دو متغیر نبودن مقدار پیش فرضشون ۱ هست.
مثلاً توی فایل قبل و نود memory:
1 2 3 4 | memory@0 { device_type = "memory"; reg = <0 0x40000000>; }; |
ما یک حافظهداریم که از آدرس صفر شروع میشه و سایزش 0x40000000 بایت یا همون یک گیگ خودمون هست.
به این قسمت توجه کنید cpu0: cpu@0 به cpu0 لیبل میگن، لیبلها واسه این هستن که بعداً آگه جایی ما خواستیم رفرنس بدیم به این نود از اون لیبل استفاده کنیم.
@ هم آگه نودمون متغیر reg داشت حتماً باید باشه و عدد بعدش معمولاً آدرس اون متغیر هست.
متغیر مهم بعدی device_type هست که خیلی گزینهها میتونه داشته باشه که واسه ما سختافزاریها معمولاً قابلفهم هستن.
داستان دیوایس تریز خیلی مفصل هست ولی واسه ما تا همین حد کافیه.
من تابهحال کتابی واسش ندیدم جستهگریخته کتابهای امبددلینوکس و درایور لینوکس هر کدوم یه سری مطالب در موردش گفتن.
آگه علاقهمند بودین و سرچ کردین و کتابی رو پیدا کردین به من هم اطلاع بدین.
یوبوت-کرنل لینوکس Linux Kernel-دیوایس تریز: پرتاب
خیلی از بردها رو میتونیم با یوبوت و داشتن کرنل لینوکس و دیوایس تریز با یه روت سیستم ساده استفاده کنیم.
این کار رو توی فصل قبل انجام دادیم اینجا یکم ملموستر شد چون یه مرحله رفتیم جلو و البته که توی فصل بعد خیلی ملموس میشه.
توی فصل قبل گفتیم که رسپبریپای از بوت لودر خودش استفاده میکنه و خیلی با یوبوت سازگار نیست!
الان اینجا میخوایم با بوت لودر خودش کار رو کامل انجام بدیم.
خب اولش توی کارت حافظهای که داشتیم بوت لودرهای رسپبری رو میریزیم و بعدش کرنل لینوکس خودمون رو با فایل های همراهش البته.
با فرض اینکه آدرس کارت حافظه /dev/sdc هست.
1 2 | umount /dev/sdc* sudo mount /dev/sdc1 /mnt |
اول بوت لودر رسپبری:
1 2 | cd ~/EmbeddedLinux/RPI3BP/bootldr sudo cp ./firmware-master/boot/{fixup.dat,start.elf,bootcode.bin} /mnt/ |
حالا بریم سراغ کرنل لینوکس خودمون و فایل هاش (فایل ها از داکیومنتهای خود رسپبری میاد و چیزی نیست که من علمش رو از قبل داشته بوده باشم):
1 2 3 4 5 | cd ../kernel/linux sudo cp arch/arm64/boot/Image /mnt/kernel8.img sudo mkdir /mnt/overlays sudo cp arch/arm64/boot/dts/overlays/*.dtbo /mnt/overlays/ sudo cp arch/arm64/boot/dts/broadcom/*.dtb /mnt/ |
و در نهایت دو فایل config.txt و cmdline.xtx رو میسازیم:
1 | sudo vi /mnt/config.txt |
1 2 3 4 5 6 7 | arm_64bit=1 disable_overscan=1 gpu_mem_256=100 gpu_mem_512=100 gpu_mem_1024=100 dtoverlay=miniuart-bt dtoverlay=krnbt=on |
اگه واستون سؤال هست یا علاقهمندین که بدونین این خطوط از کجا اومده این لینک رو ببنید
1 2 3 | sudo vi /mnt/cmdline.txt root=/dev/mmcblk0p2 rootwait console=tty1 console=ttyAMA0,115200 sudo umount /dev/sdc* |
کارت حافظه رو روی برد بذاریم و برد رو روشن کنیم.
توی فصل قبل هم ما یه اجرای لینوکس با رسپبری داشتیم منتهی با یوبوت و فایل کرنلی که من بهتون داده بودم.
اون فایل کرنل لینوکس ، مستقیم از سایت کرنل لینوکس گرفته شده بود و ساخته شده بود و فایل اضافه ای هم نداشت.
تفاوت اون کرنل لینوکس با این کرنل لینوکس و بوت لودر خود رسپبریپای توی این هست که اونجا از یوبوت به بعد روی سریال ما چیزی نداشتیم ولی اینجا همه اطلاعات رو به ما میده.
اون جا اگه LCD وصل میکردین اطلاعات رو داشتین روش ولی روی سریال نه.
بله این است تفاوت فایل های کانفیگ!
این جز معدود جاهایی هست که من دارم مفصل حرف میزنم و خودم مقایسه میکنم و نمیگم برید انجام بدین یا سرچ کنین.
و اما چرا؟
بله میشه ساعتها وقت گذاشت و دیوایس تری رو درست کرد و یوبوت رو تغییر داد و … اما ارزش نداره.
توی کار عملی و انجام پروژههای الکترونیک دنبال اثبات کردن تواناییتون نباشین دنبال انجام دادن پروژه و به دست آوردن پول باشین!
مباحث پیشرفته کرنل لینوکس Linux Kernel
کرنل پنیک Kernel Panic
محتویات پورت سریال بعد از روشن کردن برد ایناست:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | [ 0.000000] Booting Linux on physical CPU 0x0000000000 [0x410fd034] [ 0.000000] Linux version 5.10.103-v8-aniroot.1.0+ (at00@at00-LIFEBOOK-AH544) (aarch64-linux-gnu-gcc (Linaro GCC 7.5-2019.12) 7.5.0, GNU ld (Linar2 [ 0.000000] random: fast init done [ 0.000000] Machine model: Raspberry Pi 3 Model B Plus Rev 1.3 [ 0.000000] efi: UEFI not found. ... [ 4.515138] of_cfs_init [ 4.520420] of_cfs_init: OK [ 4.531463] Waiting for root device /dev/mmcblk0p2... [ 4.544114] mmc1: queuing unknown CIS tuple 0x80 (2 bytes) [ 4.554231] mmc1: queuing unknown CIS tuple 0x80 (3 bytes) [ 4.565560] mmc1: queuing unknown CIS tuple 0x80 (3 bytes) [ 4.577785] mmc1: queuing unknown CIS tuple 0x80 (7 bytes) [ 4.604038] usb 1-1: new high-speed USB device number 2 using dwc_otg [ 4.614652] Indeed it is in host mode hprt0 = 00001101 [ 4.626985] mmc0: host does not support reading read-only switch, assuming write-enable [ 4.648979] mmc0: new high speed SDHC card at address 0001 [ 4.667502] mmcblk0: mmc0:0001 SD 3.72 GiB [ 4.704168] mmc1: new high speed SDIO card at address 0001 [ 4.724576] mmcblk0: p1 p2 [ 4.742180] EXT4-fs (mmcblk0p2): INFO: recovery required on readonly filesystem [ 4.752231] EXT4-fs (mmcblk0p2): write access will be enabled during recovery [ 4.765109] EXT4-fs (mmcblk0p2): recovery complete [ 4.777388] EXT4-fs (mmcblk0p2): mounted filesystem with ordered data mode. Opts: (null) [ 4.790552] VFS: Mounted root (ext4 filesystem) readonly on device 179:2. [ 4.802450] devtmpfs: error mounting -2 [ 4.818249] Freeing unused kernel memory: 3456K [ 4.825602] Run /sbin/init as init process [ 4.832704] Run /etc/init as init process [ 4.839503] Run /bin/init as init process [ 4.846189] Run /bin/sh as init process [ 4.852766] usb 1-1: New USB device found, idVendor=0424, idProduct=2514, bcdDevice= b.b3 [ 4.852809] usb 1-1: New USB device strings: Mfr=0, Product=0, SerialNumber=0 [ 4.866074] Kernel panic - not syncing: No working init found. Try passing init= option to kernel. See Linux Documentation/admin-guide/init.rst f. [ 4.895452] CPU: 3 PID: 1 Comm: swapper/0 Not tainted 5.10.103-v8-aniroot.1.0+ #1 [ 4.908700] Hardware name: Raspberry Pi 3 Model B Plus Rev 1.3 (DT) [ 4.917956] Call trace: [ 4.923244] dump_backtrace+0x0/0x1c0 [ 4.929698] show_stack+0x2c/0x38 [ 4.935747] dump_stack+0xec/0x150 [ 4.941827] panic+0x194/0x3c0 [ 4.947507] kernel_init+0x108/0x118 [ 4.953687] ret_from_fork+0x10/0x1c [ 4.959795] SMP: stopping secondary CPUs [ 4.966238] Kernel Offset: 0x2f93400000 from 0xffffffc010000000 [ 4.974747] PHYS_OFFSET: 0xffffffd340000000 [ 4.981470] CPU features: 0x0040002,24802004 [ 4.988207] Memory Limit: none [ 4.993636] ---[ end Kernel panic - not syncing: No working init found. Try passing init= option to kernel. See Linux Documentation/admin-guide/i- |
اگه دقت کنید مشخصه که کرنل لینوکس داره دنبال یه چیزی میگرده که نیست.
بله دنبال فایل سیستم روت میگرده.
کرنل پنیک Kernel Panic زمانی هست که کرنل لینوکس به خطایی میرسه که واسش غیرقابلبازیابی هست و نمیتونه کاریش کنه که اینجا پیدا نکردن فایل سیستم روت هست.
پیغامهای کرنل لینوکس Linux Kernel
کرنل لینوکس توی هفت سطح پیغامهایی رو با استفاده از تابع printk به ما میده. (همونهایی که هنگام بوت شدن لینوکس روی اسکرین یا سریال میبنید، البته نه فقط همونا!)
توی پیکربندی کرنل یه متغیر هست به نام
1 | CONFIG_CONSOLE_LOGLEVEL_DEFAULT |
که هر خطایی که مقدارش از اون پایینتر یا برابر باشه رو نشون میده. میتونید تنظیمات مربوطه رو ازاینجا ببینید و تغییر بدید:
1 | > Kernel hacking > printk and dmesg option |
خط فرمان کرنل
توی فصل قبل گفتیم یکی از کارهای بوت لودر پاس دادن متغیرهای خط فرمان به کرنل لینوکس هست حالا این متغیرها که تا الان هم زیاد دیدینشون چیا هستن و به چه معنی هستن:
یه سری هم به نظرات این پایین بندازید، نظرات رو به خونید و اگر شما همنظری دارید، لطفاً با ما به اشتراک بگذارید!
بسیار عالی. لذت بردم.
سلام پیمان جان
خوشحالیم که دنبال کننده مطالب هستید و به بچه ها انرژی می دهید
آقا بازم گل کاشتید که??
سلامت و شاد باشید همیشه
سپاسگزارم
سلام امیر جان
تشکر بسیار از حسن نظر شما
شاد باشید