در قسمت قبل به بررسی بوت لودر (U-boot) پرداختیم و اون رو بر روی سخت افزار خودمون اجرا کردیم، حالا توی این قسمت میخوایم استفاده از یوبوت برای اجرای لینوکس رو آموزش دهیم و سیستم فایلمون رو به لینوکس متصل کنیم.
استفاده از یوبوت برای اجرای لینوکس
یوبوت (U-Boot) از یک رابط خط-فرمان (Command-line) استفاده میکنه.
این رابط نسبت به رابط خط-فرمان لینوکس خیلی ابتدایی هست و تقربیا به جز backspace از هیچ کنترل دیگهای نمیتونین استفاده کنید. البته تنظیمات یوبوت (U-Boot) رو میشه عوض کرد جوری که خط-فرمان اون منطبق با Hush باشه که من تا حالا این کار رو نکردم و نمیخوام بکنم!!!
به صورت پیشفرض اعداد در این خط-فرمان یوبوت (U-Boot) در مبنای ۱۶ یا همون هگزادسیمال خودمون هستن.
در قسمتهای قبلی آموزش، مسیر حرکت و فصلهای پیش رو، بسیاری از مفاهیم موردنیاز و سؤالها و ابهامات شما در این دوره پاسخدادهشده است.
پس اگر اولین بار است که این آموزش را شروع میکنید، با توجه به سلسلهمراتب پیشبینیشده برای آموزشها، بهتر است از جلسه اول شروع کنید!
برای آموزش امبدد لینوکس کلیک کنید.
متغیرهای محیطی یوبوت U-Boot
یوبوت (U-Boot) برای انتقال اطلاعات بین توابع و یا دستورات مختلف از متغیرهای محیطی استفاه میکنه.
مقادیر اولیه بسیاری از متغیرهای در سورس فایلهای مربوط به هر برد/ پردازنده تعریف شدن.
بسته به نوع حافظهتون شما هم میتوانید متغیرهای جدیدی رو تعریف و ذخیره کنید.
با دستور زیر میتونید تمام متغیرهای تعریف شده رو ببیند:
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 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 | printenv arch=arm baudrate=115200 board=rpi board_name=3 Model B+ board_rev=0xD board_rev_scheme=1 board_revision=0xA020D3 boot_a_script=load ${devtype} ${devnum}:${distro_bootpart} ${scriptaddr} ${prefix}${script}; source ${scriptaddr} boot_efi_binary=load ${devtype} ${devnum}:${distro_bootpart} ${kernel_addr_r} efi/boot/bootaa64.efi; if fdt addr -q ${fdt_addr_r}; then bootefi ${keri boot_efi_bootmgr=if fdt addr -q ${fdt_addr_r}; then bootefi bootmgr ${fdt_addr_r};else bootefi bootmgr;fi boot_extlinux=sysboot ${devtype} ${devnum}:${distro_bootpart} any ${scriptaddr} ${prefix}${boot_syslinux_conf} boot_net_usb_start=usb start boot_prefixes=/ /boot/ boot_script_dhcp=boot.scr.uimg boot_scripts=boot.scr.uimg boot.scr boot_syslinux_conf=extlinux/extlinux.conf boot_targets=mmc0 mmc1 mmc2 usb0 pxe dhcp bootcmd=run distro_bootcmd bootcmd_dhcp=devtype=dhcp; run boot_net_usb_start; if dhcp ${scriptaddr} ${boot_script_dhcp}; then source ${scriptaddr}; fi;setenv efi_fdtfile ${fdtf; bootcmd_mmc0=devnum=0; run mmc_boot bootcmd_mmc1=devnum=1; run mmc_boot bootcmd_mmc2=devnum=2; run mmc_boot bootcmd_pxe=run boot_net_usb_start; dhcp; if pxe get; then pxe boot; fi bootcmd_usb0=devnum=0; run usb_boot bootdelay=2 cpu=armv8 dhcpuboot=usb start; dhcp u-boot.uimg; bootm distro_bootcmd=for target in ${boot_targets}; do run bootcmd_${target}; done efi_dtb_prefixes=/ /dtb/ /dtb/current/ ethaddr=b8:27:eb:44:5a:00 fdt_addr=2eff8100 fdt_addr_r=0x02600000 fdt_high=ffffffffffffffff fdtcontroladdr=3b3cc9c0 fdtfile=broadcom/bcm2837-rpi-3-b-plus.dtb initrd_high=ffffffffffffffff kernel_addr_r=0x00080000 load_efi_dtb=load ${devtype} ${devnum}:${distro_bootpart} ${fdt_addr_r} ${prefix}${efi_fdtfile} loadaddr=0x1000000 mmc_boot=if mmc dev ${devnum}; then devtype=mmc; run scan_dev_for_boot_part; fi preboot=usb start pxefile_addr_r=0x02500000 ramdisk_addr_r=0x02700000 scan_dev_for_boot=echo Scanning ${devtype} ${devnum}:${distro_bootpart}...; for prefix in ${boot_prefixes}; do run scan_dev_for_extlinux; run scan_de; scan_dev_for_boot_part=part list ${devtype} ${devnum} -bootable devplist; env exists devplist || setenv devplist 1; for distro_bootpart in ${devplistt scan_dev_for_efi=setenv efi_fdtfile ${fdtfile}; for prefix in ${efi_dtb_prefixes}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${ee scan_dev_for_extlinux=if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${boot_syslinux_conf}; then echo Found ${prefix}${boot_syslinux_coni scan_dev_for_scripts=for script in ${boot_scripts}; do if test -e ${devtype} ${devnum}:${distro_bootpart} ${prefix}${script}; then echo Found U-Boot e scriptaddr=0x02400000 serial#=0000000048445a00 soc=bcm283x stderr=serial,vidconsole stdin=serial,usbkbd stdout=serial,vidconsole usb_boot=usb start; if usb dev ${devnum}; then devtype=usb; run scan_dev_for_boot_part; fi usbethaddr=b8:27:eb:44:5a:00 vendor=raspberrypi |
با این دستور هم میتونید متغیر جدید my_website رو با مقدار aniroot.com تعریف کنید:
1 | setenv my_website aniroot.com |
با دستور saveenv هم میتونید متغیر خودتون رو واسه همیشه ذخیره کنید.
آماده سازی فایلها برای لود شدن توسط یوبوت (U-Boot)
یوبوت (U-Boot) فایل سیستم نداره و از یک سرایند بیتی برای شناسایی فایلها استفاده میکنه، برای ساختن این سرایند ابزاری هست به نام mkimage که هم میتونید از طریق مدیربستههای ابونتو نصبش کنید و هم اینکه از توی فولدر tools استفادهش کنید:
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 | ~/EmbeddedLinux/RPI3BP/bootldr/u-boot/tools/mkimage Error: Missing output filename Usage: mkimage -l image -l ==> list image header information mkimage [-x] -A arch -O os -T type -C comp -a addr -e ep -n name -d data_file[:data_file...] image -A ==> set architecture to 'arch' -O ==> set operating system to 'os' -T ==> set image type to 'type' -C ==> set compression type 'comp' -a ==> set load address to 'addr' (hex) -e ==> set entry point to 'ep' (hex) -n ==> set image name to 'name' -d ==> use image data from 'datafile' -x ==> set XIP (execute in place) mkimage [-D dtc_options] [-f fit-image.its|-f auto|-F] [-b <dtb> [-b <dtb>]] [-i <ramdisk.cpio.gz>] fit-image <dtb> file is used with -f auto, it may occur multiple times. -D => set all options for device tree compiler -f => input filename for FIT source -i => input filename for ramdisk file Signing / verified boot not supported (CONFIG_FIT_SIGNATURE undefined) mkimage -V ==> print version information and exit Use '-T list' to see a list of available image types |
با تایپ کردن خط بالا در خط-فرمان لینوکس یک سری توضیحات در مورد این ابزار بهتون میده و با سرچ کردن هم میتونید اطلاعات بیشتری به دست بیارید.
مثلاً این یک مدل از استفاده از این دستور هست.
1 | sudo mkimage -A arm64 -O linux -T script -d ~/EmbeddedLinux/RPI3BP/bootldr/boot.cmd ~/EmbeddedLinux/RPI3BP/bootldr/boot.scr |
داریم برای معماری ARM اون هم از نوع ۶۴ بیتی برای سیستم عامل لینوکس یه فایل از نوع اسکریبپ رو آماده میکنیم.
بارگزاری ایمیج توسط یوبوت (U-Boot)
با توجه به حافظهای که میخوایم از اون ایمیج یا هر اطلاعاتی رو بارگزاری کنیم و فایل سیستم اون حافظه دستورات مختلفی داره یوبوت (U-Boot).
مثلاً برای بارگزاری از حافظه SD که با فایل سیستم FAT32 فرمت شده میتوینم از دستور زیر استفاده کنیم:
1 | fatload mmc 0 $kernel_addr_r Image |
در این مثال از حافظه شماره SD شماره صفر میخوایم فایل Image رو در آدرس kernel_addr_r (که خود kernel_addr_r از متغیرهای محیطی یوبوت U-Boot هست) بارگزاری کنیم.
بوت کردن لیونکس
خب حالا میخوایم از یوبوت (U-Boot) استفاده کنیم و لینوکس رو روی بردمون اجرا کنیم.
برای این کار به یک سری فایل نیاز هست که توی فصول بعدی باشون آشنا میشیم و یاد میگیریم چهجوری بسازیمشون. چون اینجا لازمشون داریم من این فایلها رو ساختم و آمادهش رو واسه دانلود توی این مقاله قرار دادم تا بتونیم با هم پیش بریم و با یوبوت U-Boot بیشتر آشنا بشیم.
این فایلها چیاهستن؟
کرنل لینوکس، دیوایستریز و فایل سیستم روت اولیه.
اول تک تک این فایلها رو در آدرس مربوطه بارگزاری میکنیم.
سوال: این آدرس رو از کجا باید بدونیم؟
جواب این آدرسها بعضی وقتا یونیک هستن ولی اغلب یونیک نیستن و با داشتن اطلاعاتی از حافظه رم و نحوه آدرس دهیشون توسط پردازنده و حجم فایل میشه بشون رسید ولی اغلب در راهنماهای شرکت سازنده مقادیر مناسب پیشنهاد شده.
تذکر از کپی پیست کردن دستورات زیر خودداری کنید و دستور را کامل تایپ کنید!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | fatload mmc 0 $kernel_addr_r Image 20140544 bytes read in 905 ms (21.2 MiB/s) fatload mmc 0 $fdt_addr_r bcm2837-rpi-3-b-plus.dtb 21781 bytes read in 5 ms (4.2 MiB/s) fatload mmc 0 $ramdisk_addr_r uInitrd 1395264 bytes read in 67 ms (19.9 MiB/s) |
خب بعدش متغیر محیطی bootargs رو مقدار دهی میکنیم که درحقیقت همون ارگومانهایی هست که به کرنل میدیم:
1 | setenv bootargs console=tty1 console=ttyAMA0,115200 earlyprintk root=/dev/root rootwait panic=10 |
در نهایت با این دستور کرنل رو بارگزاری میکنیم:
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 | booti $kernel_addr_r $ramdisk_addr_r $fdt_addr_r Moving Image from 0x80000 to 0x200000, end=1670000 ## Loading init Ramdisk from Legacy Image at 02700000 ... Image Name: uInitrd Image Type: AArch64 Linux RAMDisk Image (uncompressed) Data Size: 1395200 Bytes = 1.3 MiB Load Address: 00000000 Entry Point: 00000000 Verifying Checksum ... OK ## Flattened Device Tree blob at 02600000 Booting using the fdt blob at 0x2600000 Using Device Tree in place at 0000000002600000, end 0000000002608514 Starting kernel … |
خب کرنل شروع شد…
البته به لطف رسپبری پای تا اینجا رو بیشتر نداریم و نمیتونید وارد محیط لینوکس بشید.
قصهاش هم مفصل هست رسپبری پای از سریال اصلی برای بلوتوث استفاده میکنه و اصلا رسپبری پای بوت لودر خودش رو داره و میونه خوبی با یوبوت (U-Boot) نداره و بهتره از همون بوت لودر خودش که سورسش در دسترس نیست استفاده کنید…
البته که منطقی نبود در دو خط نحوه استفاده از بوت لودر رسپبری پای رو بگیم و این فصل رو با دو خط آموزش منتشر کنیم 🙂
ولی خب برای یادگیری یوبوت (U-Boot) و استفاده از اون برای بقیهی پروژهها الان آماده هستین تقریبا.
چرا تقریبا؟
چون کدوم برد رو دیدن هر دفعه برای اجرای لینوکس یکی بیاد با سریال بش وصل بشه و یه سری دستورات رو تایپ کنه و …
بله روند بوت باید به صورت خودکار انجام بشه.
برای این کار یه فایل باید کنار بقیه فایلها توی SD کارت بذارین
محتویاتش دقیقاً همونهایی هست که در خط-فرمان یوبوت (U-Boot) تایپ کردیم و بعدش باید با mkimage برای یوبوت (U-Boot) آمادهش کنید:
1 | <span style="font-size: 10pt;">booti $kernel_addr_r $ramdisk_addr_r $fdt_addr_r</span> |
و حالا با این دستور آمادهش کنیم برای ریختن روی SD کارت:
1 | <span style="font-size: 10pt;">sudo mkimage -A arm64 -O linux -T script -d ~/EmbeddedLinux/RPI3BP/bootldr/boot.cmd ~/EmbeddedLinux/RPI3BP/bootldr/boot.scr</span> |
از این به بعد با روشن شدن برد خود به خود لینوکس اجرا میشه.
اگه یه روزی خواستیم بریم توی محیط یوبوت (U-Boot) کافیه اولش تند تند تایپ کنیم 🙂 در اینجا این فصل هم تمام میشه، اما آنچه ما بدان نپرداختیم و شاید روزی به کارتان اید پورت کردن یوبوت (U-Boot) برای برد جدید هست. میتونه هم کار خیلی راحتی باشه هم کار بیش از حد سختی. پیشنهاد میکنم برید دنبالش و به عنوان تمرین یوبوت (U-Boot) رو روی لیچیپای و برای پردازنده F1C100s اجرا کنید.
یه سری هم به نظرات این پایین بندازید، نظرات رو بخونید و اگر شما هم نظری دارید، لطفا با ما به اشتراک بگذارید!
پایان این فصل
سلام
آقا خدا خیرت بده
جواب سوالایی که مدت ها تو سرم بود رو دارم یکی یکی طی این آموزش ها پیدا میکنم
خدا به شما اجر دنیوی و اخروی بده
??????
سلام امیر جان
حال و احوال! خوشحال شدیم از این حس خوب شما ❤️
امیدوارم در کار هم لذتش را ببرید ?