در مقاله قبل Bootloader را در نقطه ای گذاشتیم که Mode و Slot را انتخاب کرده و زنجیره اعتماد را بررسی می کند. حالا باید چیزی را واقعا از Storage بخواند و در RAM قرار دهد تا Linux بتواند شروع شود.
در Android جدید این اطلاعات دیگر لزوما داخل یک فایل واحد نیستند. Kernel، ramdisk عمومی، بخش های مخصوص Vendor، Device Tree و Metadata امنیتی می توانند در Imageهای جدا قرار گرفته باشند.
تصویر کلی، هر Image یک نقش مشخص
boot → Kernel, and on some layouts generic ramdisk
init_boot → generic ramdisk on Android 13 launch devices
vendor_boot → vendor ramdisk + vendor boot data + DTB
dtbo → Device Tree overlays
vbmeta → AVB metadata
super → logical system/vendor/product partitions
این جدول را نباید روی همه نسل های Android یکسان فرض کرد. ساختار Boot Images در طول زمان تغییر کرده و دستگاهی که با نسخه جدید Android Launch شده می تواند Layout متفاوتی از دستگاهی داشته باشد که فقط Upgrade شده است.
boot.img، جایی که Kernel را پیدا می کنیم
در دستگاه های جدید مبتنی بر
GKI
پارتیشن boot محل اصلی Kernel عمومی Android است.
اما اینکه ramdisk هم داخل boot باشد به نسل دستگاه بستگی دارد.
روی دستگاه هایی که با Android 12 و معماری GKI آن نسل Launch شده اند، generic ramdisk می تواند کنار Kernel داخل boot قرار داشته باشد.
برای دستگاه هایی که با Android 13 Launch می شوند generic ramdisk از boot جدا شده و به init_boot منتقل شده است.
Typical Android 12 GKI layout
boot = kernel + generic ramdisk
Android 13 launch layout
boot = GKI kernel
init_boot = generic ramdisk
init_boot، خانه جدید generic ramdisk در Launchهای Android 13
generic ramdisk محیط بسیار کوچکی است که برای شروع Userspace اولیه Android استفاده می شود و فایل /init مهم ترین بخش آن است.
در دستگاه هایی که با Android 13 Launch می شوند این ramdisk در init_boot قرار دارد.
به همین دلیل دیدن Android 13 روی یک گوشی به تنهایی کافی نیست که نتیجه بگیریم حتما init_boot دارد. دستگاهی که از نسل قدیمی تر Upgrade شده می تواند Layout قبلی را حفظ کرده باشد.
وقتی ابزاری قرار است ramdisk اولیه را تغییر دهد، اول باید Layout واقعی دستگاه مشخص شود. روی یک نسل ramdisk داخل boot است و روی دستگاه دیگری ممکن است داخل init_boot باشد. به همین دلیل دستورالعمل یک مدل را نباید بدون بررسی روی مدل دیگر اجرا کرد.
vendor_boot، بخش Boot وابسته به سخت افزار دستگاه
Android 11 همراه با GKI جداسازی بیشتری بین Kernel عمومی و اجزای مخصوص Device ایجاد کرد.
اطلاعات مخصوص Vendor از boot بیرون آمدند و vendor_boot برای نگهداری چیزهایی مثل vendor ramdisk و DTB وارد معماری شد.
در Android 12 ساختار vendor_boot کامل تر شد و می تواند چند vendor ramdisk fragment داشته باشد. Bootloader می تواند با توجه به Mode دستگاه مجموعه مناسب را برای Boot انتخاب کند.
DTB و DTBO، توضیح سخت افزار برای Kernel
Linux Kernel باید بداند روی چه Board و چه سخت افزاری اجرا می شود. DTB اطلاعات ساختار سخت افزار را در اختیار Kernel قرار می دهد.
DTBO برای اعمال تفاوت های مدل ها و Variantهای مختلف روی یک Base Device Tree استفاده می شود. این جداسازی به GKI کمک می کند Kernel اصلی عمومی تر بماند و جزئیات Hardware بیرون از آن نگه داشته شوند.
🔍 عمیق تر بشویم، Boot image header v3 و v4
Android 9 یک Version field برای Boot image header اضافه کرد تا ساختار Image بتواند بدون شکستن Compatibility تکامل پیدا کند. برای معماری های جدید دو نسخه مهم v3 و v4 هستند.
- v3 در Android 11 با معماری GKI و جداسازی vendor_boot اهمیت پیدا کرد.
- v4 در Android 12 قابلیت های بیشتری مثل Boot signature و ساختار غنی تر vendor ramdisk را اضافه کرد.
- جدا شدن generic ramdisk به init_boot برای دستگاه های Launch شده با Android 13 یک تغییر Layout است و نباید آن را فقط نتیجه تغییر Header از v3 به v4 دانست.
vbmeta، Image اجرایی نیست
vbmeta را در مقاله AVB دیدیم.
این پارتیشن برای اجرای Kernel نیست، Metadata امنیتی Android Verified Boot را نگه می دارد و Bootloader قبل از اجرای OS از آن استفاده می کند.
super و Dynamic Partitions، بخش دیگری از Storage
از Android 10 پارتیشن هایی مثل system، vendor و product می توانند به شکل Logical Partition داخل یک پارتیشن فیزیکی بزرگ تر به نام super قرار بگیرند.
Physical storage
├─ boot_a / boot_b
├─ init_boot_a / init_boot_b
├─ vendor_boot_a / vendor_boot_b
├─ vbmeta_a / vbmeta_b
└─ super
├─ system
├─ vendor
└─ product
Bootloader لازم نیست Filesystem اصلی Android را مثل یک OS کامل Mount کند. در مراحل بعد first-stage init با Device Mapper، Logical Partitionهای لازم را از داخل super آماده می کند.
ramdisk، اولین Filesystem موقت در RAM
ramdisk یک Filesystem کوچک است که پیش از Mount شدن Filesystemهای اصلی Android در RAM در دسترس قرار می گیرد.
فایل /init از همین محیط اجرا می شود و PID 1 را در Userspace Android می سازد.
این نقطه همان پلی است که مقاله بعدی از آن عبور می کند، یعنی جایی که Bootloader کارش را تمام کرده، Kernel شروع شده و init باید پارتیشن های واقعی Android را آماده کند.
bootconfig و kernel cmdline، اطلاعاتی که همراه Kernel می روند
Bootloader فقط Kernel و ramdisk را Load نمی کند.
باید پارامترهایی هم به Kernel و Android بدهد.
در Android 12 و جدیدتر بسیاری از پارامترهای Android specific مثل androidboot.* می توانند از طریق bootconfig منتقل شوند.
bootconfig جای کل kernel command line را نگرفته است. پارامترهای خود Kernel همچنان می توانند در cmdline باشند، در حالی که بخش بزرگی از تنظیمات مخصوص Android به bootconfig منتقل شده است.
🔍 عمیق تر بشویم، vendor_kernel_boot در بعضی دستگاه ها
ممکن است روی بعضی دستگاه های جدید پارتیشنی با نام vendor_kernel_boot دیده شود.
این پارتیشن جزو Layout اجباری تمام Androidهای جدید نیست و باید آن را Device specific در نظر گرفت.
برای نمونه Google در بعضی نسل های Pixel از چنین ساختاری برای جدا کردن بخشی از Kernel artifacts استفاده کرده است. بنابراین وجود این پارتیشن را نباید به همه دستگاه ها تعمیم داد.
لحظه آخر Bootloader، ساختن محیطی که Kernel بتواند از آن شروع کند
verify selected images
↓
load kernel into RAM
load generic / vendor ramdisks
load DTB / DTBO
prepare cmdline / bootconfig
↓
jump to kernel entry
در این لحظه تمام اجزای لازم برای شروع Linux در RAM قرار گرفته اند.
از اینجا به بعد Bootloader کنار می رود و Kernel مسئول CPU، Memory، Driverها و در نهایت اجرای /init می شود.
Android جدید یک Boot Image واحد و ساده ندارد. Kernel، generic ramdisk، بخش های مخصوص Vendor، Device Tree و AVB Metadata می توانند در Imageهای جدا باشند و Layout واقعی دستگاه به نسل Android و نحوه Launch آن بستگی دارد.
واژه نامه کوتاه
- boot
- پارتیشن Boot اصلی، در معماری GKI محل Kernel و بسته به نسل دستگاه گاهی generic ramdisk.
- init_boot
- محل generic ramdisk روی دستگاه هایی که با Android 13 و جدیدتر Launch شده اند.
- vendor_boot
- پارتیشن حاوی داده های Boot مخصوص Vendor مثل vendor ramdisk و DTB.
- DTB
- Device Tree Blob، توصیف باینری سخت افزار برای Linux Kernel.
- GKI
- Generic Kernel Image، معماری Kernel عمومی تر Android با جداسازی بیشتر بخش های Vendor.
- ramdisk
- Filesystem کوچک و موقت در RAM برای شروع اولیه Userspace.