פיתוח ליבות

בדף הזה מוסבר התהליך של בניית ליבות בהתאמה אישית למכשירי Android. ההוראות האלה מנחות אתכם בתהליך של בחירת המקורות המתאימים, בניית הליבה והטמעת התוצאות בקובץ אימג' של המערכת שנבנה מתוך פרויקט קוד פתוח של Android (AOSP).

הורדת מקורות וכלים לבנייה

בליבות עדכניות, משתמשים ב-repo כדי להוריד את המקורות, את ערכת הכלים ואת סקריפטים הבנייה. חלק מהקרנלים (לדוגמה, קרנלים של Pixel 3) דורשים מקורות מכמה מאגרי git, בעוד שאחרים (לדוגמה, קרנלים נפוצים) דורשים רק מקור אחד. השימוש בגישה repo מבטיח הגדרה נכונה של ספריית המקור.

מורידים את המקורות של הענף המתאים:

mkdir android-kernel && cd android-kernel
repo init -u https://android.googlesource.com/kernel/manifest -b BRANCH
repo sync

רשימה של ענפי מאגר (BRANCH) שאפשר להשתמש בהם עם הפקודה הקודמת `repo init` זמינה במאמר ענפי ליבה ומערכות הבנייה שלהם.

פרטים על הורדה והידור של ליבות למכשירי Pixel זמינים במאמר בנושא בניית ליבות Pixel.

בניית הליבה

פיתוח באמצעות Bazel (Kleaf)

ב-Android 13 נוספה האפשרות ליצור ליבות באמצעות Bazel.

כדי ליצור הפצה של ליבת GKI לארכיטקטורת aarch64, צריך להוציא ענף של ליבת Android Common Kernel מגרסה Android 13 ואילך, ואז להריץ את הפקודה הבאה:

tools/bazel run //common:kernel_aarch64_dist [-- --destdir=$DIST_DIR]

לאחר מכן, קובץ הליבה הבינארי, המודולים והתמונות התואמות נמצאים בספרייה $DIST_DIR. אם לא מציינים את --destdir, אפשר לראות את מיקום הארטיפקטים בפלט של הפקודה. פרטים נוספים זמינים במסמכי התיעוד בנושא AOSP.

בנייה באמצעות build.sh (מאמר שמתייחס לגרסה קודמת)

להסתעפויות ב-Android 12 ומטה, או להסתעפויות ללא Kleaf:

build/build.sh

הקובץ הבינארי של הליבה, המודולים והתמונה המתאימה נמצאים בספרייה out/BRANCH/dist.

יצירת מודולים של ספקים למכשיר הווירטואלי

ב-Android 13, נוספה האפשרות ליצור ליבות באמצעות Bazel (Kleaf), במקום build.sh.

כדי ליצור הפצה למודולים של virtual_device, מריצים את הפקודה:

tools/bazel run //common-modules/virtual-device:virtual_device_x86_64_dist [-- --destdir=$DIST_DIR]

פרטים נוספים על בניית ליבות של Android באמצעות Bazel זמינים כאן. ‫Kleaf – בניית ליבות Android באמצעות Bazel.

פרטים על תמיכת Kleaf בארכיטקטורות ספציפיות זמינים במאמר תמיכת Kleaf במכשירים ובליבות.

יצירת מודולים של הספק למכשיר הווירטואלי באמצעות build.sh (מאמר שמתייחס לגרסה קודמת)

ב-Android 12,‏ Cuttlefish ו-Goldfish מתאחדים, כך שהם חולקים את אותו ליבה: virtual_device. כדי ליצור את המודולים של הליבה הזו, משתמשים בהגדרת הבנייה הזו:

BUILD_CONFIG=common-modules/virtual-device/build.config.virtual_device.x86_64 build/build.sh

ב-Android 11 הוצג GKI, שמפריד את ליבת מערכת ההפעלה לתמונת ליבה שמתוחזקת על ידי Google ולמודולים שמתוחזקים על ידי הספק, שנבנים בנפרד.

בדוגמה הזו מוצגת הגדרה של תמונת ליבה:

BUILD_CONFIG=common/build.config.gki.x86_64 build/build.sh

בדוגמה הזו מוצגת הגדרת מודול (Cuttlefish ו-Emulator):

BUILD_CONFIG=common-modules/virtual-device/build.config.cuttlefish.x86_64 build/build.sh

הרצת ליבת הקרנל

יש כמה דרכים להריץ ליבה בהתאמה אישית. בהמשך מפורטות דרכים מוכרות שמתאימות לתרחישי פיתוח שונים.

הטמעה בגרסת ה-build של תמונת Android

מעתיקים את Image.lz4-dtb למיקום הבינארי של הליבה המתאים בעץ AOSP, ובונים מחדש את קובץ אימג' לאתחול.

אפשרות אחרת היא להגדיר את המשתנה TARGET_PREBUILT_KERNEL בזמן השימוש בפקודה make bootimage (או בכל פקודה אחרת בשורת הפקודה make שיוצרת קובץ אימג' לאתחול). המשתנה הזה נתמך בכל המכשירים כי הוא מוגדר דרך device/common/populate-new-device.sh. לדוגמה:

export TARGET_PREBUILT_KERNEL=DIST_DIR/Image.lz4-dtb

הפעלת Flash ואתחול ליבות באמצעות fastboot

במכשירים החדשים ביותר יש תוסף לתוכנת האתחול שמייעל את התהליך של יצירה ואתחול של קובץ אימג' לאתחול.

כדי לאתחל את הליבה בלי להפעיל את הפלאשינג:

adb reboot bootloader
fastboot boot Image.lz4-dtb

בשיטה הזו, הליבה לא באמת מוצגת, והיא לא תישמר אחרי הפעלה מחדש.

הרצת ליבות ב-Cuttlefish

אתם יכולים להריץ ליבות בארכיטקטורה שתבחרו במכשירי Cuttlefish.

כדי להפעיל מכשיר Cuttlefish עם קבוצה מסוימת של ארטיפקטים של ליבת המערכת, מריצים את הפקודה cvd create עם הארטיפקטים של ליבת המערכת הרלוונטית כפרמטרים. בדוגמה הבאה של פקודה נעשה שימוש בארטיפקטים של ליבת המערכת עבור יעד arm64 ממניפסט הליבה common-android14-6.1.

cvd create \
    -kernel_path=/$PATH/$TO/common-android14-6.1/out/android14-6.1/dist/Image \
    -initramfs_path=/$PATH/$TO/common-android14-6.1/out/android14-6.1/dist/initramfs.img

מידע נוסף זמין במאמר בנושא פיתוח ליבות ב-Cuttlefish.

התאמה אישית של ה-build של הליבה

כדי להתאים אישית את הגרסאות של ליבת Kleaf, אפשר לעיין במסמכי התיעוד של Kleaf.

התאמה אישית של ה-build של הליבה באמצעות build.sh (גרסה קודמת)

ב-build/build.sh, משתני סביבה יכולים להשפיע על תהליך ה-build ועל התוצאה שלו. רובם אופציונליים, ולכל ענף של ליבת המערכת צריכה להיות הגדרת ברירת מחדל מתאימה. ריכזנו כאן את קיצורי הדרך הנפוצים ביותר. רשימה מלאה (ומעודכנת) זמינה build/build.shכאן.

משתנה הסביבה תיאור דוגמה
BUILD_CONFIG קובץ תצורת ה-build שממנו מאתחלים את סביבת ה-build. המיקום צריך להיות מוגדר ביחס לספריית השורש של המאגר. ברירת המחדל היא build.config.
חובה לליבות נפוצות.
BUILD_CONFIG=common/build.config.gki.aarch64
CC שינוי ההגדרות של הקומפיילר שבו משתמשים. חוזר לברירת המחדל של הקומפיילר שמוגדר על ידי build.config. CC=clang
DIST_DIR ספריית הפלט הבסיסית להפצה של ליבת המערכת. DIST_DIR=/path/to/my/dist
OUT_DIR ספריית הפלט הבסיסית של בניית הליבה. OUT_DIR=/path/to/my/out
SKIP_DEFCONFIG דילוג make defconfig SKIP_DEFCONFIG=1
SKIP_MRPROPER דילוג make mrproper SKIP_MRPROPER=1

הגדרת ליבה בהתאמה אישית לגרסאות build מקומיות

ב-Android מגרסה 14 ומעלה, אפשר להשתמש בקטעי defconfig כדי להתאים אישית את הגדרות הליבה. מידע נוסף זמין במסמכי Kleaf בנושא קטעי defconfig.

הגדרת ליבה בהתאמה אישית לגרסאות build מקומיות באמצעות הגדרות build (מאמר מגרסה קודמת)

ב-Android מגרסה 13 ומטה, אפשר לראות את ההוראות הבאות.

אם אתם צריכים לשנות באופן קבוע אפשרות הגדרה של ליבת המערכת, למשל כשאתם עובדים על תכונה, או אם אתם צריכים להגדיר אפשרות למטרות פיתוח, תוכלו לעשות זאת על ידי שמירה של שינוי מקומי או עותק של הגדרות הבנייה.

מגדירים את המשתנה POST_DEFCONFIG_CMDS להוראה שמוערכת מיד אחרי השלמת השלב הרגיל make defconfig. מכיוון שהקבצים build.config נכללים בסביבת הבנייה, אפשר לקרוא לפונקציות שמוגדרות ב-build.config כחלק מהפקודות של post-defconfig.

דוגמה נפוצה היא השבתה של אופטימיזציה בזמן הקישור (LTO) עבור ליבות של crosshatch במהלך הפיתוח. למרות שה-LTO מועיל לגרעינים שפורסמו, התקורה בזמן הבנייה יכולה להיות משמעותית. קטע הקוד הבא נוסף ל-build.config המקומי כדי להשבית את LTO באופן קבוע כשמשתמשים ב-build/build.sh.

POST_DEFCONFIG_CMDS="check_defconfig && update_debug_config"
function update_debug_config() {
    ${KERNEL_DIR}/scripts/config --file ${OUT_DIR}/.config \
         -d LTO \
         -d LTO_CLANG \
         -d CFI \
         -d CFI_PERMISSIVE \
         -d CFI_CLANG
    (cd ${OUT_DIR} && \
     make O=${OUT_DIR} $archsubarch CC=${CC} CROSS_COMPILE=${CROSS_COMPILE} olddefconfig)
}

זיהוי גרסאות ליבה

אפשר לזהות את הגרסה הנכונה לבנייה משני מקורות: עץ ה-AOSP וקובץ האימג' של המערכת.

גרסת ליבה מעץ AOSP

עץ ה-AOSP מכיל גרסאות ליבה מוכנות מראש. היומן של git מציג את הגרסה הנכונה כחלק מהודעת השליחה:

cd $AOSP/device/VENDOR/NAME
git log --max-count=1

אם גרסת הליבה לא מופיעה ביומן ה-git, צריך לקבל אותה מתמונת המערכת, כמו שמתואר בהמשך.

גרסת ליבה מקובץ אימג' של המערכת

כדי לקבוע את גרסת הליבה שבה נעשה שימוש בקובץ אימג' של מערכת, מריצים את הפקודה הבאה על קובץ הליבה:

file kernel

לקובצי Image.lz4-dtb, מריצים את הפקודה:

grep -a 'Linux version' Image.lz4-dtb

יצירת קובץ אימג' לאתחול

אפשר ליצור קובץ אימג' לאתחול באמצעות סביבת ה-build של הליבה.

יצירת קובץ אימג' לאתחול למכשירים עם init_boot

במכשירים עם מחיצת init_boot, קובץ האימג' לאתחול נוצר יחד עם הליבה. התמונה initramfs לא מוטמעת בקובץ אימג' לאתחול.

לדוגמה, באמצעות Kleaf, אפשר ליצור את קובץ אימג' לאתחול של GKI באמצעות:

tools/bazel run //common:kernel_aarch64_dist [-- --destdir=$DIST_DIR]

עם build/build.sh (גרסה קודמת), אפשר ליצור את קובץ האימג' לאתחול של GKI באמצעות:

BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

קובץ האימג' לאתחול GKI נמצא ב-$DIST_DIR.

יצירת קובץ אימג' לאתחול למכשירים ללא init_boot (גרסה קודמת)

במכשירים שאין בהם מחיצת init_boot, צריך קובץ בינארי של ramdisk. אפשר להשיג אותו על ידי הורדה של קובץ אימג' לאתחול של GKI ופריסה שלה. כל קובץ אימג' לאתחול של GKI ממהדורת Android המשויכת תפעל.

tools/mkbootimg/unpack_bootimg.py --boot_img=boot-5.4-gz.img
mv $KERNEL_ROOT/out/ramdisk gki-ramdisk.lz4

תיקיית היעד היא הספרייה ברמה העליונה של עץ ליבת המערכת (ספריית העבודה הנוכחית).

אם אתם מפתחים באמצעות ענף הגרסה האחרון של AOSP, אתם יכולים להוריד את ramdisk-recovery.img ארטיפקט הבנייה מגרסת aosp_arm64 בכתובת ci.android.com ולהשתמש בו כקובץ בינארי של ramdisk.

אם יש לכם קובץ בינארי של ramdisk והעתקתם אותו אל gki-ramdisk.lz4 בתיקיית השורש של build הליבה, אתם יכולים ליצור קובץ אימג' לאתחול על ידי הרצה:

BUILD_BOOT_IMG=1 SKIP_VENDOR_BOOT=1 KERNEL_BINARY=Image GKI_RAMDISK_PREBUILT_BINARY=gki-ramdisk.lz4 BUILD_CONFIG=common/build.config.gki.aarch64 build/build.sh

אם אתם עובדים עם ארכיטקטורה מבוססת x86, מחליפים את Image ב-bzImage ואת aarch64 ב-x86_64:

BUILD_BOOT_IMG=1 SKIP_VENDOR_BOOT=1 KERNEL_BINARY=bzImage GKI_RAMDISK_PREBUILT_BINARY=gki-ramdisk.lz4 BUILD_CONFIG=common/build.config.gki.x86_64 build/build.sh

הקובץ הזה נמצא בספריית פריטי המידע שנוצרו בתהליך פיתוח (Artifact) $KERNEL_ROOT/out/$KERNEL_VERSION/dist.

קובץ האימג' לאתחול נמצא ב-out/<kernel branch>/dist/boot.img.