ב-Android יש אפשרות להטמעת קובצי עזר של כל הרכיבים שנדרשים כדי להטמיע את Android Virtualization Framework. כרגע ההטמעה מוגבלת ל-ARM64. בדף הזה מוסבר על ארכיטקטורת framework.
ברקע
ארכיטקטורת Arm מאפשרת עד ארבע רמות חריגות, כשרמת החריגה 0 (EL0) היא ברמה הנמוכה ביותר, ורמת החריגה 3 (EL3). החלק הגדול ביותר של ה-codebase של Android (כל הרכיבים של מרחב המשתמשים) פועל ב-EL0. שאר מה שמכונה Android הוא הליבה (kernel) של Linux, שפועלת ב-EL1.
שכבת EL2 מאפשרת שימוש ב-hypervisor שמאפשר לבודד זיכרון ומכשירים במכונות וירטואליות (pVM) נפרדות ב-EL1/EL0, עם סודיות ואחריות גבוהה לתקינות.
Hypervisor
המכונה הווירטואלית שמבוססת על ליבה (pKVM) מוגנת מתבססת על hypervisor של Linux KVM, שהורחב עם היכולת להגביל את הגישה למטענים ייעודיים (payloads) שרצים במכונות וירטואליות של אורחים שמסומנות כ'מוגנות' בזמן היצירה.
ב-KVM/arm64 יש תמיכה במצבי ביצוע שונים, בהתאם לזמינות של תכונות מסוימות של המעבד (CPU), כלומר Virtualization Host extensions (VHE) (ARMv8.1 ואילך). באחד מהמצבים האלה, שידוע גם כמצב שאינו VHE, קוד ה-hypervisor מחולק מתמונת הליבה במהלך ההפעלה ומותקן ב-EL2, ואילו הליבה עצמה פועלת ב-EL1. למרות שהרכיב EL2 ב-KVM הוא חלק מ-codebase של Linux, הוא רכיב קטן שאחראי למעבר בין כמה EL1s. רכיב ה-hypervisor מבוסס על Linux, אבל נמצא בקטע זיכרון ייעודי נפרד של תמונת vmlinux
. pKVM משתמשת בעיצוב הזה על ידי הרחבת הקוד של hypervisor עם תכונות חדשות, וכך מאפשרת להטיל הגבלות על הליבה של מארח Android ומרחב המשתמשים, ומגבילה את הגישה של המארח לזיכרון של האורח ול-hypervisor.
מודולים של ספקי pKVM
מודול של ספק pKVM הוא מודול ספציפי לחומרה שמכיל פונקציונליות ספציפית למכשיר, כמו מנהלי התקנים של יחידות ניהול זיכרון לפלט-פלט (IOMMU). המודולים האלה מאפשרים לנייד תכונות אבטחה שמחייבות גישה ברמת חריגה 2 (EL2) ל-pKVM.
כדי ללמוד איך להטמיע ולטעון מודול של ספק pKVM, קראו את המאמר הטמעה של מודול ספק pKVM.
תהליך ההפעלה
האיור הבא מתאר את תהליך האתחול של pKVM:
- תוכנת האתחול נכנסת לליבה (kernel) הגנרית ב-EL2.
- הליבה (kernel) הגנרית מזהה שהיא רצה ב-EL2 ומוציאה את ההרשאות שלה ל-EL1, בזמן שה-pKVM והמודולים שלו ממשיכים לפעול ב-EL2. בנוסף, המודולים של ספקי pKVM נטענים בשלב הזה.
- הליבה (kernel) הגנרית ממשיכה באתחול כרגיל, וטוענת את כל מנהלי ההתקנים הנחוצים במכשיר עד שהיא מגיעה למרחב המשתמשים. בשלב הזה, ה-pKVM קיים ומטפל בטבלאות של דפים בשלב 2.
תהליך האתחול סומכים על תוכנת האתחול שתשמור על תקינות תמונת הליבה רק במהלך האתחול המוקדם. כשהליבה חסרה, היא לא נחשבת יותר כמהימנה על ידי hypervisor, ואחר כך אחראי להגן על עצמה גם אם הליבה נפרצה.
אם יש באותה תמונה בינארית את הליבה של Android ואת ה-Hypervisor, יכול להיות ממשק תקשורת צמוד מאוד ביניהם. הצימוד ההדוק הזה מבטיח עדכונים אטומיים של שני הרכיבים, שלא צריך לשמור על הממשק יציב ומספק מידה רבה של גמישות בלי לפגוע בתחזוקה לטווח הארוך. הצימוד ההדוק מאפשר גם אופטימיזציה של הביצועים כששני הרכיבים יכולים לשתף פעולה בלי להשפיע על התחייבויות האבטחה שמספק hypervisor.
בנוסף, השימוש ב-GKI בסביבה העסקית של Android מאפשר לפרוס באופן אוטומטי את hypervisor של pKVM במכשירי Android באותו בינארי כמו הליבה.
הגנת גישה לזיכרון של המעבד (CPU)
ארכיטקטורת Arm מציינת יחידה לניהול זיכרון (MMU) המפוצלת בשני שלבים עצמאיים, ובשניהם אפשר להשתמש כדי להטמיע תרגום כתובות ובקרת גישה לחלקים שונים בזיכרון. שלב 1 MMU נשלט על ידי EL1 ומאפשר רמה ראשונה של תרגום כתובת. שלב 1 MMU משמש את Linux לניהול מרחב הכתובות הווירטואלי שמסופק לכל תהליך של מרחב משתמשים ולמרחב כתובות וירטואלי משלו.
שלב 2 MMU נשלט על ידי EL2 ומאפשר החלה של תרגום כתובת שנייה על כתובת הפלט של שלב 1 MMU, וכתוצאה מכך נוצרת כתובת פיזית (PA). hypervisors יכולים להשתמש בתרגום בשלב 2 כדי לשלוט בגישה לזיכרון מכל המכונות הווירטואליות של האורחים ולתרגם אותן. כפי שמתואר בתרשים 2, כששני שלבי התרגום מופעלים, כתובת הפלט של שלב 1 נקראת כתובת פיזית ביניים (IPA). הערה: הכתובת הווירטואלית (VA) מתורגמת ל-IPA ולאחר מכן ל-PA.
בעבר, KVM פועלת כשתרגום שלב 2 היה מופעל בזמן הפעלת אורחים ושלב 2 מושבת בזמן הפעלת ליבת Linux המארח. הארכיטקטורה הזו מאפשרת גישה לזיכרון משלב המארח 1 MMU כדי לעבור דרך שלב 2 MMU, ולכן מאפשרת גישה בלתי מוגבלת מהמארח לדפי זיכרון של אורחים. לעומת זאת, pKVM מפעילה הגנה בשלב 2 גם בהקשר של המארח, ומעניקה את hypervisor האחראי להגנה על דפי הזיכרון של האורחים במקום על המארח.
ב-KVM נעשה שימוש מלא בתרגום כתובת בשלב 2 כדי להטמיע מיפויים מורכבים של IPA/PA עבור האורחים, וכך נוצרת אשליה של זיכרון רציף עבור האורחים, למרות הפיצול הפיזי. עם זאת, השימוש ב-MMU בשלב 2 אצל המארח מוגבל לבקרת גישה בלבד. שלב 2 של המארח עובר מיפוי זהויות, מה שמבטיח שהזיכרון הרציף במרחב ה-IPA של המארח רציף במרחב ה-PA. הארכיטקטורה הזו מאפשרת שימוש במיפויים גדולים בטבלת הדפים, וכתוצאה מכך מפחיתה את הלחץ על מאגר הנתונים הזמני של התרגום (TLB). מכיוון שאפשר להוסיף לאינדקס מיפוי זהויות (PA), שלב 2 המארח משמש גם למעקב אחרי הבעלות על הדף ישירות בטבלת הדפים.
הגנה על גישה ישירה לזיכרון (DMA)
כפי שתואר קודם, ביטול המיפוי של דפי אורחים ממארח Linux בטבלאות הדפים של המעבד (CPU) הוא שלב הכרחי אבל לא מספיק כדי להגן על זיכרון האורח. pKVM גם צריכה להגן מפני גישה לזיכרון שמתבצעות על ידי מכשירים שתומכים ב-DMA במסגרת השליטה של הליבה של המארח, ומפני אפשרות של התקפת DMA כתוצאה ממארח זדוני. כדי למנוע ממכשיר כזה לגשת לזיכרון של אורח, ל-pKVM נדרשת יחידת ניהול זיכרון מסוג פלט-פלט (IOMMU) לכל מכשיר שתומך ב-DMA במערכת, כפי שמוצג באיור 3.
לכל הפחות, חומרת IOMMU מספקת אמצעים להענקה ולביטול של גישת קריאה/כתיבה במכשיר לזיכרון פיזי ברמת פירוט הדף. עם זאת, חומרת ה-IOMMU הזו מגבילה את השימוש במכשירים במכונות וירטואליות כי הם מניחים בשלב 2 של מיפוי זהויות.
כדי להבטיח בידוד בין מכונות וירטואליות, טרנזקציות זיכרון שנוצרות בשם ישויות שונות צריכות להיות זהות באמצעות ה-IOMMU, כך שאפשר יהיה להשתמש בקבוצה המתאימה של טבלאות דפים.
בנוסף, הפחתת כמות הקוד שספציפי ל-SoC ב-EL2 היא אסטרטגיית מפתח לצמצום בסיס המחשוב המהימן (TCB) של pKVM והפעלה מנוגדת להכללת מנהלי התקנים של IOMMU ב-hypervisor. כדי לפתור את הבעיה הזו, המארח ב-EL1 אחראי על משימות עזר של ניהול IOMMU, כמו ניהול צריכת חשמל, אתחול ובמקרים הרלוונטיים, טיפול בהפרעות.
עם זאת, הענקת שליטה למצב המכשיר על ידי המארח מציבים דרישות נוספות בממשק התכנות של חומרת IOMMU כדי להבטיח שלא תהיה אפשרות לעקוף בדיקות הרשאה באמצעים אחרים, לדוגמה, לאחר איפוס של המכשיר.
ארכיטקטורת IOMMU סטנדרטית ונתמכת היטב למכשירים עם זרוע מאפשרת בידוד והקצאה ישירה היא ארכיטקטורת Arm System Memory Management Unit (SMMU). הארכיטקטורה הזו היא פתרון העזר המומלץ.
הבעלות על הזיכרון
בזמן האתחול, ההנחה היא שכל הזיכרון שאינו hypervisor הוא בבעלות המארח, וה-hypervisor עוקב אחרי זה. כשנוצרת pVM, המארח תורם דפי זיכרון כדי לאפשר את ההפעלה שלו, וה-hypervisor מעביר את הבעלות על הדפים האלה מהמארח ל-pVM. לכן, ה-hypervisor הגדירה הגבלות על בקרת גישה בטבלת הדפים בשלב 2 של המארח, כדי למנוע ממנו לגשת לדפים שוב תוך שמירה על סודיות לאורחים.
התקשורת בין המארח לבין האורחים מתאפשרת על ידי שיתוף זיכרון מבוקר ביניהם. האורחים יכולים לשתף חלק מהדפים שלהם בחזרה עם המארח באמצעות היפר-קריאה, שמורה ל-hypervisor למפות מחדש את הדפים האלה בטבלת הדפים בשלב 2 של המארח. באופן דומה, התקשורת של המארח עם TrustZone מתאפשרת על ידי פעולות שיתוף זיכרון ו/או הלוואות. כל הפעולות האלה נמצאות בפיקוח ובמעקב קפדני על ידי pKVM באמצעות מפרט הקושחה עבור Arm (FF-A).
דרישות הזיכרון של ה-pVM עשויות להשתנות עם הזמן, ולכן ניתנת היפר-קריאה שמאפשרת לוותר על הבעלות על דפים ספציפיים ששייכים למקבל הקריאה החוזרת. בפועל, משתמשים בהיפר-קריאה הזו עם פרוטוקול בלון וירטואלי כדי לאפשר ל-VMM לבקש זיכרון חזרה מה-pVM, ול-pVM להודיע ל-VMM באופן מבוקר על דפים שהוסרו.
ה-hypervisor אחראי לעקוב אחרי הבעלות על כל דפי הזיכרון במערכת, ולבדוק אם הם משותפים או מושאלים לישויות אחרות. רוב המעקב אחרי המצב הזה מתבצע באמצעות מטא-נתונים שמצורפים לטבלאות הדפים של המארח ובשלב 2 של האורחים, תוך שימוש בביטים שמורים ברשומות של טבלת הדפים (PTE), שכפי שהשם שלהם מרמז, שמורים לשימוש בתוכנה.
המארח צריך לוודא שהוא לא מנסה לגשת לדפים ש-hypervisor לא ניגש אליהם. גישה לא חוקית למארח גורמת להחדרת חריג סינכרוני למארח על ידי hypervisor, ויכולה לגרום לכך שהמשימה האחראית במרחב המשתמשים תקבל אות SEGV או לקריסה של הליבה של המארח. כדי למנוע גישה לא מכוונת, דפים שנתרמו לאורחים לא עומדים בדרישות להחלפה או למיזוג על ידי הליבה של המארח.
טיפול בשיבושים וטיימרים
הפרעות הן חלק חשוב באינטראקציה של אורחים עם מכשירים ובתקשורת בין מעבדים, שבהם הפרעות בין מעבדי מידע (IPI) הן מנגנון התקשורת העיקרי. מודל ה-KVM הוא להאציל את כל ניהול ההפרעות הווירטואליות למארח ב-EL1, ולשם כך הוא פועל כחלק מה-hypervisor של hypervisor.
ב-pKVM יש אמולציה גנרית 3 (GICv3) של בקר הפרעות גנרי על בסיס קוד ה-KVM הקיים. הטיפול בטיימר וב-IPI הוא כחלק מקוד האמולציה הלא מהימן הזה.
תמיכה ב-GICv3
הממשק בין EL1 ל-EL2 צריך לוודא שמצב ההפרעה המלא גלוי למארח EL1, כולל עותקים של רישומי ה-hypervisor שקשורים להפרעות. בדרך כלל החשיפה הזו מתבצעת באמצעות אזורי זיכרון משותפים, אחד לכל מעבד (CPU) וירטואלי (vCPU).
אפשר לפשט את קוד התמיכה בזמן הריצה לרישום המערכת כדי לתמוך רק ברישומי הפרעות שנוצרו על ידי תוכנה (SGIR) וברישום Disable InterruptRegister (DIR) של רישום. לפי הארכיטקטורה, הרישומים האלה תמיד יכולים לתעד ל-EL2, והמלכודות האחרות היו שימושיות עד עכשיו רק לצמצום טעויות. כל השאר מטופל בחומרה.
בצד של MMIO, כל האמולציה של EL1 מתבצעת באמצעות שימוש חוזר בכל התשתית הנוכחית ב-KVM. לסיום, הפונקציה Wait for Interrupt (WFI) תמיד עוברת ממסר ל-EL1, כי זה אחד מהפרימיטיביים הבסיסיים לתזמון ב-KVM.
תמיכה בטיימר
ערך הקומפרטור של הטיימר הווירטואלי צריך להיחשף ל-EL1 בכל WFI הלוכד, כדי ש-EL1 יוכל להחדיר טיימר להפרעות בזמן שה-vCPU חסומה. הטיימר הפיזי הוא באמולציה מלאה, וכל המלכודות ממסרות ל-EL1.
טיפול ב-MMIO
כדי לתקשר עם צג המכונה הווירטואלית (VMM) ולבצע אמולציה של GIC, צריך להעביר את מלכודות MMIO חזרה למארח ב-EL1 לצורך מיון נוסף. ל-pKVM נדרשים:
- IPA וגודל הגישה
- נתונים במקרה של כתיבה
- הסופיות של המעבד (CPU) בנקודת הקליטה
בנוסף, מלכודות עם רישום של שימוש כללי (GPR) כמקור או ליעד מועברות באמצעות פסאודו-רישום של העברה מופשטת.
ממשקים לאורחים
האורחים יכולים לתקשר עם אורח מוגן באמצעות שילוב של קריאות יתר וגישה לזיכרון לאזורים לכודים. קריאות היפר-קריאות נחשפות בהתאם לתקן SMCCC, עם הטווח השמור להקצאת ספק על ידי KVM. לקריאות ההיפר-קישור הבאות יש חשיבות מיוחדת לאורחים של pKVM.
פעולות היפר-שיחות כלליות
- PSCI מספק מנגנון סטנדרטי שמאפשר לאורח לשלוט במחזור החיים של ה-vCPU, כולל העברה, השבתה וכיבוי המערכת.
- TRNG מספק מנגנון סטנדרטי שבו האורח יכול לבקש אנטרופיה מה-pKVM, ומעביר את הקריאה ל-EL3. המנגנון הזה שימושי במיוחד כשלא ניתן לסמוך על המארח שיבצע וירטואליזציה של מחולל מספרים אקראיים לחומרה (RNG).
קריאות היפר-שיחות ב-pKVM
- שיתוף הזיכרון עם המארח למארח אין גישה בהתחלה לכל זיכרון האורח, אבל הגישה למארח נחוצה כדי לתקשר עם זיכרון משותף ובמכשירים מוגבלים שמסתמכים על מאגרי נתונים זמניים משותפים. קריאות היפר-קריאה לשיתוף ולביטול השיתוף של דפים עם המארח מאפשרות לאורח להחליט בדיוק אילו חלקים בזיכרון יהיו נגישים לשאר מכשירי Android ללא צורך בלחיצת יד.
- שחרור זיכרון מהמארח. בדרך כלל, כל הזיכרון של האורח שייך לאורח, עד שהוא מושמד. המצב הזה יכול לא להתאים למכונות וירטואליות לטווח ארוך עם דרישות זיכרון שמשתנות לאורך זמן. ההיפר-קריאה
relinquish
מאפשרת לאורח להעביר באופן מפורש את הבעלות על הדפים בחזרה למארח, בלי לדרוש סיום של האורח. - מלכודות גישה לזיכרון למארח. בדרך כלל, אם אורח KVM ניגש לכתובת שלא תואמת לאזור זיכרון חוקי, ה-thread של ה-vCPU יוצא מהמארח, ובדרך כלל הגישה משמשת ל-MMIO ועבר אמולציה של ה-VMM במרחב המשתמש. כדי לאפשר את הטיפול הזה, נדרש pKVM לפרסם פרטים על ההוראה הבעייתית, כמו הכתובת, לרשום פרמטרים או את התוכן שלו כדי לאפשר גישה חזרה למארח. חשיפה כזו עלולה לחשוף בטעות מידע אישי רגיש מאורח מוגן. pKVM פותרת את הבעיה הזו על ידי התייחסות לפגמים ב- הפתרון הזה נקרא שומר MMIO.
מכשיר קלט/פלט וירטואלי (virtio)
Virtio הוא תקן פופולרי, נייד ומותאם אישית להטמעה ולאינטראקציה עם מכשירים שעברו אימות. רוב המכשירים שנחשפים לאורחים מוגנים מוטמעים באמצעות virtio. Virtio גם מהווה את הבסיס של הטמעת Vsock שמשמשת לתקשורת בין אורח מוגן לבין שאר מכשירי Android.
מכשירי Virtio מוטמעים בדרך כלל במרחב המשתמש של המארח על ידי ה-VMM, ומיירטים את הרשאות הגישה של הזיכרון הלכד מהאורח לממשק ה-MMIO של מכשיר ה-Vertio ואמולציה של ההתנהגות הצפויה. הגישה ל-MMIO יקרה יחסית, כי כל גישה למכשיר דורשת הלוך ושוב ל-VMM ולחזרה. לכן, רוב העברת הנתונים בפועל בין המכשיר לאורח מתבצעת באמצעות סדרת מאפיינים בזיכרון. ההנחה העיקרית לגבי virtio היא שהמארח יכול לגשת לזיכרון של האורח באופן שרירותי. ההנחה הזו ברורה בעיצוב של הניצחון, כי היא עשויה לכלול סימונים שמעבירים האורח עבור אמולציית המכשיר כדי שיוכלו לגשת אליהם ישירות.
למרות שניתן להשתמש בהיפר-קריאות לשיתוף הזיכרון שתוארו קודם כדי לשתף חוצץ של נתוני וירטואלי מהאורח עם המארח, השיתוף מבוצע לצורך ברמת פירוט הדף, והוא עלול לחשוף יותר נתונים ממה שנדרש אם מאגר הנתונים הזמני קטן מזה שבדף. במקום זאת, האורח מוגדר להקצות גם את הסביבות הווירטואליות וגם את מאגרי הנתונים המתאימים מחלון קבוע של זיכרון משותף, כשהנתונים מועתקים (חוזרים) אל החלון ומהחלון בהתאם.
אינטראקציה עם TrustZone
למרות שהמשתתפים לא יכולים לתקשר ישירות עם TrustZone, המארח צריך עדיין יכול לבצע שיחות SMC בעולם המאובטח. הקריאות האלו יכולות לציין מאגרי זיכרון עם מענה פיזי, שלמארח אין גישה אליהם. מכיוון שהתוכנה המאובטחת בדרך כלל לא מודעת לנגישות של מאגר הנתונים הזמני, מארח זדוני יכול להשתמש במאגר הנתונים הזמני הזה כדי לבצע התקפת נציג מבולבל (בדומה להתקפת DMA). כדי למנוע התקפות כאלה, pKVM לוכד את כל קריאות ה-SMC של המארח ל-EL2 ומתפקד כשרת proxy בין המארח לבין הצג המאובטח ב-EL3.
שיחות PSCI מהמארח מועברות לקושחת EL3 עם שינויים קלים. באופן ספציפי, נקודת הכניסה למעבד (CPU) שמתחבר לאינטרנט או שחוזרת מההשעיה משוכתבת, כך שטבלת הדפים של שלב 2 תותקן ב-EL2 לפני חזרה למארח ב-EL1. במהלך ההפעלה, ההגנה הזו נאכפת על ידי pKVM.
הארכיטקטורה הזו מסתמכת על SoC שתומכת ב-PSCI, עדיף בשימוש בגרסה מעודכנת של TF-A כקושחת EL3.
קושחה מסגרת לזרוע (FF-A) קובעת סטנדרטיזציה של אינטראקציות בין העולם הרגיל לעולם המאובטח, במיוחד בנוכחות של hypervisor מאובטח. חלק גדול במפרט מגדיר מנגנון לשיתוף זיכרון עם העולם המאובטח, באמצעות פורמט הודעה נפוץ וגם מודל הרשאות מוגדר היטב לדפים הבסיסיים. הודעות pKVM שרתי proxy FF-A עוזרות לוודא שהמארח לא מנסה לשתף זיכרון עם הצד המאובטח שאין לו הרשאות מספיקות.
הארכיטקטורה הזו מסתמכת על תוכנה מאובטחת שאוכפת את מודל הגישה לזיכרון, כדי להבטיח שאפליקציות מהימנות וכל תוכנה אחרת שפועלת בעולם המאובטח יוכלו לגשת לזיכרון רק אם הן בבעלות בלעדית של העולם המאובטח או ששותפו איתה באופן מפורש באמצעות FF-A. במערכת עם S-EL2, אכיפת מודל הגישה לזיכרון צריכה להתבצע על ידי Secure Partition Manager Core (SPMC), כמו Hafnium, ששומר טבלאות של דפים בשלב 2 בעולם המאובטח. במערכת בלי S-EL2, שלב ה-TEE יכול לאכוף מודל גישה לזיכרון דרך טבלאות הדפים שלו בשלב 1.
אם הקריאה של SMC ל-EL2 היא לא קריאה ל-PSCI או הודעה שמוגדרת ב-FF-A, מפתחות SMC שלא מטופלים יועברו ל-EL3. ההנחה היא שהקושחה המאובטחת (המהימנה מספיק) יכולה לטפל בצורה בטוחה בהתקני SMC שלא מטופלים, כי הקושחה מבינה את אמצעי הזהירות שצריך לשמור על בידוד ה-pVM.
מוניטור למכונה וירטואלית
crosvm הוא מוניטור למכונה וירטואלית (VMM) שמריץ מכונות וירטואליות דרך ממשק ה-KVM של Linux. מה שמייחד את crosvm הוא התמקדות בבטיחות באמצעות שפת התכנות Rust וארגז חול (sandbox) סביב מכשירים וירטואליים כדי להגן על הליבה (kernel) של המארח. מידע נוסף על crosvm זמין כאן.
מתארי קבצים ויוקטלים
KVM חושפת את המכשיר עם התווים /dev/kvm
למרחב המשתמשים באמצעות ביוקטים שמרכיבים את ה-KVM API. היוקטלים שייכים לקטגוריות הבאות:
- מריצים שאילתות ומגדירות מאפיינים גלובליים של המערכת, שמשפיעים על כל מערכת המשנה של KVM, ויוצרים מכונות pVM.
- הרצת שאילתות על מכונות וירטואליות ומגדירות מאפיינים שיוצרים מעבדים וירטואליים (vCPUs) ומכשירים, ומשפיעים על כל ה-pVM, למשל פריסת הזיכרון ומספר המעבדים (CPUs) הווירטואליים (vCPU) והמכשירים.
- שאילתת vCPU ioctls ומגדירים מאפיינים ששולטים בפעולה של מעבד וירטואלי יחיד.
- שליחת שאילתה על המכשיר ומגדירה מאפיינים ששולטים בפעולה של מכשיר וירטואלי יחיד.
כל תהליך crosvm מפעיל מופע אחד בלבד של מכונה וירטואלית. בתהליך הזה נעשה שימוש ב-ioctl של המערכת KVM_CREATE_VM
כדי ליצור מתאר קובץ ל-VM שיכול לשמש להנפקת יוקיקט ל-pVM. ioctl KVM_CREATE_VCPU
או KVM_CREATE_DEVICE
ב-VM FD יוצר vCPU או מכשיר ומחזיר מתאר קובץ שמצביע אל המשאב החדש. אפשר להשתמש ביוקטלים ב-vCPU או ב-FD במכשיר כדי לשלוט במכשיר שנוצר באמצעות ioctl ב-VM FD. במעבדי vCPU, המשימה הזו כוללת את המשימה החשובה של הרצת קוד אורח.
באופן פנימי, crosvm רושם את מתארי הקבצים של המכונה הווירטואלית בליבה באמצעות ממשק epoll
שמופעל בקצה. לאחר מכן, הליבה שולחת הודעה ל-crosvm בכל פעם שיש אירוע חדש בהמתנה באחד מתיאורי הקובץ.
ל-pKVM נוספה יכולת חדשה, KVM_CAP_ARM_PROTECTED_VM
, שאפשר להשתמש בה כדי לקבל מידע על סביבת ה-pVM ולהגדיר מצב מוגן למכונה וירטואלית. הפקודה crosvm משתמשת בה במהלך יצירת ה-pVM אם הדגל --protected-vm
מועבר, כדי לשלוח שאילתות ולשריין את כמות הזיכרון המתאימה לקושחת ה-pVM, ולאחר מכן להפעיל את המצב המוגן.
הקצאת זיכרון
אחד מתחומי האחריות העיקריים של VMM הוא הקצאת הזיכרון של המכונה הווירטואלית וניהול פריסת הזיכרון שלה. crosvm יוצר פריסת זיכרון קבועה המתוארת בטבלה שלמטה.
FDT במצב רגיל | PHYS_MEMORY_END - 0x200000
|
פינוי מקום | ...
|
רמדיסק | ALIGN_UP(KERNEL_END, 0x1000000)
|
ליבה | 0x80080000
|
תוכנת אתחול | 0x80200000
|
FDT במצב BIOS | 0x80000000
|
בסיס זיכרון פיזי | 0x80000000
|
קושחת pVM | 0x7FE00000
|
זיכרון המכשיר | 0x10000 - 0x40000000
|
הזיכרון הפיזי מוקצה באמצעות mmap
, והזיכרון נתרם למכונה הווירטואלית כדי לאכלס את אזורי הזיכרון שלה, שנקראים memslots, באמצעות יוקיקטל KVM_SET_USER_MEMORY_REGION
. לכן, כל זיכרון ה-pVM של האורח משויך למכונה ב-crosvm שמנהלת אותו, וכתוצאה מכך התהליך עשוי להתבטל (לסגור את המכונה הווירטואלית) אם המארח מתחיל להיגמר. כשמפסיקים מכונה וירטואלית, ה-hypervisor מאפס את הזיכרון באופן אוטומטי ומוחזר לליבה (kernel) של המארח.
ב-KVM הרגיל, ל-VMM יש גישה לכל הזיכרון של האורחים. כשמשתמשים ב-pKVM, אי אפשר למפות את זיכרון האורח ממרחב הכתובות הפיזיות של המארח כשתורמים אותו לאורחים. יוצא הדופן היחיד הוא זיכרון שהאורח משתף באופן מפורש, למשל במכשירים וירטואליים.
אזורי MMIO במרחב הכתובות של האורח לא ממופים. הגישה של האורח נלכדת לאזורים האלה, וכתוצאה מכך נוצר אירוע I/O ב-VM FD. המנגנון הזה משמש להטמעת מכשירים וירטואליים. במצב מוגן, כדי לצמצם את הסיכון לדליפת מידע בטעות, האורח צריך לאשר שאזור של מרחב הכתובות שלו משמש ל-MMIO באמצעות היפר-קריאה.
קביעת מועד
כל מעבד (CPU) וירטואלי מיוצג על ידי שרשור POSIX ומתוזמן על ידי המתזמן של Linux. ה-thread מפעיל את ה-ioctl KVM_RUN
ב-vCPU FD, וכתוצאה מכך ה-hypervisor עובר להקשר של ה-vCPU של האורח. מתזמן המארח מביא בחשבון את זמן השהייה בהקשר של אורח כזמן שמשמש את ה-thread המתאים ב-vCPU. הפונקציה KVM_RUN
מחזירה כאשר יש אירוע שה-VMM צריך לטפל בו, למשל קלט/פלט (I/O), סיום הפרעה או הפסקת ה-vCPU. ה-VMM יטפל באירוע ויתקשר שוב אל KVM_RUN
.
במהלך KVM_RUN
, ה-threads עדיין יכול לפעול לפני מתזמן המארח, מלבד להרצה של קוד ה-hypervisor של EL2, שלא ניתן לחיזוי. ל-pVM של האורח אין מנגנון לשליטה בהתנהגות הזו.
כל השרשורים של vCPU מתוזמנים כמו כל משימה אחרת במרחב המשתמשים, ולכן הם כפופים לכל מנגנוני QoS הרגילים. באופן ספציפי, אפשר לשייך כל שרשור של vCPU למעבדי CPU פיזיים, להציב אותם ביחידות cpuset, להגדיל או להגביל את השרשורים באמצעות אכיפת החסימות, לשנות את מדיניות העדיפות/התזמון שלהם ועוד.
מכשירים וירטואליים
ב-crosvm יש תמיכה בכמה מכשירים, כולל המכשירים הבאים:
- virtio-blk לתמונות דיסק מורכבות, לקריאה בלבד או לקריאה וכתיבה
- vhost-vsock לתקשורת עם המארח
- virtio-pci בתור העברת virtio
- שעון בזמן אמת של pl030 (RTC)
- UART 16550a לתקשורת טורית
קושחת pVM
קושחת ה-pVM (pvmfw) היא הקוד הראשון שמופעל על ידי pVM, בדומה ל-ROM לאתחול של מכשיר פיזי. המטרה העיקרית של pvmfw היא לבצע הפעלה מאובטחת של האתחול ולגלות את הסוד הייחודי של ה-pVM. אין הגבלה לשימוש ב-pvmfw בכל מערכת הפעלה ספציפית, כמו מערכת ההפעלה Microvid נתמכת כי היא חתומה כראוי.
הקובץ הבינארי של pvmfw מאוחסן במחיצת Flash באותו שם, ומתעדכן באמצעות OTA.
הפעלת המכשיר
רצף השלבים הבא מתווסף לתהליך ההפעלה של מכשיר שתומך ב-pKVM:
- תוכנת האתחול של Android (ABL) טוען את pvmfw מהמחיצה שלו לזיכרון ומאמת את התמונה.
- ה-ABL מקבל את הסודות של Device Identifier Composition Engine (DICE) (מזהי מכשירים מורכבים (CDI) ושרשרת אישורי DICE) מ-Root of Trust.
- ה-ABL מפיק את מזהי ה-CDI הנדרשים ל-pvmfw ומצרף אותם לקובץ הבינארי של pvmfw.
- ה-ABL מוסיף צומת אזור שמור של
linux,pkvm-guest-firmware-memory
ל-DT, ומתאר את המיקום והגודל של הקובץ הבינארי של Pvmfw ואת הסודות שנוצרו בשלב הקודם. - השליטה הקולית של ABL מעבירה את Linux ל-Linux ו-Linux מאתחלת את pKVM.
- pKVM מבטלת את המיפוי של אזור הזיכרון של pvmfw מטבלאות הדפים שבשלב 2 של המארח, ומגינה עליו מפני המארח (והאורחים) לאורך זמן הפעולה התקינה של המכשיר.
אחרי אתחול המכשיר, ה-Microdroid מופעל לפי השלבים בקטע רצף אתחול במסמך Microdroid.
אתחול pVM
כשיוצרים pVM, ה-crosvm (או VMM אחר) חייב ליצור חריץ memlo גדול מספיק כדי לאכלס את התמונה בפורמט pvmfw באמצעות ה-hypervisor. ה-VMM מוגבל גם ברשימת הרשומות שאפשר להגדיר את הערך הראשוני שלהן (x0-x14 ל-vCPU הראשי, ולא למעבדי vCPU משניים). הרשומות האחרות נשמרות והן חלק מ-hypervisor-pvmfw ABI.
כשמריצים את ה-pVM, ה-hypervisor שולטת ב-vCPU הראשית באופן ידני ל-pvmfw. הקושחה מצפה ש-crosvm יטען ליבה (kernel) חתומה של AVB, שיכולה להיות תוכנת אתחול או כל תמונה אחרת, ו-FDT לא חתום לזיכרון בקיזוזים ידועים. pvmfw מאמת את חתימת ה-AVB, ואם הפעולה מצליחה, יוצרת עץ מכשיר מהימן מה-FDT שהתקבל, מוחקת את הסודות שלו מהזיכרון ומאפסת את הסודות שלו מהזיכרון. אם אחד משלבי האימות נכשל, הקושחה תנפיק היפר-קריאה מסוג SYSTEM_RESET
ל-PSCI.
בין המגפיים, המידע על מכונת ה-pVM מאוחסן במחיצה (מכשיר virtio-blk) ומוצפן באמצעות הסוד של pvmfw כדי להבטיח שלאחר הפעלה מחדש, הסוד מוקצה למכונה הנכונה.