Android, Android वर्चुअलाइज़ेशन फ़्रेमवर्क को लागू करने के लिए ज़रूरी सभी कॉम्पोनेंट के रेफ़रंस लागू करने की सुविधा देता है. फ़िलहाल, यह सुविधा सिर्फ़ ARM64 पर काम करती है. इस पेज पर, फ़्रेमवर्क के आर्किटेक्चर के बारे में बताया गया है.
बैकग्राउंड
Arm आर्किटेक्चर में, चार एक्सेप्शन लेवल की अनुमति होती है. एक्सेप्शन लेवल 0 (EL0) में सबसे कम और एक्सेप्शन लेवल 3 (EL3) में सबसे ज़्यादा विशेषाधिकार होते हैं. Android कोडबेस का सबसे बड़ा हिस्सा (सभी यूज़रस्पेस कॉम्पोनेंट) EL0 पर चलता है. बाकी हिस्सा, आम तौर पर "Android" कहलाता है. यह Linux कर्नेल होता है, जो EL1 पर चलता है.
EL2 लेयर की मदद से, हाइपरवाइजर को शामिल किया जा सकता है. इससे EL1/EL0 पर, मेमोरी और डिवाइसों को अलग-अलग पीवीएम में अलग-अलग रखा जा सकता है. साथ ही, गोपनीयता और सुरक्षा की गारंटी भी मिलती है.
हाइपरवाइजर
सुरक्षित कर्नेल पर आधारित वर्चुअल मशीन (pKVM), Linux KVM हाइपरवाइजर पर बनाई गई है. इसमें, मेहमान वर्चुअल मशीन में चल रहे पेलोड के ऐक्सेस पर पाबंदी लगाने की सुविधा जोड़ी गई है. ये पेलोड, बनाने के समय 'सुरक्षित' के तौर पर मार्क किए जाते हैं.
KVM/arm64, सीपीयू की कुछ सुविधाओं की उपलब्धता के आधार पर, अलग-अलग तरीके से काम करता है. जैसे, वर्चुअलाइज़ेशन होस्ट एक्सटेंशन (VHE) (ARMv8.1 और उसके बाद के वर्शन). उनमें से एक मोड को आम तौर पर नॉन-वीएचई मोड कहा जाता है. इसमें, बूट के दौरान, हाइपरवाइजर कोड को कर्नेल इमेज से अलग किया जाता है और उसे EL2 पर इंस्टॉल किया जाता है. वहीं, कर्नेल खुद EL1 पर चलता है. KVM का EL2 कॉम्पोनेंट, लिनक्स कोडबेस का हिस्सा है. हालांकि, यह एक छोटा कॉम्पोनेंट है, जो एक से ज़्यादा EL1 के बीच स्विच करने की सुविधा देता है. हाइपरवाइजर कॉम्पोनेंट को Linux के साथ कॉम्पाइल किया जाता है. हालांकि, यह vmlinux
इमेज के अलग और खास मेमोरी सेक्शन में मौजूद होता है. pKVM, इस डिज़ाइन का फ़ायदा उठाकर, हाइपरवाइजर कोड को नई सुविधाओं के साथ बढ़ाता है. इससे, Android होस्ट कर्नेल और उपयोगकर्ता स्पेस पर पाबंदियां लगाई जा सकती हैं. साथ ही, होस्ट के ऐक्सेस को मेहमान मेमोरी और हाइपरवाइजर तक सीमित किया जा सकता है.
pKVM वेंडर मॉड्यूल
pKVM वेंडर मॉड्यूल, हार्डवेयर के हिसाब से बनाया गया मॉड्यूल होता है. इसमें डिवाइस के हिसाब से काम करने वाली सुविधाएं होती हैं, जैसे कि इनपुट-आउटपुट मेमोरी मैनेजमेंट यूनिट (आईओएमएमयू) ड्राइवर. इन मॉड्यूल की मदद से, सुरक्षा से जुड़ी उन सुविधाओं को pKVM पर पोर्ट किया जा सकता है जिनके लिए अपवाद लेवल 2 (EL2) का ऐक्सेस ज़रूरी है.
pKVM वेंडर मॉड्यूल को लागू और लोड करने का तरीका जानने के लिए, pKVM वेंडर मॉड्यूल लागू करना लेख पढ़ें.
बूट करने की प्रोसेस
इस इलस्ट्रेशन में, pKVM को बूट करने की प्रोसेस दिखाई गई है:
- बूटलोडर, EL2 पर सामान्य कर्नेल में प्रवेश करता है.
- सामान्य कर्नेल को पता चलता है कि वह EL2 पर चल रहा है और वह खुद को EL1 पर ले जाता है. हालांकि, pKVM और उसके मॉड्यूल, EL2 पर चलते रहते हैं. इसके अलावा, इस समय pKVM वेंडर मॉड्यूल लोड किए जाते हैं.
- सामान्य कर्नेल सामान्य तरीके से बूट होता है. साथ ही, यूज़र स्पेस तक पहुंचने तक डिवाइस के सभी ज़रूरी ड्राइवर लोड करता है. इस समय, pKVM चालू है और यह दूसरे चरण की पेज टेबल को मैनेज करता है.
बूट करने की प्रोसेस में, बूटलोडर पर भरोसा किया जाता है, ताकि वह शुरुआती बूट के दौरान, कर्नेल इमेज को पूरी तरह से सुरक्षित रख सके. जब कर्नेल से विशेष अधिकार हटा दिए जाते हैं, तो उसे अब हायपरवाइजर के भरोसेमंद नहीं माना जाता. इसके बाद, हायपरवाइजर की यह ज़िम्मेदारी होती है कि वह खुद को सुरक्षित रखे, भले ही कर्नेल से छेड़छाड़ की गई हो.
Android kernel और hypervisor को एक ही बाइनरी इमेज में रखने से, उनके बीच बेहतर तरीके से इंटरफ़ेस किया जा सकता है. इस टाइट कपलिंग की वजह से, दोनों कॉम्पोनेंट के अपडेट एक साथ किए जाते हैं. इससे, उनके बीच के इंटरफ़ेस को स्थिर रखने की ज़रूरत नहीं पड़ती. साथ ही, लंबे समय तक बनाए रखने की सुविधा के साथ-साथ, ज़्यादा सुविधाएं भी मिलती हैं. टाइट कपलिंग की मदद से, परफ़ॉर्मेंस को ऑप्टिमाइज़ भी किया जा सकता है. ऐसा तब होता है, जब दोनों कॉम्पोनेंट मिलकर काम करते हैं. हालांकि, इससे हाइपरवाइजर की ओर से दी गई सुरक्षा की गारंटी पर कोई असर नहीं पड़ता.
इसके अलावा, Android नेटवर्क में GKI का इस्तेमाल करने से, pKVM हाइपरवाइजर को Android डिवाइसों पर, कर्नेल के साथ एक ही बाइनरी में अपने-आप डिप्लॉय किया जा सकता है.
सीपीयू मेमोरी ऐक्सेस की सुरक्षा
Arm आर्किटेक्चर में, मेमोरी मैनेजमेंट यूनिट (MMU) को दो अलग-अलग चरणों में बांटा गया है. इन दोनों का इस्तेमाल, मेमोरी के अलग-अलग हिस्सों में ऐड्रेस ट्रांसलेशन और ऐक्सेस कंट्रोल लागू करने के लिए किया जा सकता है. पहले चरण के एमएमयू को ईएल1 से कंट्रोल किया जाता है. साथ ही, यह पते के पहले लेवल के ट्रांसलेशन की अनुमति देता है. Linux, पहले चरण के एमएमयू का इस्तेमाल, हर यूज़रस्पेस प्रोसेस और अपने वर्चुअल पता स्पेस के लिए दिए गए वर्चुअल पता स्पेस को मैनेज करने के लिए करता है.
दूसरे चरण का एमएमयू, EL2 से कंट्रोल किया जाता है. साथ ही, यह पहले चरण के एमएमयू के आउटपुट पते पर, दूसरे पते के अनुवाद को लागू करने की सुविधा देता है. इससे, फ़िज़िकल पता (पीए) बनता है. दूसरे चरण के अनुवाद का इस्तेमाल, सभी मेहमान VM से मेमोरी ऐक्सेस को कंट्रोल और अनुवाद करने के लिए, हाइपरवाइजर कर सकते हैं. जैसा कि दूसरी इमेज में दिखाया गया है, ट्रांसलेशन के दोनों चरणों के चालू होने पर, पहले चरण के आउटपुट पते को इंटरमीडिएट फ़िज़िकल पता (आईपीए) कहा जाता है. ध्यान दें: वर्चुअल पते (वीए) को आईपीए में और फिर पीए में बदला जाता है.
आम तौर पर, KVM में मेहमान ऑपरेटिंग सिस्टम चलाने के दौरान, अनुवाद के दूसरे चरण की सुविधा चालू रहती है. वहीं, होस्ट के तौर पर काम करने वाले Linux कर्नेल को चलाने के दौरान, अनुवाद के दूसरे चरण की सुविधा बंद रहती है. इस आर्किटेक्चर की मदद से, होस्ट के पहले चरण के एमएमयू से मेमोरी ऐक्सेस, दूसरे चरण के एमएमयू से गुज़रते हैं. इससे होस्ट से मेहमान के मेमोरी पेजों को बिना किसी पाबंदी के ऐक्सेस किया जा सकता है. दूसरी ओर, pKVM होस्ट कॉन्टेक्स्ट में भी दूसरे चरण की सुरक्षा चालू करता है. साथ ही, होस्ट के बजाय मेहमान मेमोरी पेजों की सुरक्षा करने का काम, हाइपरवाइजर को सौंपता है.
KVM, दूसरे चरण में पते के अनुवाद का पूरा फ़ायदा उठाता है, ताकि मेहमानों के लिए जटिल आईपीए/पीए मैपिंग लागू की जा सके. इससे, मेहमानों के लिए फ़िज़िकल फ़्रैगमेंटेशन के बावजूद, एक जैसी मेमोरी का भ्रम पैदा होता है. हालांकि, होस्ट के लिए, दूसरे चरण के MMU का इस्तेमाल सिर्फ़ ऐक्सेस कंट्रोल के लिए किया जा सकता है. होस्ट के दूसरे चरण को पहचान के हिसाब से मैप किया जाता है. इससे यह पक्का होता है कि होस्ट IPA स्पेस में एक साथ मौजूद मेमोरी, PA स्पेस में भी एक साथ मौजूद हो. इस आर्किटेक्चर की मदद से, पेज टेबल में बड़ी मैपिंग का इस्तेमाल किया जा सकता है. इससे ट्रांसलेशन लुकसाइड बफ़र (TLB) पर दबाव कम हो जाता है. आइडेंटिटी मैपिंग को PA से इंडेक्स किया जा सकता है. इसलिए, होस्ट के दूसरे चरण का इस्तेमाल, सीधे पेज टेबल में पेज का मालिकाना हक ट्रैक करने के लिए भी किया जाता है.
डायरेक्ट मेमोरी ऐक्सेस (डीएमए) की सुरक्षा
जैसा कि पहले बताया गया है, सीपीयू पेज टेबल में मेहमान पेजों को लिनक्स होस्ट से अनमैप करना, मेहमान मेमोरी को सुरक्षित रखने के लिए ज़रूरी है. हालांकि, यह पूरी सुरक्षा नहीं देता. pKVM को होस्ट कर्नेल के कंट्रोल में, डीएमए की सुविधा वाले डिवाइसों से मेमोरी ऐक्सेस होने से भी बचाना होता है. साथ ही, नुकसान पहुंचाने वाले होस्ट से डीएमए हमले की संभावना से भी बचाना होता है. इस तरह के डिवाइस को मेहमान मेमोरी ऐक्सेस करने से रोकने के लिए, pKVM को सिस्टम में मौजूद हर डीएमए डिवाइस के लिए इनपुट-आउटपुट मेमोरी मैनेजमेंट यूनिट (आईओएमएमयू) हार्डवेयर की ज़रूरत होती है, जैसा कि तीसरे चित्र में दिखाया गया है.
कम से कम, IOMMU हार्डवेयर, किसी डिवाइस को फ़िज़िकल मेमोरी के लिए, पेज के हिसाब से, पढ़ने/लिखने का ऐक्सेस देने और रद्द करने का तरीका उपलब्ध कराता है. हालांकि, यह IOMMU हार्डवेयर, pVM में डिवाइसों के इस्तेमाल पर पाबंदी लगाता है, क्योंकि वे पहचान से मैप किए गए दूसरे चरण का अनुमान लगाते हैं.
वर्चुअल मशीनों को अलग-अलग रखने के लिए, अलग-अलग इकाइयों के लिए जनरेट किए गए मेमोरी ट्रांज़ैक्शन को आईओएमएमयू से अलग किया जाना चाहिए, ताकि ट्रांसलेशन के लिए पेज टेबल के सही सेट का इस्तेमाल किया जा सके.
इसके अलावा, EL2 पर SoC के हिसाब से कोड की संख्या कम करना, pKVM के पूरे ट्रस्टेड कंप्यूटिंग बेस (टीसीबी) को कम करने की एक अहम रणनीति है. यह रणनीति, हाइपरवाइजर में IOMMU ड्राइवर को शामिल करने के ख़िलाफ़ है. इस समस्या को कम करने के लिए, EL1 पर मौजूद होस्ट, IOMMU के सहायक मैनेजमेंट टास्क के लिए ज़िम्मेदार होता है. जैसे, पावर मैनेजमेंट, शुरू करना, और जहां ज़रूरी हो वहां इंटरप्ट मैनेज करना.
हालांकि, होस्ट को डिवाइस की स्थिति के कंट्रोल में रखने से, IOMMU हार्डवेयर के प्रोग्रामिंग इंटरफ़ेस पर अतिरिक्त ज़रूरी शर्तें लागू होती हैं. इससे यह पक्का किया जा सकता है कि अनुमति की जांच को दूसरे तरीकों से बायपास न किया जा सके. उदाहरण के लिए, डिवाइस को रीसेट करने के बाद.
Arm डिवाइसों के लिए, स्टैंडर्ड और अच्छी तरह से काम करने वाला आईओएमएमयू, Arm सिस्टम मेमोरी मैनेजमेंट यूनिट (एसएमएमयू) आर्किटेक्चर है. यह आर्किटेक्चर, डिवाइस को अलग करने और सीधे असाइनमेंट करने की सुविधा देता है. यह आर्किटेक्चर, रेफ़रंस के तौर पर सुझाया गया समाधान है.
मेमोरी का मालिकाना हक
बूट होने के समय, यह माना जाता है कि Hypervisor के अलावा बाकी सभी मेमोरी का मालिकाना हक होस्ट के पास है. साथ ही, Hypervisor इसे ट्रैक करता है. जब कोई pVM स्पॉन होता है, तो उसे बूट करने के लिए होस्ट, मेमोरी पेज देता है. साथ ही, हाइपरवाइजर उन पेजों का मालिकाना हक, होस्ट से pVM पर ट्रांसफ़र करता है. इसलिए, हाइपरवाइजर, होस्ट के दूसरे चरण की पेज टेबल में ऐक्सेस कंट्रोल की पाबंदियां लगाता है, ताकि वह पेजों को फिर से ऐक्सेस न कर सके. इससे, मेहमान की गोपनीयता बनी रहती है.
होस्ट और मेहमानों के बीच बातचीत करने के लिए, कंट्रोल की गई मेमोरी शेयर की जाती है. मेहमान, हाइपरकॉल का इस्तेमाल करके अपने कुछ पेजों को होस्ट के साथ शेयर कर सकते हैं. यह हाइपरवाइजर को, होस्ट के दूसरे चरण की पेज टेबल में उन पेजों को फिर से मैप करने का निर्देश देता है. इसी तरह, मेमोरी शेयर करने और/या उधार देने की सुविधाओं की मदद से, होस्ट को TrustZone के साथ कम्यूनिकेट करने की सुविधा मिलती है. इन सभी सुविधाओं को Arm (FF-A) स्पेसिफ़िकेशन के लिए फ़र्मवेयर फ़्रेमवर्क का इस्तेमाल करके, pKVM की मदद से बारीकी से मॉनिटर और कंट्रोल किया जाता है.
समय के साथ, pVM की मेमोरी की ज़रूरतें बदल सकती हैं. इसलिए, एक हाइपरकॉल दिया जाता है, जिसकी मदद से कॉलर के कुछ पेजों का मालिकाना हक, होस्ट को वापस दिया जा सकता है. आम तौर पर, इस हाइपरकॉल का इस्तेमाल, virtio balloon प्रोटोकॉल के साथ किया जाता है, ताकि VMM, pVM से मेमोरी वापस पाने का अनुरोध कर सके. साथ ही, pVM, कंट्रोल किए गए तरीके से VMM को छोड़े गए पेजों की सूचना दे सके.
सिस्टम में मौजूद सभी मेमोरी पेजों के मालिकाना हक को ट्रैक करने की ज़िम्मेदारी, हाइपरवाइजर की होती है. साथ ही, यह भी ट्रैक किया जाता है कि उन्हें अन्य इकाइयों के साथ शेयर किया जा रहा है या उन्हें उधार दिया जा रहा है. ज़्यादातर मामलों में, होस्ट और मेहमानों के दूसरे चरण की पेज टेबल में जोड़े गए मेटाडेटा का इस्तेमाल करके, इस स्थिति को ट्रैक किया जाता है. इसके लिए, पेज टेबल एंट्री (पीटीई) में रिज़र्व किए गए बिट का इस्तेमाल किया जाता है. जैसा कि नाम से पता चलता है, ये बिट सॉफ़्टवेयर के इस्तेमाल के लिए रिज़र्व किए जाते हैं.
होस्ट को यह पक्का करना होगा कि वह उन पेजों को ऐक्सेस न करे जिन्हें हाइपरवाइजर ने ऐक्सेस करने से रोका है. होस्ट का गैर-कानूनी ऐक्सेस होने पर, हाइपरवाइजर, होस्ट में सिंक्रोनस अपवाद इंजेक्ट करता है. इसकी वजह से, उपयोगकर्ता स्पेस के टास्क को SEGV सिग्नल मिल सकता है या होस्ट कर्नेल क्रैश हो सकता है. मेहमानों को दिए गए पेजों को, होस्ट कर्नेल के ज़रिए स्वैप या मर्ज करने की अनुमति नहीं दी जाती, ताकि वे अनजाने में ऐक्सेस न हो पाएं.
रुकावट को मैनेज करना और टाइमर
इंटरप्ट, किसी मेहमान के डिवाइसों के साथ इंटरैक्ट करने के तरीके और सीपीयू के बीच कम्यूनिकेशन के लिए ज़रूरी हैं. इंटरप्ट, इंटरप्रोसेसर इंटरप्ट (आईपीआई) के ज़रिए कम्यूनिकेट करते हैं. KVM मॉडल, EL1 में होस्ट को सभी वर्चुअल इंटरप्ट मैनेजमेंट का अधिकार देता है. इस काम के लिए, होस्ट, हाइपरवाइजर के ऐसे हिस्से की तरह काम करता है जिस पर भरोसा नहीं किया जा सकता.
pKVM, मौजूदा KVM कोड के आधार पर, जनरल इंटरप्ट कंट्रोलर वर्शन 3 (GICv3) का पूरा इम्यूलेशन उपलब्ध कराता है. टाइमर और आईपीआई को, इस गैर-भरोसेमंद इम्यूलेशन कोड के हिस्से के तौर पर मैनेज किया जाता है.
GICv3 के साथ काम करना
EL1 और EL2 के बीच के इंटरफ़ेस को यह पक्का करना होगा कि पूरी इंटरप्ट स्थिति, EL1 होस्ट को दिखे. इसमें इंटरप्ट से जुड़े हाइपरवाइजर रजिस्टर की कॉपी भी शामिल हैं. आम तौर पर, शेयर की गई स्मृति वाले क्षेत्रों का इस्तेमाल करके, यह जानकारी देखी जा सकती है. हर वर्चुअल सीपीयू (vCPU) के लिए एक क्षेत्र होता है.
सिस्टम रजिस्टर के रनटाइम के लिए सहायता कोड को आसान बनाया जा सकता है, ताकि सिर्फ़ सॉफ़्टवेयर जनरेट किए गए इंटरप्ट रजिस्टर (एसजीआईआर) और इंटरप्ट रजिस्टर (डीआईआर) को ट्रैप किया जा सके. आर्किटेक्चर के मुताबिक, ये रजिस्टर हमेशा EL2 पर ट्रैप होते हैं. वहीं, अब तक अन्य ट्रैप का इस्तेमाल सिर्फ़ गड़बड़ियों को कम करने के लिए किया गया है. बाकी सभी काम, हार्डवेयर में किए जा रहे हैं.
एमएमआईओ के मामले में, सब कुछ EL1 पर एमुलेट किया जाता है. इसके लिए, KVM में मौजूद सभी मौजूदा इन्फ़्रास्ट्रक्चर का फिर से इस्तेमाल किया जाता है. आखिर में, इंटरप्ट के लिए इंतज़ार करें (WFI) को हमेशा EL1 पर भेजा जाता है, क्योंकि यह शेड्यूलिंग के बुनियादी प्राइमिटिव में से एक है जिसका इस्तेमाल KVM करता है.
टाइमर से जुड़ी सहायता
वर्चुअल टाइमर के लिए तुलना करने वाली वैल्यू, हर ट्रैपिंग WFI पर EL1 को दिखानी चाहिए, ताकि vCPU ब्लॉक होने के दौरान EL1, टाइमर इंटरप्ट इंजेक्ट कर सके. फ़िज़िकल टाइमर को पूरी तरह से एमुलेट किया जाता है और सभी ट्रैप, EL1 पर भेजे जाते हैं.
MMIO हैंडलिंग
वर्चुअल मशीन मॉनिटर (VMM) के साथ कम्यूनिकेट करने और GIC इम्यूलेशन करने के लिए, MMIO ट्रैप को EL1 में होस्ट पर वापस भेजा जाना चाहिए, ताकि आगे की जांच की जा सके. pKVM के लिए, इन चीज़ों की ज़रूरत होती है:
- आईपीए और ऐक्सेस का साइज़
- डेटा को लिखने के मामले में
- ट्रैपिंग के समय सीपीयू का Endianness
इसके अलावा, सोर्स/डेस्टिनेशन के तौर पर सामान्य मकसद वाले रजिस्टर (GPR) वाले ट्रैप, एब्स्ट्रैक्ट ट्रांसफ़र स्यूडो-रजिस्टर का इस्तेमाल करके रिले किए जाते हैं.
मेहमान के लिए इंटरफ़ेस
कोई मेहमान, हाइपरकॉल और ट्रैप किए गए क्षेत्रों के लिए मेमोरी ऐक्सेस का इस्तेमाल करके, सुरक्षित मेहमान से बातचीत कर सकता है. हाइपरकॉल, एसएमसीसी स्टैंडर्ड के मुताबिक दिखाए जाते हैं. साथ ही, KVM की मदद से वेंडर को तय सीमा में एलोकेशन दिया जाता है. नीचे दिए गए हाइपरकॉल, pKVM के मेहमानों के लिए खास तौर पर अहम हैं.
सामान्य हाइपरकॉल
- PSCI, मेहमान को अपने vCPU के लाइफ़साइकल को कंट्रोल करने के लिए एक स्टैंडर्ड तरीका उपलब्ध कराता है. इसमें, ऑनलाइन करने, ऑफ़लाइन करने, और सिस्टम बंद करने की सुविधा शामिल है.
- टीआरएनजी, मेहमान के लिए एक स्टैंडर्ड तरीका उपलब्ध कराता है, ताकि वह pKVM से एन्ट्रापी का अनुरोध कर सके. यह अनुरोध, EL3 को भेजा जाता है. यह तरीका खास तौर पर तब मददगार होता है, जब होस्ट पर हार्डवेयर रैंडम नंबर जनरेटर (आरएनजी) को वर्चुअलाइज़ करने का भरोसा नहीं किया जा सकता.
pKVM हाइपरकॉल
- होस्ट के साथ यादें शेयर करना. शुरुआत में, होस्ट के पास मेहमान की सभी मेमोरी का ऐक्सेस नहीं होता. हालांकि, शेयर की गई मेमोरी के ज़रिए कम्यूनिकेट करने के लिए और शेयर किए गए बफ़र पर काम करने वाले पैरावर्चुअलाइज़ किए गए डिवाइसों के लिए, होस्ट का ऐक्सेस ज़रूरी होता है. होस्ट के साथ पेजों को शेयर करने और अनशेयर करने के लिए, हाइपरकॉल की मदद से मेहमान यह तय कर सकते हैं कि Android के बाकी हिस्सों को मेमोरी के किन हिस्सों को ऐक्सेस करने की अनुमति दी जाए. इसके लिए, उन्हें हैंडशेक की ज़रूरत नहीं पड़ती.
- होस्ट को मेमोरी वापस देना. आम तौर पर, मेहमान की सभी यादें तब तक उसके पास रहती हैं, जब तक उन्हें मिटा नहीं दिया जाता. यह स्थिति, लंबे समय तक चलने वाले ऐसे वर्चुअल मशीन के लिए सही नहीं हो सकती जिनकी मेमोरी की ज़रूरत समय के साथ बदलती रहती है.
relinquish
हाइपरकॉल की मदद से, मेहमान पेजों का मालिकाना हक, होस्ट को वापस ट्रांसफ़र कर सकता है. इसके लिए, मेहमान को हटाने की ज़रूरत नहीं होती. - होस्ट के लिए मेमोरी ऐक्सेस ट्रैपिंग. आम तौर पर, अगर कोई KVM मेहमान किसी ऐसे पते को ऐक्सेस करता है जो मान्य मेमोरी क्षेत्र से मेल नहीं खाता है, तो vCPU थ्रेड होस्ट पर निकल जाता है. आम तौर पर, ऐक्सेस का इस्तेमाल एमएमआईओ के लिए किया जाता है और यूज़र स्पेस में वीएमएम (वर्चुअल मशीन मॉनिटर) इसे एमुलेट करता है. इस गड़बड़ी को ठीक करने के लिए, pKVM को गड़बड़ी वाले निर्देश के बारे में जानकारी देनी होती है. जैसे, उसका पता, रजिस्टर पैरामीटर, और होस्ट को वापस भेजा जाने वाला संभावित कॉन्टेंट. अगर ट्रैप की उम्मीद नहीं थी, तो हो सकता है कि सुरक्षित मेहमान का संवेदनशील डेटा अनजाने में ज़ाहिर हो जाए. pKVM इस समस्या को हल करता है. इसके लिए, वह गड़बड़ियों को गंभीर मानता है. हालांकि, ऐसा तब तक नहीं होता, जब तक मेहमान ने पहले से ही गड़बड़ी वाली आईपीए रेंज की पहचान करने के लिए, हाइपरकॉल जारी न किया हो. ऐसा करने पर, होस्ट को ट्रैप करने के लिए ऐक्सेस की अनुमति दी जाती है. इस समाधान को एमएमआईओ गार्ड कहा जाता है.
वर्चुअल I/O डिवाइस (virtio)
Virtio, पैरावर्चुअलाइज़ किए गए डिवाइसों को लागू करने और उनसे इंटरैक्ट करने के लिए, एक लोकप्रिय, पोर्टेबल, और बेहतर स्टैंडर्ड है. सुरक्षित मेहमानों को दिखाए जाने वाले ज़्यादातर डिवाइसों को virtio का इस्तेमाल करके लागू किया जाता है. Virtio, vsock के लागू होने की सुविधा भी देता है. इसका इस्तेमाल, सुरक्षित मेहमान और Android के बाकी हिस्सों के बीच कम्यूनिकेशन के लिए किया जाता है.
आम तौर पर, वर्चुअल मशीन मॉनिटर (VMM) की मदद से, वर्चुअल डिवाइसों को होस्ट के यूज़र स्पेस में लागू किया जाता है. यह मेहमान से वर्चुअल डिवाइस के एमएमआईओ इंटरफ़ेस में ट्रैप की गई मेमोरी ऐक्सेस को इंटरसेप्ट करता है और उम्मीद के मुताबिक व्यवहार को एमुलेट करता है. एमएमआईओ ऐक्सेस करना अपेक्षाकृत महंगा होता है, क्योंकि डिवाइस को हर बार ऐक्सेस करने के लिए, वीएमएम में राउंड ट्रिप की ज़रूरत होती है. इसलिए, डिवाइस और मेहमान के बीच ज़्यादातर डेटा ट्रांसफ़र, मेमोरी में वर्चुअल कतार के सेट का इस्तेमाल करके होता है. वर्टिओ की एक अहम शर्त यह है कि होस्ट, मेहमान की मेमोरी को मनमुताबिक ऐक्सेस कर सकता है. यह अनुमान, वर्चुअल कतार के डिज़ाइन में साफ़ तौर पर दिखता है. इसमें मेहमान में मौजूद बफ़र के पॉइंटर हो सकते हैं, जिन्हें डिवाइस इम्यूलेशन सीधे तौर पर ऐक्सेस करना चाहता है.
हालांकि, पहले बताई गई मेमोरी शेयर करने की सुविधा वाले हाइपरकॉल का इस्तेमाल, वर्चुअल मशीन के डेटा बफ़र को मेन मेमोरी से होस्ट मेमोरी में शेयर करने के लिए किया जा सकता है. हालांकि, यह शेयरिंग ज़रूरी तौर पर पेज के हिसाब से की जाती है. अगर बफ़र का साइज़ पेज के साइज़ से कम है, तो ज़रूरत से ज़्यादा डेटा शेयर हो सकता है. इसके बजाय, मेहमान को शेयर की गई मेमोरी की तय विंडो से, वर्चुअल कतारें और उनसे जुड़े डेटा बफ़र, दोनों को आवंटित करने के लिए कॉन्फ़िगर किया जाता है. साथ ही, ज़रूरत के हिसाब से डेटा को विंडो में और उससे कॉपी (बाउंस) किया जाता है.
TrustZone के साथ इंटरैक्शन
मेहमान, TrustZone के साथ सीधे तौर पर इंटरैक्ट नहीं कर सकते. हालांकि, होस्ट के पास अब भी सुरक्षित वर्ल्ड में एसएमसी कॉल जारी करने का विकल्प होगा. इन कॉल से, फ़िज़िकल ऐड्रेस वाले ऐसे मेमोरी बफ़र के बारे में पता चल सकता है जिन्हें होस्ट ऐक्सेस नहीं कर सकता. आम तौर पर, सुरक्षित सॉफ़्टवेयर को बफ़र के ऐक्सेस की जानकारी नहीं होती. इसलिए, कोई नुकसान पहुंचाने वाला होस्ट, इस बफ़र का इस्तेमाल करके, 'कनफ़्यूज़्ड डिप्टी अटैक' (डीएमए अटैक जैसा) कर सकता है. ऐसे हमलों से बचने के लिए, pKVM, EL2 पर होस्ट के सभी SMC कॉल को ट्रैप करता है. साथ ही, EL3 पर होस्ट और सुरक्षित मॉनिटर के बीच प्रॉक्सी के तौर पर काम करता है.
होस्ट से किए गए PSCI कॉल, कम से कम बदलावों के साथ EL3 फ़र्मवेयर पर फ़ॉरवर्ड किए जाते हैं. खास तौर पर, ऑनलाइन आने वाले या निलंबित होने के बाद फिर से शुरू होने वाले सीपीयू के एंट्री पॉइंट को फिर से लिखा जाता है, ताकि EL1 पर होस्ट पर वापस जाने से पहले, चरण 2 की पेज टेबल को EL2 पर इंस्टॉल किया जा सके. बूट के दौरान, pKVM इस सुरक्षा को लागू करता है.
यह आर्किटेक्चर, PSCI के साथ काम करने वाले SoC पर निर्भर करता है. इसके लिए, TF-A के अप-टू-डेट वर्शन को EL3 फ़र्मवेयर के तौर पर इस्तेमाल करना बेहतर होता है.
Arm के लिए फ़र्मवेयर फ़्रेमवर्क (FF-A), सामान्य और सुरक्षित वर्ल्ड के बीच इंटरैक्शन को स्टैंडर्ड बनाता है. ऐसा खास तौर पर, सुरक्षित हाइपरवाइजर की मौजूदगी में किया जाता है. स्पेसिफ़िकेशन के एक बड़े हिस्से में, सुरक्षित वर्ल्ड के साथ मेमोरी शेयर करने के लिए एक तरीके के बारे में बताया गया है. इसमें, आम मैसेज फ़ॉर्मैट और उस पेज के लिए अनुमतियों के मॉडल, दोनों का इस्तेमाल किया जाता है. pKVM, FF-A मैसेज को प्रॉक्सी करता है, ताकि यह पक्का किया जा सके कि होस्ट, सुरक्षित वर्ल्ड के साथ ऐसी मेमोरी शेयर न कर रहा हो जिसके लिए उसके पास ज़रूरी अनुमतियां न हों.
यह आर्किटेक्चर, सेफ़ वर्ल्ड सॉफ़्टवेयर पर निर्भर करता है, जो मेमोरी ऐक्सेस मॉडल को लागू करता है. इससे यह पक्का होता है कि सेफ़ वर्ल्ड में चल रहे भरोसेमंद ऐप्लिकेशन और अन्य सॉफ़्टवेयर, मेमोरी को सिर्फ़ तब ऐक्सेस कर सकते हैं, जब उसका मालिकाना हक सेफ़ वर्ल्ड के पास हो या उसे FF-A का इस्तेमाल करके, साफ़ तौर पर उसके साथ शेयर किया गया हो. S-EL2 वाले सिस्टम पर, मेमोरी ऐक्सेस मॉडल को लागू करने का काम, सुरक्षित पार्टिशन मैनेजर कोर (एसपीएमसी) को करना चाहिए. जैसे, Hafnium, जो सुरक्षित वर्ल्ड के लिए, स्टेज 2 पेज टेबल को मैनेज करता है. S-EL2 के बिना किसी सिस्टम पर, TEE अपने पहले चरण की पेज टेबल के ज़रिए, मेमोरी ऐक्सेस मॉडल लागू कर सकता है.
अगर EL2 पर एसएमसी कॉल, PSCI कॉल या FF-A से तय किया गया मैसेज नहीं है, तो बिना मैनेज किए गए एसएमसी, EL3 पर फ़ॉरवर्ड कर दिए जाते हैं. यह माना जाता है कि भरोसेमंद और सुरक्षित फ़र्मवेयर, मैनेज नहीं किए गए एसएमसी को सुरक्षित तरीके से मैनेज कर सकता है. ऐसा इसलिए, क्योंकि फ़र्मवेयर को pVM आइसोलेशन को बनाए रखने के लिए ज़रूरी सावधानियों के बारे में पता होता है.
वर्चुअल मशीन मॉनिटर
crosvm एक वर्चुअल मशीन मॉनिटर (VMM) है, जो Linux के KVM इंटरफ़ेस की मदद से वर्चुअल मशीन चलाता है. crosvm को खास बनाने वाली बात यह है कि यह Rust प्रोग्रामिंग भाषा का इस्तेमाल करके, सुरक्षा पर फ़ोकस करता है. साथ ही, होस्ट कर्नेल को सुरक्षित रखने के लिए, वर्चुअल डिवाइसों के आस-पास सैंडबॉक्स बनाता है. crosvm के बारे में ज़्यादा जानने के लिए, यहां इसका आधिकारिक दस्तावेज़ देखें.
फ़ाइल डिस्क्रिप्टर और ioctls
KVM, ioctls की मदद से /dev/kvm
वर्ण डिवाइस को यूज़रस्पेस में दिखाता है. ये ioctls, KVM API का हिस्सा हैं. ioctls इन कैटगरी में आते हैं:
- सिस्टम ioctls, ग्लोबल एट्रिब्यूट के बारे में क्वेरी करते हैं और उन्हें सेट करते हैं. इन एट्रिब्यूट का असर पूरे KVM सबसिस्टम पर पड़ता है. साथ ही, इनसे pVM बनाए जाते हैं.
- VM ioctls, वर्चुअल सीपीयू (vCPUs) और डिवाइस बनाने वाले एट्रिब्यूट की क्वेरी करते हैं और उन्हें सेट करते हैं. साथ ही, वे पूरे pVM पर असर डालते हैं. जैसे, मेमोरी लेआउट और वर्चुअल सीपीयू (vCPUs) और डिवाइसों की संख्या.
- vCPU ioctls, एक वर्चुअल सीपीयू के काम को कंट्रोल करने वाले एट्रिब्यूट की क्वेरी करते हैं और उन्हें सेट करते हैं.
- डिवाइस ioctls, एक वर्चुअल डिवाइस के काम को कंट्रोल करने वाले एट्रिब्यूट की क्वेरी करते हैं और उन्हें सेट करते हैं.
हर crosvm प्रोसेस, वर्चुअल मशीन का सिर्फ़ एक इंस्टेंस चलाती है. यह प्रोसेस, KVM_CREATE_VM
सिस्टम ioctl का इस्तेमाल करके एक VM फ़ाइल डिस्क्रिप्टर बनाती है. इसका इस्तेमाल, pVM ioctls जारी करने के लिए किया जा सकता है. किसी VM FD पर KVM_CREATE_VCPU
या KVM_CREATE_DEVICE
ioctl, एक vCPU/डिवाइस बनाता है और नए संसाधन की जानकारी देने वाला फ़ाइल डिस्क्रिप्टर दिखाता है. vCPU या डिवाइस FD पर ioctl का इस्तेमाल, उस डिवाइस को कंट्रोल करने के लिए किया जा सकता है जिसे VM FD पर ioctl का इस्तेमाल करके बनाया गया था. vCPU के लिए, इसमें मेहमान कोड को चलाने का अहम काम शामिल है.
अंदरूनी तौर पर, crosvm, एज-ट्रिगर किए गए epoll
इंटरफ़ेस का इस्तेमाल करके, वीएम के फ़ाइल डिस्क्रिप्टर को कर्नेल के साथ रजिस्टर करता है. इसके बाद, जब भी किसी फ़ाइल डिस्क्रिप्टर में कोई नया इवेंट बाकी होता है, तो कर्नेल, crosvm को इसकी सूचना देता है.
pKVM में एक नई सुविधा, KVM_CAP_ARM_PROTECTED_VM
जोड़ी गई है. इसका इस्तेमाल, pVM एनवायरमेंट के बारे में जानकारी पाने और किसी वर्चुअल मशीन के लिए सुरक्षित मोड सेट अप करने के लिए किया जा सकता है. अगर --protected-vm
फ़्लैग पास किया जाता है, तो crosvm इसका इस्तेमाल pVM बनाने के दौरान करता है. इससे, pVM फ़र्मवेयर के लिए ज़रूरत के मुताबिक मेमोरी को क्वेरी करने और रिज़र्व करने के साथ-साथ, सुरक्षित मोड को चालू करने में मदद मिलती है.
मेमोरी का बंटवारा
किसी VM का मेमोरी लेआउट मैनेज करना और उसे मेमोरी देना, VM मॉनेजर की मुख्य ज़िम्मेदारियों में से एक है. crosvm, एक तय मेमोरी लेआउट जनरेट करता है. इस लेआउट के बारे में नीचे दी गई टेबल में बताया गया है.
सामान्य मोड में FDT | PHYS_MEMORY_END - 0x200000
|
खाली जगह | ...
|
रैमडिस्क | ALIGN_UP(KERNEL_END, 0x1000000)
|
कर्नेल | 0x80080000
|
बूटलोडर | 0x80200000
|
BIOS मोड में FDT | 0x80000000
|
फ़िज़िकल मेमोरी का आधार | 0x80000000
|
pVM फ़र्मवेयर | 0x7FE00000
|
डिवाइस का स्टोरेज | 0x10000 - 0x40000000
|
mmap
की मदद से फ़िज़िकल मेमोरी को एलोकेट किया जाता है. साथ ही, KVM_SET_USER_MEMORY_REGION
ioctl की मदद से, memslots नाम के मेमोरी क्षेत्रों को पॉप्युलेट करने के लिए, मेमोरी को VM को दान किया जाता है. इसलिए, मेहमान के सभी pVM मेमोरी को मैनेज करने वाले crosvm इंस्टेंस को एट्रिब्यूट किया जाता है. अगर होस्ट में खाली मेमोरी कम होने लगती है, तो प्रोसेस को बंद किया जा सकता है (वीएम को बंद किया जा सकता है). जब कोई वीएम बंद किया जाता है, तो हाइपरवाइजर, मेमोरी को अपने-आप मिटा देता है और उसे होस्ट कर्नेल में वापस कर देता है.
सामान्य KVM में, VMM के पास सभी मेहमान मेमोरी का ऐक्सेस होता है. pKVM की मदद से, मेहमान को मेमोरी देने पर, उसे होस्ट के फ़िज़िकल पते के स्पेस से अनमैप कर दिया जाता है. हालांकि, मेहमान के साफ़ तौर पर शेयर किए गए मेमोरी के लिए ऐसा नहीं होता. जैसे, virtio डिवाइसों के लिए.
मेहमान के पते के स्पेस में, एमएमआईओ क्षेत्रों को मैप नहीं किया जाता. मेहमान के इन क्षेत्रों को ऐक्सेस करने पर, उसे ट्रैप कर दिया जाता है. इस वजह से, VM FD पर I/O इवेंट होता है. इस तरीके का इस्तेमाल, वर्चुअल डिवाइसों को लागू करने के लिए किया जाता है. सुरक्षित मोड में, मेहमान को यह स्वीकार करना होगा कि उसके पते के स्पेस के किसी क्षेत्र का इस्तेमाल, एमएमआईओ के लिए किया जा रहा है. ऐसा, जानकारी के अनजाने में लीक होने के जोखिम को कम करने के लिए, हाइपरकॉल का इस्तेमाल करके किया जाता है.
समय-निर्धारण
हर वर्चुअल सीपीयू को POSIX थ्रेड से दिखाया जाता है और होस्ट के Linux शेड्यूलर से शेड्यूल किया जाता है. थ्रेड, vCPU FD पर KVM_RUN
ioctl को कॉल करता है. इससे, हाइपरवाइजर, मेहमान vCPU कॉन्टेक्स्ट पर स्विच कर जाता है. होस्ट शेड्यूलर, मेहमान के कॉन्टेक्स्ट में बिताए गए समय को, उससे जुड़े vCPU थ्रेड के इस्तेमाल किए गए समय के तौर पर गिना जाता है. KVM_RUN
तब दिखता है, जब कोई ऐसा इवेंट होता है जिसे VMM को मैनेज करना होता है. जैसे, I/O, इंटरप्ट का खत्म होना या vCPU का रुक जाना. VMM, इवेंट को मैनेज करता है और KVM_RUN
को फिर से कॉल करता है.
KVM_RUN
के दौरान, होस्ट शेड्यूलर के पास थ्रेड को रोकने का विकल्प होता है. हालांकि, EL2 हाइपरवाइजर कोड को चलाने के दौरान, थ्रेड को रोका नहीं जा सकता. मेहमान के pVM में, इस व्यवहार को कंट्रोल करने का कोई तरीका नहीं होता.
सभी vCPU थ्रेड, यूज़रस्पेस के किसी भी अन्य टास्क की तरह शेड्यूल किए जाते हैं. इसलिए, वे सभी स्टैंडर्ड QoS (क्वालिटी ऑफ़ सर्विस) प्रोसेस के दायरे में आते हैं. खास तौर पर, हर वर्चुअल सीपीयू थ्रेड को फ़िज़िकल सीपीयू के हिसाब से बेहतर बनाया जा सकता है, cpuset में रखा जा सकता है, इस्तेमाल को कंट्रोल करने की सुविधा का इस्तेमाल करके उसे बेहतर या सीमित किया जा सकता है, उसकी प्राथमिकता/शेड्यूलिंग की नीति में बदलाव किया जा सकता है वगैरह.
वर्चुअल डिवाइस
crosvm कई डिवाइसों के साथ काम करता है. इनमें ये डिवाइस भी शामिल हैं:
- कॉम्पोज़िट डिस्क इमेज के लिए virtio-blk, सिर्फ़ पढ़ने के लिए या पढ़ने और लिखने के लिए
- होस्ट के साथ कम्यूनिकेट करने के लिए vhost-vsock
- virtio ट्रांसपोर्ट के तौर पर virtio-pci
- pl030 रीयल टाइम क्लॉक (आरटीसी)
- सीरियल कम्यूनिकेशन के लिए 16550a UART
pVM फ़र्मवेयर
pVM फ़र्मवेयर (pvmfw), pVM से चलाया जाने वाला पहला कोड होता है. यह किसी फ़िज़िकल डिवाइस के बूट ROM की तरह ही होता है. pvmfw का मुख्य मकसद, सुरक्षित बूट को बूटस्ट्रैप करना और pVM का यूनीक सीक्रेट निकालना होता है. pvmfw का इस्तेमाल, किसी खास ओएस के साथ ही किया जा सकता है, जैसे कि Microdroid. हालांकि, इसके लिए ज़रूरी है कि ओएस, crosvm के साथ काम करता हो और उस पर सही तरीके से हस्ताक्षर किए गए हों.
pvmfw बाइनरी, इसी नाम के फ़्लैश पार्टिशन में सेव होती है और इसे ओटीए का इस्तेमाल करके अपडेट किया जाता है.
डिवाइस का बूट होना
pKVM की सुविधा वाले डिवाइस को बूट करने के लिए, यह तरीका अपनाएं:
- Android Bootloader (ABL), pvmfw को अपने पार्टिशन से मेमोरी में लोड करता है और इमेज की पुष्टि करता है.
- एबीएल, डिवाइस आइडेंटिफ़ायर कॉम्पोज़िशन इंजन (डीआईसीई) के गुप्त पासकोड (कंपाउंड डिवाइस आइडेंटिफ़ायर (सीडीआई) और डीआईसीई सर्टिफ़िकेट चेन) को रूट ऑफ़ ट्रस्ट से पाता है.
- ABL, pvmfw के लिए ज़रूरी सीडीआई जनरेट करता है और उन्हें pvmfw बिनेरी में जोड़ता है.
- एबीएल, डीटी में
linux,pkvm-guest-firmware-memory
रिज़र्व्ड मेमोरी रीजन node जोड़ता है. इसमें, pvmfw बाइनरी की जगह और साइज़ के साथ-साथ, पिछले चरण में मिले गुप्त पासकोड की जानकारी होती है. - इसके बाद, ABL, Linux को कंट्रोल सौंप देता है और Linux, pKVM को शुरू करता है.
- pKVM, होस्ट के स्टेज 2 पेज टेबल से pvmfw मेमोरी क्षेत्र को अनमैप करता है और डिवाइस के अपटाइम के दौरान, उसे होस्ट (और मेहमानों) से सुरक्षित रखता है.
डिवाइस के बूट होने के बाद, Microdroid दस्तावेज़ के बूट क्रम सेक्शन में दिए गए चरणों के हिसाब से Microdroid को बूट किया जाता है.
pVM बूट
pVM बनाते समय, crosvm (या कोई अन्य VMM) को इतना बड़ा स्लॉट बनाना होगा कि उसमें हाइपरवाइजर, pvmfw इमेज डाल सके. VMM, उन रजिस्टर की सूची में भी सीमित है जिनकी शुरुआती वैल्यू सेट की जा सकती है (प्राइमरी vCPU के लिए x0-x14, सेकंडरी vCPU के लिए कोई नहीं). बाकी रजिस्टर रिज़र्व हैं और ये hypervisor-pvmfw ABI का हिस्सा हैं.
pVM के चालू होने पर, हाइपरवाइजर सबसे पहले प्राइमरी vCPU का कंट्रोल, pvmfw को सौंपता है. फ़र्मवेयर को उम्मीद है कि crosvm ने AVB से हस्ताक्षर किया गया कर्नेल लोड किया होगा, जो बूटलोडर या कोई अन्य इमेज हो सकती है. साथ ही, वह मेमोरी में जाने-पहचाने ऑफ़सेट पर, हस्ताक्षर नहीं किया गया एफ़डीटी भी लोड करेगा. pvmfw, AVB हस्ताक्षर की पुष्टि करता है. पुष्टि होने पर, वह मिले एफ़डीटी से भरोसेमंद डिवाइस ट्री जनरेट करता है, मेमोरी से उसके गोपनीय डेटा को मिटा देता है, और पेलोड के एंट्री पॉइंट पर भेज देता है. अगर पुष्टि करने का कोई चरण पूरा नहीं होता है, तो फ़र्मवेयर PSCI SYSTEM_RESET
हाइपरकॉल जारी करता है.
बूट के बीच, pVM इंस्टेंस की जानकारी को एक सेगमेंट (virtio-blk डिवाइस) में सेव किया जाता है और pvmfw के पासवर्ड से एन्क्रिप्ट किया जाता है. इससे यह पक्का किया जाता है कि रीबूट करने के बाद, पासवर्ड को सही इंस्टेंस के लिए प्रोवाइड किया जा रहा है.