فرآیند بوت لینوکس ARM

این مجموعه ای از مقالات خواهد بود که توضیح می دهد چگونه هسته لینوکس در معماری ARM بوت می شود. این قسمت اول است

فرآیند بوت لینوکس ARM:

ما فرآیند بوت سیستم روی تراشه AT91RM9200 را توضیح خواهیم داد که بر اساس پردازنده ARM920T ARM Thumb ساخته شده است. Kwickbyte یک برد تعبیه شده به نام kb9202 بر اساس AT91RM9200 می سازد. ما این برد را به عنوان مثال در نظر می گیریم و می بینیم که لینوکس چگونه روی این برد راه اندازی می شود.

قبل از شروع خواندن این، باید برگه داده AT91RM9200 (مشخصات) را بخوانید.

همچنین برای درک بهتر فرآیند بوت باید راهنمای معماری ARM را بخوانید.

اجزای فرآیند بوت لینوکس:

فرآیند بوت لینوکس شامل اجزای زیر است.

بوت لودر
تصویر هسته
فایل سیستم ریشه

قبل از اینکه ببینیم اجزای فوق چگونه کار می کنند، در زیر جریان فراخوانی فرآیند بوت کرنل لینوکس برای معماری بازو ارائه شده است. این یک تصویر بزرگ از کل فرآیند بوت لینوکس می دهد. ما از بوت لودر U-boot استفاده می کنیم.
فرآیند بوت لینوکس ARM: تصویر بزرگ
یو بوت:

_start (cpu/arm920t/start.S)

start_code (cpu/arm920t/start.S)

start_armboot (lib_arm/board.c)

board_init (board/kb9202/kb9202.c)

timer_init (cpu/arm920t/at91/timer.c)

serial_init (drivers/serial/at91rm9200_usart.c)

main_loop (lib_arm/board.c)

اکنون u-boot راه‌اندازی شده و در خط u-boot قرار دارد و آماده پذیرش دستورات است. فرض کنید که تصویر هسته در RAM بارگذاری شده و دستور bootm صادر شده است.

do_bootm (common/cmd_bootm.c)

bootm_start (common/cmd_bootm.c)

bootm_load_os (common/cmd_bootm.c)

do_bootm_linux (lib_arm/bootm.c)

متن (linux/arch/arm/kernel/head.S)

کنترل به لینوکس داده شده است.

هسته لینوکس:

متن (arch/arm/kernel/head.S:78)

__lookup_processor_type (arch/arm/kernel/head-common.S:160)

__lookup_machine_type (arch/arm/kernel/head-common.S:211)

__create_page_tables (arch/arm/kernel/head.S:219)

__arm920_setup (arch/arm/mm/proc-arm920.S:389)

__enable_mmu (arch/arm/kernel/head.S:160)

__turn_mmu_on (arch/arm/kernel/head.S:205)

__switch_data (arch/arm/kernel/head-common.S:20)

start_kernel (init/main.c:529)

start_kernel (init/main.c:529)

tick_init(kernel/time/tick-common.c:413)

setup_arch (arch/arm/kernel/setup.c:666)

setup_machine (arch/arm/kernel/setup.c:369)

lookup_machine_type ( )

setup_command_line (init/main.c:408)

build_all_zonelists (mm/page_alloc.c:3031)

parse_args (kernel/params.c:129)

mm_init (init/main.c:516)

mem_init (arch/arm/mm/init.c:528)

kmem_cache_init (mm/slab.c، mm/slob.c، mm/slub.c)

sched_init (kernel/sched.c)

init_IRQ (arch/arm/kernel/irq.c)

init_timers (kernel/timer.c:1713)

hrtimers_init (kernel/hrtimer.c:1741)

softirq_init (kernel/softirq.c:674)

console_init (drivers/char/tty_io.c:3084)

vfs_caches_init (fs/dcache.c:2352)

mnt_init (fs/namespace.c:2308)

init_rootfs ()

init_mount_tree (fs/namespace.c:2285)

do_kern_mount (fs/namespace.c:1053)

set_fs_pwd(fs/fs_struct.c:29)

set_fs_root(fs/fs_struct.c:12)

bdev_cache_init (fs/block_dev.c:465)

chrdev_init (fs/char_dev.c:566)

signals_init (kernel/signal.c:2737)

rest_init (init/main.c:425)

kernel_thread (431, arch/arm/kernel/process.c:388)

kernel_thread() یک رشته هسته ایجاد می کند و کنترل به kernel_init () داده می شود.

kernel_init (431, init/main.c:856)

do_basic_setup (888, init/main.c:787)

init_workqueues (789، kernel/workqueue.c:1204)

driver_init (793, drivers/base/init.c:20)

do_initcalls (796, init/main.c:769) /* همه زیر سیستم ها را با توابع init فراخوانی می کند */

prepar_namespace (906, init/do_mounts.c:366)

initrd_load (399، init/do_mounts_initrd.c:107)

rd_load_image (117, init/do_mounts_rd.c:158) /* اگر initrd داده شود */

ident_ramdisk_image (179, init/do_mounts_rd.c:53)

handle_initrd (119, init/do_mounts_initrd.c:37) /*اگر rd_load_image موفقیت آمیز باشد */

mount_block_root (45, init/do_mounts.c:233)

do_mount_root (247، init/do_mounts.:218)

mount_root (417, init/do_mounts.c:334) /* اگر initrd داده نشود */

mount_block_root (359, init/do_mounts.c:233)

do_mount_root (247, init/do_mounts.c:218)

init_post (915, init/main.c:816)

run_init_process (847, init/main.c:807)

kernel_execve (810، arch/arm/kernel/sys_arm.c:81)

فضای کاربری

init() /*userspace /sbin/init */

بوت لودر:

بوت لودر یک برنامه کوچک است که تصویر هسته را در RAM بارگذاری می کند و تصویر هسته را بوت می کند. به این بوت استرپ نیز می گویند زیرا با بارگذاری یک سیستم عامل، سیستم را بالا می آورد. بوت لودر قبل از شروع هر نرم افزار دیگری شروع می شود و پردازنده را مقداردهی اولیه می کند و cpu را برای اجرای برنامه ای مانند یک سیستم عامل آماده می کند. اکثر پردازنده‌ها دارای یک آدرس پیش‌فرض هستند که اولین بایت‌های کد از آن پس از برق اعمال می‌شود یا برد ریست می‌شود. طراحان سخت افزار از این اطلاعات برای ذخیره کد بوت لودر در آن آدرس در رام یا فلش استفاده می کنند. از آنجایی که باید cpu را مقداردهی اولیه کند و باید برنامه ای را اجرا کند که در آدرس خاص معماری قرار دارد، بوت لودرها بسیار مختص پردازنده و مخصوص برد هستند. هر برد تعبیه شده دارای یک بوت استرپ برای دانلود تصویر هسته یا برنامه مستقل در برد و شروع اجرای تصویر یا برنامه هسته است. بوت لودر زمانی اجرا می شود که برق به برد پردازنده اعمال شود. اساساً دارای حداقل ویژگی هایی برای بارگذاری تصویر و راه اندازی آن خواهد بود.

مطلب پیشنهادی
معماری و تاریخ Corbels – آنها چه هستند و از کجا آمده اند

همچنین امکان کنترل سیستم با استفاده از رابط اشکال زدایی سخت افزاری مانند JTAG وجود دارد. این رابط ممکن است برای نوشتن برنامه بوت لودر در حافظه غیر فرار قابل بوت (به عنوان مثال فلش) با دستور دادن به هسته پردازنده برای انجام اقدامات لازم برای برنامه ریزی حافظه غیر فرار استفاده شود. به طور کلی برای اولین بار برای دانلود بوت لودر اصلی و برای برخی از مراحل بازیابی انجام می شود. JTAG یک رابط استاندارد و محبوب است که توسط بسیاری از فروشندگان برد ارائه شده است. برخی از میکروکنترلرها رابط‌های سخت‌افزاری خاصی را ارائه می‌کنند که نمی‌توان از آنها برای کنترل دلخواه سیستم یا اجرای مستقیم کد استفاده کرد، اما در عوض از طریق پروتکل‌های ساده، کد بوت را در حافظه غیر فرار قابل بوت (مانند حافظه فلش) درج می‌کنند. سپس در مرحله تولید، از چنین رابط هایی برای تزریق کد بوت (و احتمالاً کدهای دیگر) به حافظه غیر فرار استفاده می شود. پس از تنظیم مجدد سیستم، میکرو کنترلر شروع به اجرای کد برنامه ریزی شده در حافظه غیر فرار خود می کند، درست مانند پردازنده های معمولی که از رام ها برای بوت استفاده می کنند. در بسیاری از موارد چنین واسط هایی با منطق سخت سیمی پیاده سازی می شوند. در موارد دیگر، چنین رابط‌هایی را می‌توان با نرم‌افزاری که در رام بوت یکپارچه روی تراشه از پین‌های GPIO اجرا می‌کند، ایجاد کرد.

برخی دیگر از بوت لودرهای شخص ثالث موجود هستند که مجموعه ای غنی از ویژگی ها و رابط کاربری آسان را ارائه می دهند. شما می توانید این بوت لودرهای شخص ثالث را در برد دانلود کنید و می توانید آنها را بوت لودرهای پیش فرض برای برد خود تبدیل کنید. به طور کلی بوت لودرهای ارائه شده توسط فروشندگان برد با این بوت لودرهای شخص ثالث جایگزین می شوند. تعداد کمی boolader شخص ثالث موجود است و برخی از آنها منبع باز (یا بوت لودرهای رایگان) و برخی تجاری هستند. برخی از آنها Das U-Boot، Red boot، GRUB (برای کامپیوترهای رومیزی)، LILO، Loadlin،، bootsect-loader، SYSLINUX، EtherBoot، ELILO هستند.

مطلب پیشنهادی
هنر بزرگ و طراحی معاصر را در رم کاوش کنید

ما بوت لودر U-boot را به عنوان بوت لودر خود می گیریم. U-boot بوت لودر پرکاربرد در سیستم های تعبیه شده است. ما کد را از منبع u-boot-2010.03 توضیح خواهیم داد. U-boot را می توانید از سایت زیر دانلود کنید. http://www.denx.de/wiki/U-Boot

U-boot چگونه ساخته می شود:

بر اساس پیکربندی U-boot، تمام فایل های اسمبلی (.S) و فایل های C (.c) با استفاده از کامپایلر متقابل که برای یک معماری خاص ساخته شده است، کامپایل می شوند و فایل های شی (.o) تولید می شوند. همه این فایل‌های شیء توسط لینک‌کننده به هم متصل می‌شوند و یک فایل اجرایی ایجاد می‌شود. یک فایل شی یا فایل اجرایی مجموعه ای از بخش هایی مانند.text،.data،.bss و غیره است. فایل های شی و فایل های اجرایی فرمت فایلی مانند elf دارند. تمام بخش های فایل های شی در فایل اجرایی بر اساس اسکریپتی به نام لینکر اسکریپت مرتب خواهند شد. این اسکریپت به شما می گوید که در هنگام اجرا، تمام بخش ها باید در کجا بارگذاری شوند. درک این اسکریپت برای دانستن نحوه تشکیل بوت لودر و هسته و نحوه بارگذاری بخش های مختلف بوت لودر یا هسته در حافظه بسیار مهم است.

به طور کلی، هنگامی که یک برنامه اجرا می شود (اجرا می شود)، یک لودر فایل اجرایی را می خواند و بخش های مختلف فایل اجرایی را در محل مشخص شده حافظه بارگذاری می کند و شروع به اجرای تابع شروع (نقطه ورودی) مشخص شده در اسکریپت لینکر می کند. اما، اگر می‌خواهید یک بوت‌لودر را اجرا کنید (بارگذاری کنید)، هیچ لودری وجود نخواهد داشت که بخش‌های مختلف فایل اجرایی را در حافظه بارگذاری کند (عمداً برای درک فرمت فایل). سپس باید از ابزاری به نام objcopy استفاده کنید که تمام بخش ها را از فایل اجرایی می گیرد و یک فایل باینری ایجاد می کند که هیچ فرمت فایلی ندارد. این فایل باینری را می توان در حافظه بارگذاری کرد و اجرا کرد یا می توان در یک آدرس خاص (مشخص به معماری) در رام نوشت که با اعمال برق به برد توسط cpu اجرا می شود.

فرض کنید بر اساس پیکربندی U-boot همه فایل ها کامپایل شده و فایل های شی ایجاد می شوند. U-boot makefile از اسکریپت پیوند دهنده زیر (مخصوص معماری) برای ساخت یک فایل اجرایی استفاده می کند.

فایل: cpu/arm920t/u-boot.lds

32 OUTPUT_FORMAT(“elf32-littlearm”، “elf32-littlearm”، “elf32-littlearm”)

33 OUTPUT_ARCH (بازو)

34 ENTRY(_start)

35 بخش

36 {

37. = 0x00000000;

38

39. = ALIGN(4);

40. متن:

41 {

42 cpu/arm920t/start.o (text)

43 *(text.)

44 }

4546. = ALIGN(4);

47.rodata: { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }

48

49. = ALIGN(4);

50.data: { *(.data) }

مطلب پیشنهادی
بررسی اره رومیزی Proxxon 37070 FET

51

52. = ALIGN(4);

53.got: { *(.got) }

54

55. =.;

56 __u_boot_cmd_start =.;

57.u_boot_cmd: { *(.u_boot_cmd) }

58 __u_boot_cmd_end =.;

59

60. = ALIGN(4);

61 __bss_start =.;

62.bss (NOLOAD): { *(.bss). = ALIGN(4); }

63 _پایان =.;

64 }

OUTPUT_FORMAT در خط #32 فرمت فایل فایل اجرایی را مشخص کنید. در اینجا فرمت فایل اجرایی elf32 و endianness اندیان کمی است. OUTPUT_ARCH در خط شماره 33 معماری ای را که این کد روی آن اجرا می شود مشخص کنید. ENTRY در خط #34 تابع شروع (نقطه ورود) برنامه u-boot را مشخص می کند. در اینجا نقطه ورود _start است.

SECTIONS در خط #35 نحوه نگاشت بخش های مختلف در فایل اجرایی را مشخص می کند. Loader از آدرس های مشخص شده در این بخش برای بارگذاری بخش های مختلف برنامه در حافظه استفاده می کند.

‘.’ در خط #37 آدرس شروعی را مشخص می کند که بخش های زیر باید در آن بارگذاری شوند. در این مورد آدرس شروع 0x00000000 است. بعد از این در خط #39 حافظه با 4 بایت تراز شده و قسمت متن در خط #40 قرار می گیرد.

40. متن:

41 {

42 cpu/arm920t/start.o (text)

43 *(text.)

44 }

در ‘.’ موقعیت (0x00000000) کد در cpu/arm920t/start.o نگاشت شده و از کدی پیروی می کند که در بخش های متنی همه فایل های شی (.o) دیگر وجود دارد. cpu/arm920t/start.o حاوی تابع _start() (به زبان اسمبلی) است که نقطه ورود این برنامه است.

در حال حاضر ‘.’ در 0x00000000 + اندازه (text) خواهد بود. مجدداً حافظه با 4 بایت تراز می شود و بخش rodata در خط 47 دنبال می شود.

. = ALIGN(4);

47.rodata: { *(SORT_BY_ALIGNMENT(SORT_BY_NAME(.rodata*))) }

بخش‌های rodata از تمام فایل‌های اشیاء در این آدرس نگاشت می‌شوند. بخش های.data و.git را دنبال می کند.

49. = ALIGN(4);

50.data: { *(.data) }

51

52. = ALIGN(4);

53.got: { *(.got) }

هر دستور U-boot یک شی از نوع ‘cmd_tbl_t’ است که حاوی نام دستور، رشته راهنما و نشانگر تابع است که باید هنگام اجرای این دستور اجرا شود. تمام این اشیاء دستوری به صورت متوالی در حافظه قرار می گیرند. هر یک از این شیء دستوری در یک بخش تعریف شده توسط U-boot به نام.u_boot_cmd در فایل شی ساخته شده است. این قسمت های all.u_boot_cmd بعد از قسمت های بالا (.data و.git) در حافظه قرار می گیرند.

. =.;

56 __u_boot_cmd_start =.;

57.u_boot_cmd: { *(.u_boot_cmd) }

58 __u_boot_cmd_end =.;

__u_boot_cmd_start شامل شروع اشیاء دستورات و __u_boot_cmd_end شامل انتهای اشیاء فرمان است.

و در ادامه بخش‌های bss (متغیرهای جهانی بدون مقدار اولیه) را دنبال می‌کند.

60. = ALIGN(4);

61 __bss_start =.;

62.bss (NOLOAD): { *(.bss). = ALIGN(4); }

63 _پایان =.;

__bss_start به آدرس start.bss اشاره می کند و _end حاوی انتهای همه بخش ها است.

با استفاده از این لینکر اسکریپت یک فایل اجرایی به نام u-boot تولید می شود. ابزار Objcopy برای تولید یک فایل باینری از فایل اجرایی u-boot استفاده می شود.

u-boot.bin: u-boot

$(OBJCOPY) ${OBJCFLAGS} -O باینری $

Source by Raghu Bharadwaj

مقاله اصلی

دیدگاه‌ خود را بنویسید

نشانی ایمیل شما منتشر نخواهد شد.