معماری AVF

اندروید یک پیاده سازی مرجع از تمام اجزای مورد نیاز برای پیاده سازی چارچوب مجازی سازی اندروید ارائه می کند. در حال حاضر این پیاده سازی به ARM64 محدود شده است. این صفحه معماری چارچوب را توضیح می دهد.

پس زمینه

معماری Arm اجازه می‌دهد تا چهار سطح استثنایی، با سطح استثنا 0 (EL0) کمترین امتیاز و سطح استثنا 3 (EL3) بیشترین امتیاز را داشته باشد. بزرگترین بخش از پایگاه کد اندروید (همه اجزای فضای کاربری) در EL0 اجرا می شود. بقیه چیزی که معمولا "اندروید" نامیده می شود، هسته لینوکس است که در EL1 اجرا می شود.

لایه EL2 امکان معرفی یک هایپروایزر را فراهم می کند که با تضمین محرمانه بودن و یکپارچگی قوی، حافظه و دستگاه ها را در pVM های جداگانه در EL1/EL0 ایزوله می کند.

هایپروایزر

ماشین مجازی مبتنی بر هسته محافظت شده (pKVM) بر اساس هایپروایزر لینوکس KVM ساخته شده است، که با توانایی محدود کردن دسترسی به بارهای در حال اجرا در ماشین های مجازی مهمان با علامت "محافظت" در زمان ایجاد توسعه یافته است.

KVM/arm64 بسته به در دسترس بودن برخی ویژگی‌های CPU، یعنی Virtualization Host Extensions (VHE) (ARMv8.1 و جدیدتر) از حالت‌های اجرای مختلف پشتیبانی می‌کند. در یکی از این حالت‌ها، که معمولاً به عنوان حالت غیر VHE شناخته می‌شود، کد هایپروایزر هنگام بوت از تصویر هسته جدا می‌شود و در EL2 نصب می‌شود، در حالی که خود هسته در EL1 اجرا می‌شود. اگرچه بخشی از پایگاه کد لینوکس است، مؤلفه EL2 KVM یک مؤلفه کوچک است که وظیفه سوئیچ بین چندین EL1 را بر عهده دارد. جزء Hypervisor با لینوکس کامپایل شده است، اما در یک بخش حافظه اختصاصی جداگانه از تصویر vmlinux قرار دارد. pKVM از این طراحی با گسترش کد هایپروایزر با ویژگی‌های جدید استفاده می‌کند که به آن اجازه می‌دهد محدودیت‌هایی بر هسته میزبان اندروید و فضای کاربر اعمال کند و دسترسی میزبان به حافظه مهمان و هایپروایزر را محدود کند.

ماژول های فروشنده pKVM

یک ماژول فروشنده pKVM یک ماژول مخصوص سخت افزار است که دارای عملکردهای خاص دستگاه است، مانند درایورهای واحد مدیریت حافظه ورودی-خروجی (IOMMU). این ماژول‌ها به شما امکان می‌دهند ویژگی‌های امنیتی را که نیاز به دسترسی سطح استثنا 2 (EL2) به pKVM دارند، پورت کنید.

برای یادگیری نحوه پیاده سازی و بارگذاری یک ماژول فروشنده pKVM، به پیاده سازی ماژول فروشنده pKVM مراجعه کنید.

رویه بوت

شکل زیر فرآیند بوت pKVM را نشان می دهد:

روش بوت pKVM

شکل 1. روش بوت pKVM

  1. بوت لودر در EL2 وارد هسته عمومی می شود.
  2. هسته عمومی تشخیص می دهد که در EL2 در حال اجرا است و خود را به EL1 محروم می کند در حالی که pKVM و ماژول های آن همچنان در EL2 کار می کنند. علاوه بر این، ماژول‌های فروشنده pKVM در این زمان بارگیری می‌شوند.
  3. هسته عمومی به طور معمول بوت می شود و تمام درایورهای دستگاه لازم را تا رسیدن به فضای کاربر بارگیری می کند. در این مرحله، pKVM در جای خود قرار دارد و جداول صفحه مرحله 2 را مدیریت می کند.

رویه بوت به بوت لودر اعتماد می کند که یکپارچگی تصویر هسته را فقط در طول راه اندازی اولیه حفظ کند. هنگامی که هسته محروم می شود، دیگر توسط هایپروایزر قابل اعتماد تلقی نمی شود، که پس از آن مسئول محافظت از خود حتی اگر هسته در معرض خطر قرار گرفته باشد.

وجود هسته اندروید و هایپروایزر در یک تصویر باینری یک رابط ارتباطی بسیار محکم بین آنها ایجاد می کند. این اتصال محکم به روز رسانی اتمی دو جزء را تضمین می کند، که از نیاز به پایدار نگه داشتن رابط بین آنها جلوگیری می کند و انعطاف پذیری زیادی را بدون به خطر انداختن قابلیت نگهداری طولانی مدت ارائه می دهد. کوپلینگ محکم همچنین زمانی که هر دو مؤلفه می توانند بدون تأثیر بر تضمین های امنیتی ارائه شده توسط هایپروایزر با یکدیگر همکاری کنند، امکان بهینه سازی عملکرد را فراهم می کند.

علاوه بر این، پذیرش GKI در اکوسیستم اندروید به طور خودکار به هایپروایزر pKVM اجازه می دهد تا در دستگاه های اندرویدی به صورت باینری مشابه هسته مستقر شود.

حفاظت از دسترسی به حافظه CPU

معماری Arm یک واحد مدیریت حافظه (MMU) را به دو مرحله مستقل تقسیم می‌کند، که هر دو می‌توانند برای پیاده‌سازی ترجمه آدرس و کنترل دسترسی به بخش‌های مختلف حافظه استفاده شوند. مرحله 1 MMU توسط EL1 کنترل می شود و سطح اول ترجمه آدرس را امکان پذیر می کند. مرحله 1 MMU توسط لینوکس برای مدیریت فضای آدرس مجازی ارائه شده به هر فرآیند فضای کاربر و فضای آدرس مجازی خود استفاده می شود.

مرحله 2 MMU توسط EL2 کنترل می شود و استفاده از ترجمه آدرس دوم را در آدرس خروجی MMU مرحله 1 امکان پذیر می کند که منجر به یک آدرس فیزیکی (PA) می شود. ترجمه مرحله 2 می تواند توسط هایپروایزرها برای کنترل و ترجمه دسترسی های حافظه از تمام ماشین های مجازی مهمان استفاده شود. همانطور که در شکل 2 نشان داده شده است، زمانی که هر دو مرحله ترجمه فعال هستند، آدرس خروجی مرحله 1 یک آدرس فیزیکی متوسط ​​(IPA) نامیده می شود. توجه: آدرس مجازی (VA) به یک IPA و سپس به یک PA ترجمه می شود.

حفاظت از دسترسی به حافظه CPU

شکل 2. حفاظت از دسترسی به حافظه CPU

از لحاظ تاریخی، KVM با فعال کردن ترجمه مرحله 2 در حین اجرای مهمان ها و با غیرفعال شدن مرحله 2 هنگام اجرای هسته لینوکس میزبان اجرا می شود. این معماری به دسترسی‌های حافظه از MMU مرحله 1 میزبان اجازه می‌دهد تا از MMU مرحله 2 عبور کند، بنابراین دسترسی نامحدود از میزبان به صفحات حافظه مهمان را امکان‌پذیر می‌سازد. از طرف دیگر، pKVM حفاظت مرحله 2 را حتی در زمینه میزبان فعال می کند و هایپروایزر را به جای میزبان، مسئول محافظت از صفحات حافظه مهمان می کند.

KVM به طور کامل از ترجمه آدرس در مرحله 2 برای پیاده سازی نگاشت های پیچیده IPA/PA برای مهمانان استفاده می کند، که با وجود تکه تکه شدن فیزیکی، توهم حافظه پیوسته را برای مهمانان ایجاد می کند. با این حال، استفاده از مرحله 2 MMU برای میزبان فقط به کنترل دسترسی محدود شده است. مرحله میزبان 2 با هویت نگاشت شده است و اطمینان حاصل می کند که حافظه پیوسته در فضای میزبان IPA در فضای PA به هم پیوسته است. این معماری امکان استفاده از نگاشت‌های بزرگ را در جدول صفحه فراهم می‌کند و در نتیجه فشار بر بافر نگاهی ترجمه (TLB) را کاهش می‌دهد. از آنجا که نگاشت هویت می تواند توسط PA نمایه شود، مرحله میزبان 2 نیز برای ردیابی مالکیت صفحه به طور مستقیم در جدول صفحه استفاده می شود.

حفاظت از دسترسی مستقیم به حافظه (DMA).

همانطور که قبلاً توضیح داده شد، برداشتن نقشه صفحات مهمان از میزبان لینوکس در جداول صفحه CPU یک مرحله ضروری اما ناکافی برای محافظت از حافظه مهمان است. pKVM همچنین نیاز به محافظت در برابر دسترسی‌های حافظه ایجاد شده توسط دستگاه‌های دارای DMA تحت کنترل هسته میزبان و احتمال حمله DMA توسط یک میزبان مخرب دارد. برای جلوگیری از دسترسی چنین دستگاهی به حافظه مهمان، pKVM به سخت افزار واحد مدیریت حافظه ورودی-خروجی (IOMMU) برای هر دستگاه دارای DMA در سیستم، همانطور که در شکل 3 نشان داده شده است، نیاز دارد.

حفاظت از دسترسی به حافظه DMA

شکل 3. حفاظت از دسترسی به حافظه DMA

حداقل، سخت‌افزار IOMMU ابزاری را برای اعطا و لغو دسترسی خواندن/نوشتن برای دستگاه به حافظه فیزیکی با جزئیات صفحه فراهم می‌کند. با این حال، این سخت‌افزار IOMMU استفاده از دستگاه‌ها را در pVM محدود می‌کند، زیرا آن‌ها مرحله 2 نقشه‌برداری هویتی را فرض می‌کنند.

برای اطمینان از جداسازی بین ماشین های مجازی، تراکنش های حافظه ایجاد شده از طرف نهادهای مختلف باید توسط IOMMU قابل تشخیص باشند تا بتوان از مجموعه مناسب جداول صفحه برای ترجمه استفاده کرد.

علاوه بر این، کاهش مقدار کد اختصاصی SoC در EL2 یک استراتژی کلیدی برای کاهش پایه محاسباتی قابل اعتماد کلی (TCB) pKVM است و برخلاف گنجاندن درایورهای IOMMU در هایپروایزر است. برای کاهش این مشکل، میزبان در EL1 مسئول وظایف مدیریت کمکی IOMMU، مانند مدیریت توان، مقداردهی اولیه و در صورت لزوم، مدیریت وقفه است.

با این حال، قرار دادن میزبان در کنترل وضعیت دستگاه، الزامات بیشتری را در رابط برنامه‌نویسی سخت‌افزار IOMMU ایجاد می‌کند تا اطمینان حاصل شود که بررسی‌های مجوز را نمی‌توان با روش‌های دیگری دور زد، به عنوان مثال، پس از بازنشانی دستگاه.

یک IOMMU استاندارد و به خوبی پشتیبانی شده برای دستگاه‌های بازویی که هم جداسازی و هم انتساب مستقیم را ممکن می‌سازد، معماری واحد مدیریت حافظه سیستم بازو (SMMU) است. این معماری راه حل مرجع توصیه شده است.

مالکیت حافظه

در زمان راه‌اندازی، فرض می‌شود که تمام حافظه غیرهایپروایزر متعلق به میزبان است و به همین ترتیب توسط هایپروایزر ردیابی می‌شود. هنگامی که یک pVM ایجاد می‌شود، میزبان صفحات حافظه را اهدا می‌کند تا به آن اجازه راه‌اندازی داده شود و هایپروایزر مالکیت آن صفحات را از میزبان به pVM منتقل می‌کند. بنابراین، هایپروایزر محدودیت‌های کنترل دسترسی را در جدول صفحه مرحله 2 میزبان اعمال می‌کند تا از دسترسی مجدد به صفحات جلوگیری کند و محرمانه بودن را برای مهمان فراهم کند.

ارتباط بین میزبان و مهمانان با به اشتراک گذاری حافظه کنترل شده بین آنها امکان پذیر می شود. مهمانان مجاز هستند برخی از صفحات خود را با استفاده از فراخوانی با میزبان به اشتراک بگذارند، که به هایپروایزر دستور می دهد تا آن صفحات را در جدول صفحه میزبان مرحله 2 مجدداً نقشه برداری کند. به طور مشابه، ارتباط میزبان با TrustZone با عملیات اشتراک گذاری حافظه و/یا امانت امکان پذیر می شود، که همه آنها به دقت توسط pKVM با استفاده از مشخصات فریمور فریمورک برای بازو (FF-A) نظارت و کنترل می شوند.

از آنجایی که نیازهای حافظه یک pVM می تواند در طول زمان تغییر کند، یک فراخوانی ارائه می شود که اجازه می دهد مالکیت صفحات مشخص شده متعلق به تماس گیرنده به میزبان واگذار شود. در عمل از این فراخوانی با پروتکل بالون virtio استفاده می‌شود تا به VMM اجازه می‌دهد تا حافظه را از pVM بازگرداند و pVM به صورت کنترل‌شده، VMM را از صفحات رها شده مطلع کند.

هایپروایزر مسئول ردیابی مالکیت تمام صفحات حافظه در سیستم و اینکه آیا آنها به اشتراک گذاشته می شوند یا به نهادهای دیگر قرض داده می شوند، است. بیشتر این ردیابی وضعیت با استفاده از ابرداده های پیوست شده به جداول صفحه میزبان و مهمانان مرحله 2، با استفاده از بیت های رزرو شده در ورودی های جدول صفحه (PTEs) انجام می شود که همانطور که از نام آنها پیداست، برای استفاده نرم افزاری رزرو شده اند.

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

مدیریت وقفه و تایمر

وقفه ها بخش مهمی از نحوه تعامل مهمان با دستگاه ها و ارتباط بین CPU ها هستند، جایی که وقفه های بین پردازنده (IPI) مکانیسم اصلی ارتباط هستند. مدل KVM این است که تمام مدیریت وقفه مجازی را به میزبان در EL1 واگذار می کند، که برای این منظور به عنوان بخشی نامعتبر از هایپروایزر رفتار می کند.

pKVM یک شبیه سازی کامل Generic Interrupt Controller نسخه 3 (GICv3) را بر اساس کد KVM موجود ارائه می دهد. تایمر و IPIها به عنوان بخشی از این کد شبیه سازی نامعتبر مدیریت می شوند.

پشتیبانی از GICv3

رابط بین EL1 و EL2 باید اطمینان حاصل کند که حالت وقفه کامل برای میزبان EL1 قابل مشاهده است، از جمله کپی هایی از ثبات هایپروایزر مربوط به وقفه ها. این دید معمولاً با استفاده از مناطق حافظه مشترک، یکی در هر CPU مجازی (vCPU) انجام می شود.

کد پشتیبانی زمان اجرا رجیستر سیستم را می توان برای پشتیبانی از ثبت وقفه ایجاد شده نرم افزار (SGIR) و غیرفعال کردن ثبت وقفه (DIR) ساده سازی کرد. معماری الزام می کند که این ثبات ها همیشه در EL2 به دام بیفتند، در حالی که تله های دیگر تاکنون فقط برای کاهش خطا مفید بوده اند. همه چیز دیگر در سخت افزار انجام می شود.

در سمت MMIO، همه چیز در EL1 شبیه‌سازی می‌شود و از تمام زیرساخت‌های فعلی در KVM استفاده مجدد می‌شود. در نهایت، Wait for Interrupt (WFI) همیشه به EL1 رله می‌شود، زیرا این یکی از اصول اولیه زمان‌بندی است که KVM از آن استفاده می‌کند.

پشتیبانی از تایمر

مقدار مقایسه کننده برای تایمر مجازی باید در هر WFI به دام افتاده در معرض EL1 قرار گیرد تا EL1 بتواند وقفه های تایمر را در زمانی که vCPU مسدود است تزریق کند. تایمر فیزیکی کاملا شبیه سازی شده است و همه تله ها به EL1 رله می شوند.

هندلینگ MMIO

برای برقراری ارتباط با مانیتور ماشین مجازی (VMM) و انجام شبیه‌سازی GIC، تله‌های MMIO باید برای تریاژ بیشتر به میزبان در EL1 بازگردانده شوند. pKVM به موارد زیر نیاز دارد:

  • IPA و اندازه دسترسی
  • داده ها در صورت نوشتن
  • اندانی بودن CPU در نقطه به دام انداختن

علاوه بر این، تله‌هایی با یک ثبت نام عمومی (GPR) به عنوان منبع/مقصد با استفاده از یک شبه ثبت انتقال انتزاعی رله می‌شوند.

رابط های مهمان

یک مهمان می تواند با استفاده از ترکیبی از فراخوانی و دسترسی حافظه به مناطق به دام افتاده با مهمان محافظت شده ارتباط برقرار کند. فراخوانی بر اساس استاندارد SMCCC ، با یک محدوده برای تخصیص فروشنده توسط KVM در معرض نمایش قرار می گیرد. فراخوان های زیر برای مهمانان pKVM اهمیت ویژه ای دارند.

فراخوان های عمومی

  • PSCI مکانیزم استانداردی را برای مهمان فراهم می کند تا چرخه حیات vCPU های خود را از جمله آنلاین شدن، آفلاین کردن و خاموش شدن سیستم کنترل کند.
  • TRNG مکانیزم استانداردی را برای مهمان فراهم می کند تا از pKVM درخواست آنتروپی کند که تماس را به EL3 رله می کند. این مکانیسم به ویژه در مواردی مفید است که میزبان برای مجازی سازی یک مولد اعداد تصادفی سخت افزاری (RNG) قابل اعتماد نباشد.

فراخوانی pKVM

  • اشتراک گذاری حافظه با میزبان تمام حافظه مهمان در ابتدا برای میزبان غیرقابل دسترسی است، اما دسترسی میزبان برای ارتباط با حافظه مشترک و برای دستگاه‌های مجازی‌سازی شده که به بافرهای مشترک متکی هستند، ضروری است. فراخوانی برای اشتراک‌گذاری و لغو اشتراک‌گذاری صفحات با میزبان به مهمان این امکان را می‌دهد تا بدون نیاز به دست دادن دقیقاً تصمیم بگیرد که چه بخش‌هایی از حافظه برای بقیه اندروید قابل دسترسی است.
  • واگذاری حافظه به میزبان. تمام حافظه مهمان معمولاً تا زمانی که از بین نرود متعلق به مهمان است. این حالت می تواند برای ماشین های مجازی با عمر طولانی با نیازهای حافظه که در طول زمان متفاوت است، ناکافی باشد. relinquish فراخوانی به مهمان اجازه می دهد تا صریحاً مالکیت صفحات را بدون نیاز به خاتمه مهمان به میزبان بازگرداند.
  • به دام انداختن دسترسی به حافظه برای میزبان. به طور سنتی، اگر یک مهمان KVM به آدرسی دسترسی پیدا کند که با یک منطقه حافظه معتبر مطابقت ندارد، رشته vCPU به میزبان خارج می شود و دسترسی معمولاً برای MMIO استفاده می شود و توسط VMM در فضای کاربر شبیه سازی می شود. برای تسهیل این کار، pKVM باید جزئیات دستورالعمل خطا مانند آدرس، پارامترهای ثبت و احتمالاً محتویات آنها را به میزبان بازگرداند، که اگر تله پیش بینی نشده باشد، می تواند ناخواسته داده های حساس یک مهمان محافظت شده را در معرض دید قرار دهد. pKVM این مشکل را با تلقی کردن این خطاها به‌عنوان کشنده حل می‌کند، مگر اینکه مهمان قبلاً یک فراخوانی برای شناسایی محدوده IPA معیوب به عنوان محدوده‌ای که دسترسی‌ها مجاز است به میزبان بازگردانده شود، صادر کرده باشد. این راه حل به عنوان محافظ MMIO شناخته می شود.

دستگاه ورودی/خروجی مجازی (virtio)

Virtio یک استاندارد محبوب، قابل حمل و بالغ برای پیاده سازی و تعامل با دستگاه های paravirtualized است. اکثر دستگاه هایی که در معرض مهمانان محافظت شده قرار می گیرند با استفاده از virtio اجرا می شوند. Virtio همچنین زیربنای اجرای vsock است که برای ارتباط بین مهمان محافظت شده و بقیه اندروید استفاده می شود.

دستگاه‌های Virtio معمولاً در فضای کاربر میزبان توسط VMM پیاده‌سازی می‌شوند، که دسترسی‌های حافظه به دام افتاده را از مهمان به رابط MMIO دستگاه virtio رهگیری می‌کند و رفتار مورد انتظار را شبیه‌سازی می‌کند. دسترسی MMIO نسبتاً گران است زیرا هر دسترسی به دستگاه نیاز به یک رفت و برگشت به VMM و برگشت دارد، بنابراین بیشتر انتقال داده واقعی بین دستگاه و مهمان با استفاده از مجموعه‌ای از صفات در حافظه انجام می‌شود. یک فرض کلیدی virtio این است که میزبان می تواند خودسرانه به حافظه مهمان دسترسی داشته باشد. این فرض در طراحی virtqueue مشهود است، که ممکن است حاوی نشانگرهایی به بافرهایی در مهمان باشد که شبیه سازی دستگاه برای دسترسی مستقیم به آنها در نظر گرفته شده است.

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

دستگاه مجازی

شکل 4. دستگاه Virtio

تعامل با TrustZone

اگرچه مهمانان قادر به تعامل مستقیم با TrustZone نیستند، میزبان همچنان باید بتواند تماس های SMC را به دنیای امن صادر کند. این تماس‌ها می‌توانند بافرهای حافظه با آدرس فیزیکی را مشخص کنند که برای میزبان غیرقابل دسترسی هستند. از آنجایی که نرم افزار امن به طور کلی از دسترسی به بافر بی اطلاع است، یک میزبان مخرب می تواند از این بافر برای انجام یک حمله معاون اشتباه (مشابه حمله DMA) استفاده کند. برای جلوگیری از چنین حملاتی، pKVM تمام تماس های SMC میزبان را به EL2 به دام می اندازد و به عنوان یک پروکسی بین میزبان و مانیتور امن در EL3 عمل می کند.

تماس‌های PSCI از میزبان با حداقل تغییرات به سیستم عامل EL3 هدایت می‌شوند. به طور خاص، نقطه ورود برای یک CPU که آنلاین می شود یا از حالت تعلیق دوباره شروع می شود، بازنویسی می شود تا جدول صفحه مرحله 2 قبل از بازگشت به میزبان در EL1 در EL2 نصب شود. در هنگام بوت، این حفاظت توسط pKVM اعمال می شود.

این معماری مبتنی بر SoC است که از PSCI پشتیبانی می کند، ترجیحاً از طریق استفاده از نسخه به روز TF-A به عنوان سیستم عامل EL3 آن.

Firmware Framework for Arm (FF-A) تعاملات بین دنیای عادی و امن را استاندارد می کند، به ویژه در حضور یک هایپروایزر ایمن. بخش عمده ای از مشخصات مکانیزمی را برای به اشتراک گذاری حافظه با دنیای امن، با استفاده از یک قالب پیام مشترک و یک مدل مجوزهای تعریف شده برای صفحات زیرین، تعریف می کند. pKVM پیام های FF-A را پراکسی می کند تا اطمینان حاصل کند که میزبان سعی نمی کند حافظه را با سمت امنی که مجوزهای کافی برای آن ندارد به اشتراک بگذارد.

این معماری به نرم‌افزار دنیای امن که مدل دسترسی به حافظه را اعمال می‌کند، متکی است تا اطمینان حاصل کند که برنامه‌های مورد اعتماد و هر نرم‌افزار دیگری که در دنیای امن اجرا می‌شود، تنها در صورتی می‌توانند به حافظه دسترسی داشته باشند که منحصراً متعلق به دنیای امن باشد یا با استفاده از FF-A به‌صراحت با آن به اشتراک گذاشته شده باشد. در سیستمی با S-EL2، اجرای مدل دسترسی به حافظه باید توسط یک هسته مدیر پارتیشن امن (SPMC) مانند Hafnium انجام شود که جداول صفحه مرحله 2 را برای دنیای امن نگهداری می کند. در یک سیستم بدون S-EL2، TEE می تواند در عوض یک مدل دسترسی به حافظه را از طریق جداول صفحه مرحله 1 خود اعمال کند.

اگر تماس SMC به EL2 یک تماس PSCI یا پیام تعریف شده FF-A نباشد، SMC های کنترل نشده به EL3 هدایت می شوند. فرض بر این است که سفت‌افزار ایمن (الزاماً قابل اعتماد) می‌تواند SMC‌های کنترل‌نشده را با خیال راحت مدیریت کند، زیرا میان‌افزار اقدامات احتیاطی لازم برای حفظ ایزوله‌سازی pVM را درک می‌کند.

مانیتور ماشین مجازی

crosvm یک مانیتور ماشین مجازی (VMM) است که ماشین های مجازی را از طریق رابط KVM لینوکس اجرا می کند. چیزی که crosvm را منحصر به فرد می کند تمرکز آن بر ایمنی با استفاده از زبان برنامه نویسی Rust و یک جعبه شنی در اطراف دستگاه های مجازی برای محافظت از هسته میزبان است. برای اطلاعات بیشتر در مورد crosvm، اسناد رسمی آن را اینجا ببینید.

توصیفگرهای فایل و ioctls

KVM دستگاه کاراکتر /dev/kvm را با ioctlهایی که KVM API را تشکیل می‌دهند در معرض فضای کاربران قرار می‌دهد. ioctls به دسته های زیر تعلق دارند:

  • سیستم ioctls پرس و جو می کند و ویژگی های جهانی را تنظیم می کند که بر کل زیرسیستم KVM تأثیر می گذارد و pVM ها را ایجاد می کند.
  • VM ioctls پرس و جو می کند و ویژگی هایی را تنظیم می کند که CPU های مجازی (vCPU) و دستگاه ها را ایجاد می کنند و کل pVM را تحت تأثیر قرار می دهند، مانند طرح بندی حافظه و تعداد CPU های مجازی (vCPU) و دستگاه ها.
  • vCPU ioctls پرس و جو می کند و ویژگی هایی را تنظیم می کند که عملکرد یک CPU مجازی را کنترل می کند.
  • دستگاه ioctls پرس و جو می کند و ویژگی هایی را تنظیم می کند که عملکرد یک دستگاه مجازی را کنترل می کند.

هر فرآیند crosvm دقیقاً یک نمونه از یک ماشین مجازی را اجرا می کند. این فرآیند از سیستم KVM_CREATE_VM ioctl برای ایجاد یک توصیفگر فایل VM استفاده می کند که می تواند برای صدور ioctl pVM استفاده شود. یک KVM_CREATE_VCPU یا KVM_CREATE_DEVICE ioctl در VM FD یک vCPU/دستگاه ایجاد می‌کند و یک توصیفگر فایل را برمی‌گرداند که به منبع جدید اشاره می‌کند. ioctls در vCPU یا دستگاه FD را می توان برای کنترل دستگاهی که با استفاده از ioctl روی VM FD ایجاد شده است، استفاده کرد. برای vCPU ها، این شامل وظیفه مهم اجرای کد مهمان است.

در داخل، crosvm توصیفگرهای فایل VM را با هسته با استفاده از رابط epoll با edge-triggered ثبت می‌کند. سپس کرنل هر زمان که رویداد جدیدی در هر یک از توصیفگرهای پرونده وجود داشته باشد، به crosvm اطلاع می دهد.

pKVM یک قابلیت جدید به KVM_CAP_ARM_PROTECTED_VM اضافه می کند که می تواند برای دریافت اطلاعات در مورد محیط pVM و تنظیم حالت محافظت شده برای یک ماشین مجازی استفاده شود. اگر پرچم --protected-vm پاس داده شود، crosvm از این در هنگام ایجاد pVM استفاده می کند تا مقدار مناسب حافظه را برای سیستم عامل pVM پرس و جو کرده و ذخیره کند و سپس حالت محافظت شده را فعال کند.

تخصیص حافظه

یکی از وظایف اصلی VMM تخصیص حافظه VM و مدیریت چیدمان حافظه آن است. crosvm یک چیدمان حافظه ثابت ایجاد می کند که در جدول زیر توضیح داده شده است.

FDT در حالت عادی PHYS_MEMORY_END - 0x200000
فضای آزاد ...
رامدیسک ALIGN_UP(KERNEL_END, 0x1000000)
هسته 0x80080000
بوت لودر 0x80200000
FDT در حالت BIOS 0x80000000
پایه حافظه فیزیکی 0x80000000
سیستم عامل pVM 0x7FE00000
حافظه دستگاه 0x10000 - 0x40000000

حافظه فیزیکی با mmap تخصیص داده می شود و حافظه به VM اهدا می شود تا مناطق حافظه آن، به نام memslots ، با ioctl KVM_SET_USER_MEMORY_REGION پر شود. بنابراین، تمام حافظه pVM مهمان به نمونه crosvm نسبت داده می شود که آن را مدیریت می کند و می تواند منجر به از بین رفتن فرآیند (خاتمه VM) شود اگر میزبان شروع به اتمام حافظه آزاد کند. هنگامی که یک VM متوقف می شود، حافظه به طور خودکار توسط Hypervisor پاک می شود و به هسته میزبان باز می گردد.

تحت KVM معمولی، VMM دسترسی به تمام حافظه مهمان را حفظ می کند. با pKVM، زمانی که حافظه مهمان به مهمان اهدا می شود، از فضای آدرس فیزیکی میزبان خارج می شود. تنها استثنا حافظه ای است که به صراحت توسط مهمان به اشتراک گذاشته می شود، مانند دستگاه های virtio.

مناطق MMIO در فضای آدرس مهمان بدون نقشه باقی می مانند. دسترسی مهمان به این مناطق به دام افتاده و منجر به یک رویداد I/O در VM FD می شود. این مکانیزم برای پیاده سازی دستگاه های مجازی استفاده می شود. در حالت محافظت شده، مهمان باید تصدیق کند که ناحیه ای از فضای آدرسش برای MMIO با استفاده از فراخوانی استفاده می شود تا خطر نشت تصادفی اطلاعات کاهش یابد.

برنامه ریزی

هر CPU مجازی توسط یک رشته POSIX نمایش داده می شود و توسط زمانبندی لینوکس میزبان برنامه ریزی می شود. این رشته، ioctl KVM_RUN را در VCPU FD فراخوانی می‌کند، که در نتیجه Hypervisor به زمینه vCPU مهمان سوئیچ می‌کند. زمانبندی میزبان زمان صرف شده در زمینه مهمان را به عنوان زمان استفاده شده توسط رشته vCPU مربوطه محاسبه می کند. هنگامی که رویدادی وجود دارد که باید توسط VMM مدیریت شود، KVM_RUN برمی گردد، مانند I/O، پایان وقفه، یا vCPU متوقف شده است. VMM رویداد را مدیریت می کند و دوباره KVM_RUN فراخوانی می کند.

در طول KVM_RUN ، رشته توسط زمان‌بندی میزبان قابل پیش‌گیری باقی می‌ماند، به جز برای اجرای کد هایپروایزر EL2، که قابل پیش‌گیری نیست. خود pVM مهمان هیچ مکانیزمی برای کنترل این رفتار ندارد.

از آنجایی که تمام رشته‌های vCPU مانند سایر وظایف فضای کاربر برنامه‌ریزی شده‌اند، تابع تمام مکانیزم‌های QoS استاندارد هستند. به طور خاص، هر رشته vCPU را می توان به CPU های فیزیکی متصل کرد، در cpusets قرار داد، با استفاده از گیره استفاده تقویت یا درپوش گذاشت، سیاست اولویت/زمان بندی آنها تغییر کرد و موارد دیگر.

دستگاه های مجازی

crosvm تعدادی دستگاه از جمله موارد زیر را پشتیبانی می کند:

  • virtio-blk برای تصاویر دیسک کامپوزیت، فقط خواندنی یا خواندنی-نوشتنی
  • vhost-vsock برای ارتباط با میزبان
  • virtio-pci به عنوان انتقال virtio
  • ساعت واقعی pl030 (RTC)
  • 16550a UART برای ارتباط سریال

سیستم عامل pVM

سیستم عامل pVM (pvmfw) اولین کدی است که توسط یک pVM اجرا می شود، شبیه به رام بوت یک دستگاه فیزیکی. هدف اصلی pvmfw این است که بوت امن را بوت استرپ کند و راز منحصر به فرد pVM را استخراج کند. pvmfw به استفاده از سیستم عامل خاصی مانند Microdroid محدود نمی شود، تا زمانی که سیستم عامل توسط crosvm پشتیبانی می شود و به درستی امضا شده است.

باینری pvmfw در یک پارتیشن فلش به همین نام ذخیره می شود و با استفاده از OTA به روز می شود.

بوت دستگاه

دنباله مراحل زیر به رویه بوت دستگاه دارای pKVM اضافه می شود:

  1. Bootloader Android (ABL) pvmfw را از پارتیشن خود در حافظه بارگیری می کند و تصویر را تأیید می کند.
  2. ABL اسرار Device Identifier Composition Engine (DICE) خود (شناسه های دستگاه ترکیبی (CDI) و زنجیره گواهی DICE) را از یک Root of Trust بدست می آورد.
  3. ABL CDI های لازم برای pvmfw را استخراج می کند و آنها را به باینری pvmfw اضافه می کند.
  4. ABL یک گره حافظه ذخیره شده linux,pkvm-guest-firmware-memory به DT اضافه می کند که مکان و اندازه باینری pvmfw و رازهایی را که در مرحله قبل به دست آورده است را توصیف می کند.
  5. ABL کنترل را به لینوکس می دهد و لینوکس pKVM را مقداردهی اولیه می کند.
  6. pKVM منطقه حافظه pvmfw را از جداول صفحه مرحله 2 میزبان باز می کند و از آن در برابر میزبان (و مهمانان) در طول زمان کارکرد دستگاه محافظت می کند.

پس از بوت شدن دستگاه، Microdroid طبق مراحل در بخش Boot sequence سند Microdroid بوت می شود.

بوت pVM

هنگام ایجاد یک pVM، crosvm (یا VMM دیگر) باید یک مم اسلات به اندازه کافی بزرگ ایجاد کند تا توسط Hypervisor با تصویر pvmfw پر شود. VMM همچنین در لیست رجیسترهایی که می تواند مقدار اولیه آنها را تنظیم کند محدود شده است (x0-x14 برای vCPU اولیه، هیچ برای vCPU های ثانویه). رجیسترهای باقی مانده رزرو شده و بخشی از hypervisor-pvmfw ABI هستند.

هنگامی که pVM اجرا می شود، هایپروایزر ابتدا کنترل vCPU اولیه را به pvmfw می دهد. سفت‌افزار انتظار دارد که crosvm یک هسته با امضای AVB، که می‌تواند یک بوت‌لودر یا هر تصویر دیگری باشد، و یک FDT بدون علامت در حافظه با آفست‌های شناخته‌شده، بارگذاری کند. pvmfw امضای AVB را تأیید می کند و در صورت موفقیت، یک درخت دستگاه قابل اعتماد از FDT دریافتی ایجاد می کند، اسرار آن را از حافظه پاک می کند و به نقطه ورودی بار منشعب می شود. اگر یکی از مراحل تأیید ناموفق باشد، سیستم عامل یک فراخوانی PSCI SYSTEM_RESET صادر می کند.

بین راه‌اندازی‌ها، اطلاعات مربوط به نمونه pVM در یک پارتیشن (دستگاه virtio-blk) ذخیره می‌شود و با رمز pvmfw رمزگذاری می‌شود تا اطمینان حاصل شود که پس از راه‌اندازی مجدد، راز به نمونه صحیح ارائه می‌شود.

،

اندروید یک پیاده سازی مرجع از تمام اجزای مورد نیاز برای پیاده سازی چارچوب مجازی سازی اندروید ارائه می کند. در حال حاضر این پیاده سازی به ARM64 محدود شده است. این صفحه معماری چارچوب را توضیح می دهد.

پس زمینه

معماری Arm اجازه می‌دهد تا چهار سطح استثنایی، با سطح استثنا 0 (EL0) کمترین امتیاز و سطح استثنا 3 (EL3) بیشترین امتیاز را داشته باشد. بزرگترین بخش از پایگاه کد اندروید (همه اجزای فضای کاربری) در EL0 اجرا می شود. بقیه چیزی که معمولا "اندروید" نامیده می شود، هسته لینوکس است که در EL1 اجرا می شود.

لایه EL2 امکان معرفی یک هایپروایزر را فراهم می کند که با تضمین محرمانه بودن و یکپارچگی قوی، حافظه و دستگاه ها را در pVM های جداگانه در EL1/EL0 ایزوله می کند.

هایپروایزر

ماشین مجازی مبتنی بر هسته محافظت شده (pKVM) بر اساس هایپروایزر لینوکس KVM ساخته شده است، که با توانایی محدود کردن دسترسی به بارهای در حال اجرا در ماشین های مجازی مهمان با علامت "محافظت" در زمان ایجاد توسعه یافته است.

KVM/arm64 بسته به در دسترس بودن برخی ویژگی‌های CPU، یعنی Virtualization Host Extensions (VHE) (ARMv8.1 و جدیدتر) از حالت‌های اجرای مختلف پشتیبانی می‌کند. در یکی از این حالت‌ها، که معمولاً به عنوان حالت غیر VHE شناخته می‌شود، کد هایپروایزر هنگام بوت از تصویر هسته جدا می‌شود و در EL2 نصب می‌شود، در حالی که خود هسته در EL1 اجرا می‌شود. اگرچه بخشی از پایگاه کد لینوکس است، مؤلفه EL2 KVM یک مؤلفه کوچک است که وظیفه سوئیچ بین چندین EL1 را بر عهده دارد. جزء Hypervisor با لینوکس کامپایل شده است، اما در یک بخش حافظه اختصاصی جداگانه از تصویر vmlinux قرار دارد. pKVM از این طراحی با گسترش کد هایپروایزر با ویژگی‌های جدید استفاده می‌کند که به آن اجازه می‌دهد محدودیت‌هایی بر هسته میزبان اندروید و فضای کاربر اعمال کند و دسترسی میزبان به حافظه مهمان و هایپروایزر را محدود کند.

ماژول های فروشنده pKVM

یک ماژول فروشنده pKVM یک ماژول مخصوص سخت افزار است که دارای عملکردهای خاص دستگاه است، مانند درایورهای واحد مدیریت حافظه ورودی-خروجی (IOMMU). این ماژول‌ها به شما امکان می‌دهند ویژگی‌های امنیتی را که نیاز به دسترسی سطح استثنا 2 (EL2) به pKVM دارند، پورت کنید.

برای یادگیری نحوه پیاده سازی و بارگذاری یک ماژول فروشنده pKVM، به پیاده سازی ماژول فروشنده pKVM مراجعه کنید.

رویه بوت

شکل زیر فرآیند بوت pKVM را نشان می دهد:

روش بوت pKVM

شکل 1. روش بوت pKVM

  1. بوت لودر در EL2 وارد هسته عمومی می شود.
  2. هسته عمومی تشخیص می دهد که در EL2 در حال اجرا است و خود را به EL1 محروم می کند در حالی که pKVM و ماژول های آن همچنان در EL2 کار می کنند. علاوه بر این، ماژول‌های فروشنده pKVM در این زمان بارگیری می‌شوند.
  3. هسته عمومی به طور معمول بوت می شود و تمام درایورهای دستگاه لازم را تا رسیدن به فضای کاربر بارگیری می کند. در این مرحله، pKVM در جای خود قرار دارد و جداول صفحه مرحله 2 را مدیریت می کند.

رویه بوت به بوت لودر اعتماد می کند که یکپارچگی تصویر هسته را فقط در طول راه اندازی اولیه حفظ کند. هنگامی که هسته محروم می شود، دیگر توسط هایپروایزر قابل اعتماد تلقی نمی شود، که پس از آن مسئول محافظت از خود حتی اگر هسته در معرض خطر قرار گرفته باشد.

وجود هسته اندروید و هایپروایزر در یک تصویر باینری یک رابط ارتباطی بسیار محکم بین آنها ایجاد می کند. این اتصال محکم به روز رسانی اتمی دو جزء را تضمین می کند، که از نیاز به پایدار نگه داشتن رابط بین آنها جلوگیری می کند و انعطاف پذیری زیادی را بدون به خطر انداختن قابلیت نگهداری طولانی مدت ارائه می دهد. کوپلینگ محکم همچنین زمانی که هر دو مؤلفه می توانند بدون تأثیر بر تضمین های امنیتی ارائه شده توسط هایپروایزر با یکدیگر همکاری کنند، امکان بهینه سازی عملکرد را فراهم می کند.

علاوه بر این، پذیرش GKI در اکوسیستم اندروید به طور خودکار به هایپروایزر pKVM اجازه می دهد تا در دستگاه های اندرویدی به صورت باینری مشابه هسته مستقر شود.

حفاظت از دسترسی به حافظه CPU

معماری Arm یک واحد مدیریت حافظه (MMU) را به دو مرحله مستقل تقسیم می‌کند، که هر دو می‌توانند برای پیاده‌سازی ترجمه آدرس و کنترل دسترسی به بخش‌های مختلف حافظه استفاده شوند. مرحله 1 MMU توسط EL1 کنترل می شود و سطح اول ترجمه آدرس را امکان پذیر می کند. مرحله 1 MMU توسط لینوکس برای مدیریت فضای آدرس مجازی ارائه شده به هر فرآیند فضای کاربر و فضای آدرس مجازی خود استفاده می شود.

مرحله 2 MMU توسط EL2 کنترل می شود و استفاده از ترجمه آدرس دوم را در آدرس خروجی MMU مرحله 1 امکان پذیر می کند که منجر به یک آدرس فیزیکی (PA) می شود. ترجمه مرحله 2 می تواند توسط هایپروایزرها برای کنترل و ترجمه دسترسی های حافظه از تمام ماشین های مجازی مهمان استفاده شود. همانطور که در شکل 2 نشان داده شده است، زمانی که هر دو مرحله ترجمه فعال هستند، آدرس خروجی مرحله 1 یک آدرس فیزیکی متوسط ​​(IPA) نامیده می شود. توجه: آدرس مجازی (VA) به یک IPA و سپس به یک PA ترجمه می شود.

حفاظت از دسترسی به حافظه CPU

شکل 2. حفاظت از دسترسی به حافظه CPU

از لحاظ تاریخی، KVM با فعال کردن ترجمه مرحله 2 در حین اجرای مهمان ها و با غیرفعال شدن مرحله 2 هنگام اجرای هسته لینوکس میزبان اجرا می شود. این معماری به دسترسی‌های حافظه از MMU مرحله 1 میزبان اجازه می‌دهد تا از MMU مرحله 2 عبور کند، بنابراین دسترسی نامحدود از میزبان به صفحات حافظه مهمان را امکان‌پذیر می‌سازد. از طرف دیگر، pKVM حفاظت مرحله 2 را حتی در زمینه میزبان فعال می کند و هایپروایزر را به جای میزبان، مسئول محافظت از صفحات حافظه مهمان می کند.

KVM برای اجرای نقشه های پیچیده IPA/PA برای میهمانان ، از ترجمه آدرس در مرحله 2 استفاده می کند ، که با وجود تکه تکه شدن فیزیکی ، توهم حافظه پیوسته برای مهمانان را ایجاد می کند. با این حال ، استفاده از مرحله 2 MMU برای میزبان فقط به کنترل دسترسی محدود می شود. میزبان مرحله 2 از هویت نقشه برداری شده و اطمینان حاصل می کند که حافظه متناوب در فضای IPA میزبان در فضای PA متناوب است. این معماری امکان استفاده از نقشه های بزرگ در جدول صفحه را فراهم می کند و در نتیجه فشار بر روی بافر Lookaside ترجمه (TLB) را کاهش می دهد. از آنجا که نقشه برداری هویت را می توان توسط PA فهرست بندی کرد ، از مرحله میزبان 2 نیز برای ردیابی مالکیت صفحه به طور مستقیم در جدول صفحه استفاده می شود.

حفاظت از دسترسی مستقیم حافظه (DMA)

همانطور که قبلاً توضیح داده شد ، جابجایی صفحات مهمان از میزبان لینوکس در جداول صفحه CPU یک گام ضروری اما ناکافی برای محافظت از حافظه مهمان است. PKVM همچنین باید در برابر دسترسی های حافظه ساخته شده توسط دستگاه های دارای قابلیت DMA تحت کنترل هسته میزبان و امکان حمله DMA که توسط یک میزبان مخرب آغاز شده است ، محافظت کند. برای جلوگیری از دسترسی به چنین دستگاهی به حافظه مهمان ، PKVM برای هر دستگاه دارای DMA در سیستم ، همانطور که در شکل 3 نشان داده شده است ، به سخت افزار واحد مدیریت حافظه ورودی (IOMMU) نیاز دارد.

حفاظت از دسترسی حافظه DMA

شکل 3 محافظت از دسترسی حافظه DMA

حداقل ، سخت افزار IOMMU وسیله اعطای و ابطال دسترسی به خواندن/نوشتن را برای یک دستگاه به حافظه فیزیکی در دانه بندی صفحه فراهم می کند. با این حال ، این سخت افزار IOMMU استفاده از دستگاه ها را در PVM ها محدود می کند زیرا آنها مرحله 2 را با عنوان هویت تصور می کنند.

برای اطمینان از جداسازی بین ماشین های مجازی ، معاملات حافظه تولید شده از طرف نهادهای مختلف باید توسط IOMMU قابل تشخیص باشد تا از مجموعه مناسب جداول صفحه برای ترجمه استفاده شود.

علاوه بر این ، کاهش میزان کد خاص SOC در EL2 یک استراتژی کلیدی برای کاهش پایگاه محاسباتی قابل اعتماد (TCB) PKVM است و خلاف ورود رانندگان IOMMU در Hypervisor است. برای کاهش این مسئله ، میزبان EL1 مسئول کارهای مدیریت کمکی IOMMU ، مانند مدیریت انرژی ، اولیه سازی و در صورت لزوم ، قطع رسیدگی است.

با این حال ، قرار دادن میزبان در کنترل حالت دستگاه ، نیازهای اضافی را در رابط برنامه نویسی سخت افزار IOMMU قرار می دهد تا اطمینان حاصل شود که بررسی های مجوز با روش های دیگر قابل عبور نیست ، به عنوان مثال ، به دنبال تنظیم مجدد دستگاه.

IOMMU استاندارد و به خوبی پشتیبانی شده برای دستگاه های ARM که باعث انزوا و تکلیف مستقیم می شود ، معماری واحد مدیریت حافظه سیستم ARM (SMMU) است. این معماری راه حل مرجع توصیه شده است.

مالکیت حافظه

در زمان بوت ، فرض بر این است که تمام حافظه غیر Hypervisor متعلق به میزبان است و توسط Hypervisor به این ترتیب ردیابی می شود. هنگامی که یک PVM تخم ریزی می شود ، میزبان صفحات حافظه را اهدا می کند تا به آن اجازه دهد بوت شود و Hypervisor مالکیت آن صفحات را از میزبان به PVM منتقل می کند. بنابراین ، Hypervisor محدودیت های کنترل دسترسی را در جدول صفحه 2 میزبان قرار می دهد تا از دسترسی مجدد آن به صفحات جلوگیری شود و محرمانه بودن را به میهمان فراهم کند.

ارتباط بین میزبان و میهمانان با به اشتراک گذاری حافظه کنترل شده بین آنها امکان پذیر است. میهمانان مجاز به به اشتراک گذاشتن برخی از صفحات خود با میزبان با استفاده از Hypercall هستند ، که به Hypervisor دستور می دهد تا آن صفحات را در جدول مرحله میزبان 2 تنظیم کنند. به همین ترتیب ، ارتباط میزبان با TrustZone با استفاده از حافظه و/یا عملیات وام امکان پذیر است ، که همه آنها با استفاده از Firmwore Formwork For ARM (FF-A) توسط PKVM کنترل و کنترل می شوند.

از آنجا که نیازهای حافظه PVM می تواند با گذشت زمان تغییر کند ، یک ابرخودک ارائه می شود که به مالکیت صفحات مشخص شده متعلق به تماس گیرنده اجازه می دهد تا به میزبان بازگردد. در عمل از این hypercall با پروتکل بالون Virtio استفاده می شود تا VMM بتواند حافظه را از PVM بازگرداند و PVM بتواند VMM را از صفحات رها شده ، به روشی کنترل شده مطلع کند.

Hypervisor مسئول ردیابی مالکیت کلیه صفحات حافظه در سیستم و اینکه آیا آنها به اشتراک گذاشته می شوند یا به اشخاص دیگر وام می دهند. بیشتر این ردیابی حالت با استفاده از ابرداده متصل به میزهای صفحه 2 میزبان و مهمانان انجام می شود ، با استفاده از بیت های رزرو شده در ورودی های جدول صفحه (PTE) که همانطور که از نام آنها پیداست ، برای استفاده از نرم افزار رزرو شده است.

میزبان باید اطمینان حاصل کند که سعی در دسترسی به صفحاتی که توسط Hypervisor غیرقابل دسترسی شده اند ، نیست. دسترسی غیرقانونی میزبان باعث می شود که یک استثناء همزمان توسط Hypervisor به میزبان تزریق شود ، که یا می تواند منجر به انجام وظیفه کاربران مسئول دریافت سیگنال SEGV یا خرابی هسته میزبان شود. برای جلوگیری از دسترسی تصادفی ، صفحات اهدا شده به میهمانان برای تعویض یا ادغام توسط هسته میزبان واجد شرایط نیستند.

قطع کار و تایمر

وقفه ها بخش مهمی از نحوه تعامل مهمان با دستگاه ها و برای برقراری ارتباط بین CPU ها است ، جایی که قطع بین پردازنده (IPI) مکانیزم اصلی ارتباط است. مدل KVM این است که تمام مدیریت قطع مجازی را به میزبان در EL1 واگذار کند ، که برای این منظور به عنوان بخشی غیرقابل اعتماد از Hypervisor رفتار می کند.

PKVM یک تقلید کامل کنترل کننده کلی نسخه 3 (GICV3) را بر اساس کد KVM موجود ارائه می دهد. تایمر و IPI ها به عنوان بخشی از این کد شبیه سازی غیرقابل اعتماد اداره می شوند.

پشتیبانی GICV3

رابط بین EL1 و EL2 باید اطمینان حاصل کند که حالت قطع کامل برای میزبان EL1 قابل مشاهده است ، از جمله نسخه های ثبت های Hypervisor مربوط به وقفه ها. این دید به طور معمول با استفاده از مناطق حافظه مشترک ، یکی در هر پردازنده مجازی (VCPU) انجام می شود.

سیستم پشتیبانی از زمان اجرا سیستم ثبت نام فقط می تواند برای پشتیبانی از نرم افزار ایجاد شده توسط ثبت نام قطع (SGIR) و غیرفعال کردن ثبت نام (DIR) ثبت نام ، ساده شود. این معماری موظف است که این ثبت ها همیشه به EL2 دامن بزنند ، در حالی که تله های دیگر تاکنون فقط برای کاهش Errata مفید بوده اند. هر چیز دیگری در سخت افزار اداره می شود.

از طرف MMIO ، همه چیز در EL1 تقلید می شود و از تمام زیرساخت های فعلی در KVM استفاده می کند. سرانجام ، صبر کنید تا وقفه (WFI) همیشه به EL1 منتقل شود ، زیرا این یکی از برنامه های اولیه اولیه KVM است.

حمایت تایمر

مقدار مقایسه کننده برای تایمر مجازی باید در هر Traping WFI در معرض EL1 قرار گیرد تا EL1 بتواند در حالی که VCPU مسدود شده است ، تایمر را قطع کند. تایمر فیزیکی کاملاً تقلید شده است و همه تله ها به EL1 منتقل می شوند.

دست زدن به MMIO

برای برقراری ارتباط با مانیتور ماشین مجازی (VMM) و انجام تقلید GIC ، تله های MMIO باید برای ترسیم بیشتر به میزبان در EL1 منتقل شوند. PKVM به موارد زیر نیاز دارد:

  • IPA و اندازه دسترسی
  • داده ها در صورت نوشتن
  • endianness CPU در نقطه به دام انداختن

علاوه بر این ، تله هایی با یک ثبت کلی هدف (GPR) به عنوان یک منبع/مقصد با استفاده از یک فهرست شبه انتقال انتزاعی منتقل می شوند.

رابط های مهمان

یک میهمان می تواند با استفاده از ترکیبی از هایپر کلوچه و دسترسی حافظه به مناطق به دام افتاده با یک میهمان محافظت شده ارتباط برقرار کند. Hypercalls مطابق با استاندارد SMCCC در معرض دید قرار می گیرد و دامنه آن برای تخصیص فروشنده توسط KVM محفوظ است. بیش از حد های زیر برای میهمانان PKVM از اهمیت ویژه ای برخوردار هستند.

هانیایی های عمومی

  • PSCI یک مکانیزم استاندارد را برای مهمان فراهم می کند تا چرخه عمر VCPU های خود را از جمله ورود ، تخلیه و خاموش کردن سیستم کنترل کند.
  • TRNG یک مکانیزم استاندارد را برای مهمان فراهم می کند تا آنتروپی را از PKVM درخواست کند که تماس را به EL3 منتقل می کند. این مکانیسم به ویژه در جایی مفید است که به میزبان اعتماد نمی شود تا یک ژنراتور شماره تصادفی سخت افزاری (RNG) را مجازی کند.

Hypercalls PKVM

  • اشتراک حافظه با میزبان. تمام حافظه مهمان در ابتدا برای میزبان غیرقابل دسترسی است ، اما دسترسی به میزبان برای ارتباطات مشترک حافظه و برای دستگاه های paravirtualized که به بافرهای مشترک متکی هستند ، ضروری است. Hypercalls برای به اشتراک گذاری و صفحات غیرقابل توصیف با میزبان به مهمان این امکان را می دهد تا دقیقاً تصمیم بگیرد که چه قسمت هایی از حافظه را بدون نیاز به دست دادن به بقیه اندروید در دسترس قرار می دهد.
  • رها کردن حافظه به میزبان. تمام حافظه مهمان معمولاً متعلق به میهمان است تا زمانی که ویران شود. این حالت می تواند برای VM های طولانی مدت با نیازهای حافظه که با گذشت زمان متفاوت است ، ناکافی باشد. Hypercall relinquish یک مهمان اجازه می دهد تا صریحاً مالکیت صفحات را به میزبان برگرداند بدون اینکه به خاتمه مهمان نیاز داشته باشد.
  • به دام انداختن حافظه به میزبان. به طور سنتی ، اگر یک مهمان KVM به آدرس دسترسی پیدا کند که مطابق با یک منطقه حافظه معتبر نباشد ، موضوع VCPU به میزبان خارج می شود و دسترسی به طور معمول برای MMIO استفاده می شود و توسط VMM در فضای کاربر تقلید می شود. برای تسهیل این کار ، PKVM موظف است جزئیات مربوط به دستورالعمل گسل مانند آدرس آن ، ثبت نام پارامترها و محتوای بالقوه آنها را به میزبان تبلیغ کند ، که در صورت عدم پیش بینی تله می تواند داده های حساس را از یک مهمان محافظت شده افشا کند. PKVM با درمان این گسلها به عنوان کشنده ، این مشکل را حل می کند ، مگر اینکه مهمان قبلاً یک Hypercall صادر کرده است تا دامنه IPA گسل را به عنوان مواردی که برای دسترسی به آنها مجاز است به میزبان برگردد. این راه حل به عنوان نگهبان MMIO گفته می شود.

دستگاه I/O مجازی (فضیلت)

Virtio یک استاندارد محبوب ، قابل حمل و بالغ برای اجرای و تعامل با دستگاه های paravirtualized است. اکثر دستگاه های در معرض میهمانان محافظت شده با استفاده از Virtio اجرا می شوند. Virtio همچنین زیربنای اجرای VSOCK که برای برقراری ارتباط بین یک میهمان محافظت شده و بقیه اندروید استفاده می شود.

دستگاه های Virtio به طور معمول در فضای کاربر میزبان توسط VMM پیاده سازی می شوند ، که دسترسی به حافظه به دام افتاده از مهمان به رابط MMIO دستگاه Virtio را رهگیری می کند و رفتار مورد انتظار را تقلید می کند. دسترسی MMIO نسبتاً گران است زیرا هر دسترسی به دستگاه نیاز به یک سفر دور به VMM و عقب دارد ، بنابراین بیشتر انتقال داده واقعی بین دستگاه و میهمان با استفاده از مجموعه ای از مجازی ها در حافظه رخ می دهد. یک فرض مهم از فضیلت این است که میزبان می تواند به طور دلخواه به حافظه مهمان دسترسی پیدا کند. این فرض در طراحی مجازی مشهود است ، که ممکن است حاوی نشانگرهایی برای بافر در مهمان باشد که تقلید دستگاه برای دسترسی مستقیم به آن در نظر گرفته شده است.

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

دستگاه مجازی

شکل 4 دستگاه فضیلت

تعامل با TrustZone

اگرچه میهمانان قادر به تعامل مستقیم با TrustZone نیستند ، اما میزبان هنوز هم می تواند تماس های SMC را به دنیای امن صادر کند. این تماس ها می توانند بافرهای حافظه جسمی را که برای میزبان غیرقابل دسترسی هستند ، مشخص کنند. از آنجا که نرم افزار ایمن به طور کلی از دسترسی بافر غافل است ، یک میزبان مخرب می تواند از این بافر برای انجام یک حمله معاون سردرگم (مشابه حمله DMA) استفاده کند. برای جلوگیری از چنین حملاتی ، PKVM تمام تماس های SMC میزبان را به EL2 تله می دهد و به عنوان یک پروکسی بین میزبان و مانیتور امن در EL3 عمل می کند.

تماس های PSCI از میزبان با حداقل اصلاحات به سیستم عامل EL3 ارسال می شود. به طور خاص ، نقطه ورود به یک پردازنده به صورت آنلاین یا از سرگیری از حالت تعلیق بازنویسی می شود به طوری که جدول مرحله 2 قبل از بازگشت به میزبان در EL1 در EL2 نصب شده است. در طول بوت ، این محافظت توسط PKVM اجرا می شود.

این معماری به PSCI پشتیبانی SOC متکی است ، ترجیحاً از طریق استفاده از نسخه به روز TF-A به عنوان سیستم عامل EL3.

چارچوب سیستم عامل برای ARM (FF-A) تعامل بین جهان های عادی و ایمن ، به ویژه در حضور یک فشارخون ایمن را استاندارد می کند. بخش عمده ای از مشخصات ، مکانیسم به اشتراک گذاری حافظه با دنیای امن را با استفاده از یک قالب پیام مشترک و یک مدل مجوزهای به خوبی تعریف شده برای صفحات اساسی تعریف می کند. پیام های PKVM Proxies FF-A برای اطمینان از اینکه میزبان در تلاش نیست حافظه را با سمت امن که مجوز کافی ندارد ، به اشتراک بگذارد.

این معماری متکی به نرم افزار جهانی امن است که مدل دسترسی به حافظه را اجرا می کند ، تا اطمینان حاصل شود که برنامه های قابل اعتماد و هر نرم افزاری دیگر که در دنیای امن اجرا می شوند ، می توانند به حافظه دسترسی پیدا کنند ، تنها در صورتی که به طور انحصاری متعلق به دنیای امن باشد یا با استفاده از FF-A صریحاً با آن به اشتراک گذاشته شده است. بر روی یک سیستم با S-EL2 ، اجرای مدل دسترسی حافظه باید توسط یک Core Secure Partition Manager (SPMC) مانند Hafnium انجام شود ، که میزهای صفحه 2 را برای دنیای امن حفظ می کند. در سیستم بدون S-EL2 ، TEE می تواند در عوض یک مدل دسترسی به حافظه را از طریق جداول صفحه 1 صفحه خود اجرا کند.

اگر تماس SMC به EL2 یک تماس PSCI یا پیام تعریف شده FF-A نباشد ، SMC های نامشخص به EL3 ارسال می شوند. فرض بر این است که سیستم عامل ایمن (لزوماً مورد اعتماد) می تواند SMC های نامشخص را با خیال راحت اداره کند زیرا سیستم عامل اقدامات احتیاطی مورد نیاز برای حفظ انزوا PVM را درک می کند.

مانیتور ماشین مجازی

CROSVM یک مانیتور ماشین مجازی (VMM) است که ماشین های مجازی را از طریق رابط KVM لینوکس اجرا می کند. آنچه CROSVM را منحصر به فرد می کند ، تمرکز آن بر ایمنی با استفاده از زبان برنامه نویسی زنگ زدگی و یک جعبه ماسه ای در اطراف دستگاه های مجازی برای محافظت از هسته میزبان است. برای اطلاعات بیشتر در مورد CROSVM ، مستندات رسمی آن را در اینجا ببینید.

توصیف کننده های پرونده و ioctls

KVM دستگاه کاراکتر /dev/kvm را در فضای کاربری با IOCTL هایی که API KVM را تشکیل می دهند ، در معرض دید قرار می دهد. IOCTL ها متعلق به دسته های زیر هستند:

  • پرس و جو IOCTLS سیستم و ویژگی های جهانی را تنظیم می کند که بر کل سیستم KVM تأثیر می گذارد و PVM ها را ایجاد می کند.
  • VM IOCTLS QUERY و ویژگی هایی را تنظیم می کند که CPU های مجازی (VCPUS) و دستگاه ها را ایجاد می کنند و بر یک PVM کامل تأثیر می گذارند ، مانند طرح حافظه و تعداد CPU های مجازی (VCPU) و دستگاه ها.
  • VCPU IOCTLS پرس و جو و ویژگی هایی را تنظیم می کند که عملکرد یک پردازنده مجازی واحد را کنترل می کند.
  • دستگاه IOCTLS را پرس و جو کنید و ویژگی هایی را تنظیم کنید که عملکرد یک دستگاه مجازی واحد را کنترل می کند.

هر فرآیند CROSVM دقیقاً یک نمونه از یک ماشین مجازی را اجرا می کند. این فرآیند از سیستم KVM_CREATE_VM IOCTL برای ایجاد یک توصیف کننده فایل VM استفاده می کند که می تواند برای صدور IOCTL های PVM استفاده شود. KVM_CREATE_VCPU یا KVM_CREATE_DEVICE ioctl در VM FD یک VCPU/دستگاه ایجاد می کند و یک توصیف کننده فایل را با اشاره به منبع جدید باز می گرداند. IOCTLS در VCPU یا دستگاه FD می تواند برای کنترل دستگاهی که با استفاده از IOCTL در VM FD ایجاد شده است ، استفاده شود. برای VCPU ، این شامل کار مهم اجرای کد مهمان است.

در داخل ، CROSVM توصیف کننده های پرونده VM را با هسته با استفاده از رابط epoll Edge-Triggered ثبت می کند. سپس هسته هر زمان که یک رویداد جدید در هر یک از توصیف کننده های پرونده در انتظار باشد ، CROSVM را مطلع می کند.

PKVM قابلیت جدیدی را اضافه می کند ، KVM_CAP_ARM_PROTECTED_VM ، که می تواند برای دریافت اطلاعات در مورد محیط PVM و تنظیم حالت محافظت شده برای VM استفاده شود. CROSVM در صورت ایجاد پرچم- --protected-vm ، برای پرس و جو و رزرو مقدار مناسب حافظه برای سیستم عامل PVM ، و سپس برای فعال کردن حالت محافظت شده ، از این در هنگام ایجاد PVM استفاده می کند.

تخصیص حافظه

یکی از اصلی ترین مسئولیت های یک VMM ، اختصاص حافظه VM و مدیریت طرح حافظه آن است. CROSVM یک طرح حافظه ثابت ایجاد می کند که در جدول زیر شرح داده شده است.

FDT در حالت عادی PHYS_MEMORY_END - 0x200000
فضای آزاد ...
رامدیسک ALIGN_UP(KERNEL_END, 0x1000000)
هسته 0x80080000
بوت لودر 0x80200000
FDT در حالت BIOS 0x80000000
پایه حافظه فیزیکی 0x80000000
سیستم عامل PVM 0x7FE00000
حافظه دستگاه 0x10000 - 0x40000000

حافظه فیزیکی با mmap اختصاص داده می شود و حافظه به VM اهدا می شود تا مناطق حافظه آن ، به نام memslots ، با KVM_SET_USER_MEMORY_REGION ioctl جمع شود. بنابراین تمام حافظه PVM مهمان به نمونه CROSVM که مدیریت آن را انجام می دهد ، نسبت داده می شود و در صورت شروع میزبان از حافظه آزاد ، می تواند منجر به کشته شدن این روند شود (خاتمه VM). هنگامی که یک VM متوقف شد ، حافظه به طور خودکار توسط Hypervisor از بین می رود و به هسته میزبان باز می گردد.

تحت KVM معمولی ، VMM دسترسی به تمام حافظه مهمان را حفظ می کند. با PKVM ، حافظه مهمان هنگام اهدا به میهمان از فضای آدرس فیزیکی میزبان جدا می شود. تنها استثناء حافظه به صراحت توسط مهمان به اشتراک گذاشته شده است ، مانند دستگاه های Virtio.

مناطق MMIO در فضای آدرس مهمان بدون استفاده باقی مانده است. دسترسی به این مناطق توسط میهمان به دام افتاده و منجر به یک رویداد I/O در VM FD می شود. این مکانیسم برای اجرای دستگاه های مجازی استفاده می شود. در حالت حفاظت شده ، میهمان باید تصدیق کند که از منطقه ای از فضای آدرس خود برای MMIO با استفاده از Hypercall استفاده می شود تا خطر نشت اطلاعات تصادفی را کاهش دهد.

برنامه ریزی

هر CPU مجازی توسط یک موضوع POSIX نشان داده شده و توسط برنامه ریز میزبان لینوکس برنامه ریزی شده است. موضوع KVM_RUN IOCTL را در VCPU FD فراخوانی می کند و در نتیجه Hypervisor به متن VCPU مهمان تبدیل می شود. برنامه ریز میزبان زمان صرف شده در زمینه مهمان را به عنوان زمانی که توسط موضوع VCPU مربوطه استفاده می شود ، حساب می کند. KVM_RUN هنگامی که رویدادی وجود دارد که باید توسط VMM انجام شود ، مانند I/O ، پایان وقفه یا VCPU متوقف می شود. VMM این رویداد را کنترل می کند و دوباره با KVM_RUN تماس می گیرد.

در طول KVM_RUN ، موضوع توسط برنامه ریز میزبان قابل پیش بینی است ، به جز اجرای کد Hypervisor EL2 ، که قابل پیش بینی نیست. PVM مهمان خود مکانیسمی برای کنترل این رفتار ندارد.

از آنجا که تمام موضوعات VCPU مانند سایر کارهای فضای کاربر برنامه ریزی شده است ، آنها در معرض همه مکانیسم های استاندارد QoS قرار دارند. به طور خاص ، هر نخ VCPU می تواند به CPU های فیزیکی ، که در CPUSETS قرار می گیرند ، با استفاده از بستن استفاده ، تقویت یا پوشش داده شود ، خط مشی اولویت/برنامه ریزی خود را تغییر داده و موارد دیگر.

دستگاه های مجازی

CROSVM از تعدادی دستگاه از جمله موارد زیر پشتیبانی می کند:

  • Virtio-Blk برای تصاویر دیسک کامپوزیت ، فقط خواندنی یا خواندن نوشتن
  • Vhost-vsock برای برقراری ارتباط با میزبان
  • Virtio-PCI به عنوان حمل و نقل فضیلت
  • ساعت واقعی PL030 (RTC)
  • 16550a UART برای ارتباطات سریال

سیستم عامل PVM

سیستم عامل PVM (PVMFW) اولین کدی است که توسط یک PVM اجرا شده است ، مشابه ROM Boot یک دستگاه فیزیکی. هدف اصلی PVMFW ، بوت استرپ ایمن و به دست آوردن راز منحصر به فرد PVM است. PVMFW محدود به استفاده از هر سیستم عامل خاص مانند MicroDroid نیست ، تا زمانی که سیستم عامل توسط CROSVM پشتیبانی شود و به درستی امضا شده باشد.

باینری PVMFW در یک پارتیشن فلش با همین نام ذخیره می شود و با استفاده از OTA به روز می شود.

چکمه دستگاه

دنباله زیر از مراحل به روش بوت یک دستگاه فعال شده با PKVM اضافه می شود:

  1. Android Boodloader (ABL) PVMFW را از پارتیشن خود به حافظه بار می دهد و تصویر را تأیید می کند.
  2. ABL اسرار ترکیب دستگاه شناسه دستگاه خود (DICE) (شناسه دستگاه مرکب (CDI) و زنجیره گواهی تاس) را از ریشه اعتماد بدست می آورد.
  3. ABL CDI های لازم را برای PVMFW مشتق می کند و آنها را به باینری PVMFW اضافه می کند.
  4. ABL یک گره linux,pkvm-guest-firmware-memory به DT اضافه می کند و مکان و اندازه باینری PVMFW و رازهای حاصل از آن را در مرحله قبل توصیف می کند.
  5. کنترل دست های ABL به لینوکس و لینوکس PKVM را آغاز می کند.
  6. PKVM منطقه حافظه PVMFW را از جداول صفحه 2 میزبان UNMAPS UNMAPS UNMAPS می کند و آن را از میزبان (و میهمانان) در طول زمان به روز محافظت می کند.

بعد از بوت شدن دستگاه ، میکرودروید در هر مرحله در بخش توالی بوت سند MicroDroid بوت می شود.

چکمه PVM

هنگام ایجاد PVM ، CROSVM (یا یک VMM دیگر) باید یک memslot به اندازه کافی بزرگ ایجاد کند که با تصویر PVMFW توسط Hypervisor جمع شود. VMM همچنین در لیست رجیسترهایی که مقدار اولیه آن می تواند تنظیم کند (X0-X14 برای VCPU اولیه ، هیچ کدام برای VCPU های ثانویه) محدود شده است. ثبت های باقیمانده محفوظ هستند و بخشی از Hypervisor-PVMFW ABI هستند.

هنگامی که PVM اجرا می شود ، Hypervisor ابتدا کنترل VCPU اولیه را به PVMFW کنترل می کند. این سیستم عامل انتظار دارد که CROSVM یک هسته AVB را بارگیری کند ، که می تواند یک بوت لودر یا هر تصویر دیگر باشد ، و یک FDT بدون امضا برای حافظه در جبران های شناخته شده. PVMFW امضای AVB را تأیید می کند و در صورت موفقیت ، یک درخت دستگاه قابل اعتماد را از FDT دریافت شده تولید می کند ، اسرار خود را از حافظه پاک می کند و شاخه ها را به نقطه ورود بار می دهد. در صورت عدم موفقیت یکی از مراحل تأیید ، سیستم عامل یک hypercall psci SYSTEM_RESET را صادر می کند.

بین چکمه ها ، اطلاعات مربوط به نمونه PVM در یک پارتیشن (دستگاه فضیلت-Blk) ذخیره می شود و با راز PVMFW رمزگذاری می شود تا اطمینان حاصل شود که پس از راه اندازی مجدد ، راز به نمونه صحیح ارائه می شود.

،

Android اجرای مرجع کلیه مؤلفه های مورد نیاز برای اجرای چارچوب مجازی سازی Android را ارائه می دهد. در حال حاضر این اجرای محدود به ARM64 است. این صفحه معماری چارچوب را توضیح می دهد.

پس زمینه

معماری بازو اجازه می دهد تا حداکثر چهار سطح استثنا ، با استثناء سطح 0 (EL0) کمترین امتیاز و استثناء سطح 3 (EL3) باشد. بیشترین بخش از پایگاه Android Code (کلیه مؤلفه های فضای کاربری) در EL0 اجرا می شود. بقیه آنچه معمولاً "Android" نامیده می شود ، هسته لینوکس است که در EL1 اجرا می شود.

لایه EL2 امکان معرفی یک Hypervisor را فراهم می کند که باعث می شود حافظه و دستگاه ها در PVM های فردی در EL1/EL0 ، با محرمانه بودن و ضمانت های یکپارچگی ، امکان پذیر شود.

هایپروایزر

ماشین مجازی مبتنی بر هسته محافظت شده (PKVM) بر روی Hypervisor Linux KVM ساخته شده است ، که با امکان محدود کردن دسترسی به بارهای موجود در ماشین های مجازی مهمان که در زمان ایجاد "محافظت شده" مشخص شده اند ، گسترش یافته است.

KVM/ARM64 بسته به در دسترس بودن برخی از ویژگی های CPU ، یعنی پسوندهای میزبان مجازی (VHE) (ARMV8.1 و بعد) از حالت های مختلف اجرای پشتیبانی می کند. در یکی از این حالت ها ، که معمولاً به عنوان حالت غیر VHE شناخته می شود ، کد Hypervisor از تصویر هسته در حین بوت تقسیم می شود و در EL2 نصب می شود ، در حالی که خود هسته در EL1 اجرا می شود. اگرچه بخشی از پایگاه کد لینوکس ، مؤلفه EL2 KVM یک مؤلفه کوچک در مسئول سوئیچ بین EL1 های متعدد است. مؤلفه Hypervisor با لینوکس گردآوری شده است ، اما در یک بخش حافظه جداگانه و اختصاصی از تصویر vmlinux قرار دارد. PKVM با گسترش کد Hypervisor با ویژگی های جدید ، این طرح را به شما امکان می دهد تا محدودیت هایی را در هسته میزبان Android و فضای کاربر ایجاد کند و دسترسی میزبان به حافظه مهمان و Hypervisor را محدود کند.

ماژول های فروشنده PKVM

یک ماژول فروشنده PKVM یک ماژول خاص سخت افزار است که حاوی عملکرد خاص دستگاه مانند درایورهای واحد مدیریت حافظه ورودی (IOMMU) است. این ماژول ها به شما امکان می دهد ویژگی های امنیتی را که نیاز به دسترسی به استثناء سطح 2 (EL2) به PKVM دارند ، به شما امکان پذیر شود.

برای یادگیری نحوه پیاده سازی و بارگذاری یک ماژول فروشنده PKVM ، به اجرای یک ماژول فروشنده PKVM مراجعه کنید.

رویه بوت

شکل زیر روش بوت PKVM را نشان می دهد:

روش بوت PKVM

شکل 1 روش بوت PKVM

  1. بوت لودر وارد هسته عمومی در EL2 می شود.
  2. هسته عمومی تشخیص می دهد که در EL2 در حال اجرا است و خود را به EL1 محروم می کند در حالی که PKVM و ماژول های آن همچنان در EL2 اجرا می شوند. علاوه بر این ، ماژول های فروشنده PKVM در این زمان بارگیری می شوند.
  3. هسته عمومی به طور عادی بوت می شود و تمام درایورهای دستگاه لازم را تا رسیدن به فضای کاربر بارگیری می کند. در این مرحله ، PKVM در جای خود قرار دارد و جداول صفحه Stage-2 را کنترل می کند.

روش بوت به بوت لودر اعتماد دارد تا یکپارچگی تصویر هسته را فقط در زمان بوت شدن حفظ کند. هنگامی که هسته محروم می شود ، دیگر مورد اعتماد Hypervisor قرار نمی گیرد ، که حتی اگر هسته به خطر بیفتد ، مسئولیت محافظت از خود را نیز بر عهده دارد.

داشتن هسته اندرویدی و Hypervisor در همان تصویر باینری امکان ایجاد رابط ارتباطی بسیار محکم بین آنها را فراهم می کند. این جفت محکم به روزرسانی های اتمی این دو مؤلفه را تضمین می کند ، که از نیاز به حفظ رابط بین آنها جلوگیری می کند و انعطاف پذیری زیادی را بدون به خطر انداختن قابلیت حفظ طولانی مدت ارائه می دهد. اتصال محکم همچنین امکان بهینه سازی عملکرد را فراهم می کند که هر دو مؤلفه بتوانند بدون تأثیر بر ضمانت های امنیتی ارائه شده توسط Hypervisor همکاری کنند.

علاوه بر این ، پذیرش GKI در اکوسیستم اندرویدی به طور خودکار به PKVM Hypervisor اجازه می دهد تا در همان باینری به عنوان هسته به دستگاه های اندرویدی اعزام شود.

محافظت از حافظه CPU

معماری ARM یک واحد مدیریت حافظه (MMU) را در دو مرحله مستقل تقسیم می کند ، که هر دو می توانند برای اجرای آدرس ترجمه و کنترل دسترسی به قسمت های مختلف حافظه استفاده شوند. مرحله 1 MMU توسط EL1 کنترل می شود و سطح اول ترجمه آدرس را امکان پذیر می کند. مرحله 1 MMU توسط لینوکس برای مدیریت فضای آدرس مجازی ارائه شده به هر فرآیند فضای کاربری و فضای آدرس مجازی خود استفاده می شود.

مرحله 2 MMU توسط EL2 کنترل می شود و کاربرد ترجمه آدرس دوم را در آدرس خروجی مرحله 1 MMU امکان پذیر می کند و در نتیجه یک آدرس فیزیکی (PA) ایجاد می شود. ترجمه مرحله 2 می تواند توسط Hypervisors برای کنترل و ترجمه دسترسی به حافظه از همه VM های مهمان استفاده شود. همانطور که در شکل 2 نشان داده شده است ، هنگامی که هر دو مرحله ترجمه فعال می شوند ، آدرس خروجی مرحله 1 یک آدرس فیزیکی متوسط ​​(IPA) نامیده می شود توجه: آدرس مجازی (VA) به IPA و سپس به PA ترجمه می شود.

محافظت از حافظه CPU

شکل 2. حفاظت از دسترسی حافظه CPU

از نظر تاریخی ، KVM با ترجمه مرحله 2 در حین اجرای میهمانان و با مرحله 2 غیرفعال هنگام اجرای هسته میزبان لینوکس اجرا می شود. این معماری به دسترسی حافظه از مرحله 1 MMU میزبان اجازه می دهد تا از مرحله 2 MMU عبور کند ، از این رو امکان دسترسی نامحدود از میزبان به صفحات حافظه مهمان را فراهم می کند. از طرف دیگر ، PKVM حتی در زمینه میزبان ، محافظت از مرحله 2 را نیز امکان پذیر می کند ، و Hypervisor را مسئول محافظت از صفحات حافظه مهمان به جای میزبان قرار می دهد.

KVM برای اجرای نقشه های پیچیده IPA/PA برای میهمانان ، از ترجمه آدرس در مرحله 2 استفاده می کند ، که با وجود تکه تکه شدن فیزیکی ، توهم حافظه پیوسته برای مهمانان را ایجاد می کند. با این حال ، استفاده از مرحله 2 MMU برای میزبان فقط به کنترل دسترسی محدود می شود. میزبان مرحله 2 از هویت نقشه برداری شده و اطمینان حاصل می کند که حافظه متناوب در فضای IPA میزبان در فضای PA متناوب است. این معماری امکان استفاده از نقشه های بزرگ در جدول صفحه را فراهم می کند و در نتیجه فشار بر روی بافر Lookaside ترجمه (TLB) را کاهش می دهد. از آنجا که نقشه برداری هویت را می توان توسط PA فهرست بندی کرد ، از مرحله میزبان 2 نیز برای ردیابی مالکیت صفحه به طور مستقیم در جدول صفحه استفاده می شود.

حفاظت از دسترسی مستقیم حافظه (DMA)

همانطور که قبلاً توضیح داده شد ، جابجایی صفحات مهمان از میزبان لینوکس در جداول صفحه CPU یک گام ضروری اما ناکافی برای محافظت از حافظه مهمان است. PKVM همچنین باید در برابر دسترسی های حافظه ساخته شده توسط دستگاه های دارای قابلیت DMA تحت کنترل هسته میزبان و امکان حمله DMA که توسط یک میزبان مخرب آغاز شده است ، محافظت کند. برای جلوگیری از دسترسی به چنین دستگاهی به حافظه مهمان ، PKVM برای هر دستگاه دارای DMA در سیستم ، همانطور که در شکل 3 نشان داده شده است ، به سخت افزار واحد مدیریت حافظه ورودی (IOMMU) نیاز دارد.

حفاظت از دسترسی حافظه DMA

شکل 3 محافظت از دسترسی حافظه DMA

حداقل ، سخت افزار IOMMU وسیله اعطای و ابطال دسترسی به خواندن/نوشتن را برای یک دستگاه به حافظه فیزیکی در دانه بندی صفحه فراهم می کند. با این حال ، این سخت افزار IOMMU استفاده از دستگاه ها را در PVM ها محدود می کند زیرا آنها مرحله 2 را با عنوان هویت تصور می کنند.

برای اطمینان از جداسازی بین ماشین های مجازی ، معاملات حافظه تولید شده از طرف نهادهای مختلف باید توسط IOMMU قابل تشخیص باشد تا از مجموعه مناسب جداول صفحه برای ترجمه استفاده شود.

علاوه بر این ، کاهش میزان کد خاص SOC در EL2 یک استراتژی کلیدی برای کاهش پایگاه محاسباتی قابل اعتماد (TCB) PKVM است و خلاف ورود رانندگان IOMMU در Hypervisor است. برای کاهش این مسئله ، میزبان EL1 مسئول کارهای مدیریت کمکی IOMMU ، مانند مدیریت انرژی ، اولیه سازی و در صورت لزوم ، قطع رسیدگی است.

با این حال ، قرار دادن میزبان در کنترل حالت دستگاه ، نیازهای اضافی را در رابط برنامه نویسی سخت افزار IOMMU قرار می دهد تا اطمینان حاصل شود که بررسی های مجوز با روش های دیگر قابل عبور نیست ، به عنوان مثال ، به دنبال تنظیم مجدد دستگاه.

IOMMU استاندارد و به خوبی پشتیبانی شده برای دستگاه های ARM که باعث انزوا و تکلیف مستقیم می شود ، معماری واحد مدیریت حافظه سیستم ARM (SMMU) است. این معماری راه حل مرجع توصیه شده است.

مالکیت حافظه

در زمان بوت ، فرض بر این است که تمام حافظه غیر Hypervisor متعلق به میزبان است و توسط Hypervisor به این ترتیب ردیابی می شود. هنگامی که یک PVM تخم ریزی می شود ، میزبان صفحات حافظه را اهدا می کند تا به آن اجازه دهد بوت شود و Hypervisor مالکیت آن صفحات را از میزبان به PVM منتقل می کند. بنابراین ، Hypervisor محدودیت های کنترل دسترسی را در جدول صفحه 2 میزبان قرار می دهد تا از دسترسی مجدد آن به صفحات جلوگیری شود و محرمانه بودن را به میهمان فراهم کند.

ارتباط بین میزبان و میهمانان با به اشتراک گذاری حافظه کنترل شده بین آنها امکان پذیر است. میهمانان مجاز به به اشتراک گذاشتن برخی از صفحات خود با میزبان با استفاده از Hypercall هستند ، که به Hypervisor دستور می دهد تا آن صفحات را در جدول مرحله میزبان 2 تنظیم کنند. به همین ترتیب ، ارتباط میزبان با TrustZone با استفاده از حافظه و/یا عملیات وام امکان پذیر است ، که همه آنها با استفاده از Firmwore Formwork For ARM (FF-A) توسط PKVM کنترل و کنترل می شوند.

از آنجا که نیازهای حافظه PVM می تواند با گذشت زمان تغییر کند ، یک ابرخودک ارائه می شود که به مالکیت صفحات مشخص شده متعلق به تماس گیرنده اجازه می دهد تا به میزبان بازگردد. در عمل از این hypercall با پروتکل بالون Virtio استفاده می شود تا VMM بتواند حافظه را از PVM بازگرداند و PVM بتواند VMM را از صفحات رها شده ، به روشی کنترل شده مطلع کند.

Hypervisor مسئول ردیابی مالکیت کلیه صفحات حافظه در سیستم و اینکه آیا آنها به اشتراک گذاشته می شوند یا به اشخاص دیگر وام می دهند. بیشتر این ردیابی حالت با استفاده از ابرداده متصل به میزهای صفحه 2 میزبان و مهمانان انجام می شود ، با استفاده از بیت های رزرو شده در ورودی های جدول صفحه (PTE) که همانطور که از نام آنها پیداست ، برای استفاده از نرم افزار رزرو شده است.

میزبان باید اطمینان حاصل کند که سعی در دسترسی به صفحاتی که توسط Hypervisor غیرقابل دسترسی شده اند ، نیست. دسترسی غیرقانونی میزبان باعث می شود که یک استثناء همزمان توسط Hypervisor به میزبان تزریق شود ، که یا می تواند منجر به انجام وظیفه کاربران مسئول دریافت سیگنال SEGV یا خرابی هسته میزبان شود. برای جلوگیری از دسترسی تصادفی ، صفحات اهدا شده به میهمانان برای تعویض یا ادغام توسط هسته میزبان واجد شرایط نیستند.

قطع کار و تایمر

وقفه ها بخش مهمی از نحوه تعامل مهمان با دستگاه ها و برای برقراری ارتباط بین CPU ها است ، جایی که قطع بین پردازنده (IPI) مکانیزم اصلی ارتباط است. مدل KVM این است که تمام مدیریت قطع مجازی را به میزبان در EL1 واگذار کند ، که برای این منظور به عنوان بخشی غیرقابل اعتماد از Hypervisor رفتار می کند.

PKVM یک تقلید کامل کنترل کننده کلی نسخه 3 (GICV3) را بر اساس کد KVM موجود ارائه می دهد. تایمر و IPI ها به عنوان بخشی از این کد شبیه سازی غیرقابل اعتماد اداره می شوند.

پشتیبانی GICV3

The interface between EL1 and EL2 must ensure that the full interrupt state is visible to the EL1 host, including copies of the hypervisor registers related to interrupts. This visibility is typically accomplished using shared memory regions, one per virtual CPU (vCPU).

The system register runtime support code can be simplified to support only the Software Generated Interrupt Register (SGIR) and Deactivate Interrupt Register (DIR) register trapping. The architecture mandates that these registers always trap to EL2, while the other traps have so far only been useful to mitigate errata. Everything else is being handled in hardware.

On the MMIO side, everything is emulated at EL1, reusing all the current infrastructure in KVM. Finally, Wait for Interrupt (WFI) is always relayed to EL1, because this is one of the basic scheduling primitives KVM uses.

Timer support

The comparator value for the virtual timer must be exposed to EL1 on each trapping WFI so that EL1 can inject timer interrupts while the vCPU is blocked. The physical timer is entirely emulated, and all traps relayed to EL1.

MMIO handling

To communicate with the virtual machine monitor (VMM) and perform GIC emulation, MMIO traps must be relayed back to the host in EL1 for further triaging. pKVM requires the following:

  • IPA and size of the access
  • Data in case of a write
  • Endianness of the CPU at the point of trapping

Additionally, traps with a general purpose register (GPR) as a source/destination are relayed using an abstract transfer pseudo-register.

Guest interfaces

A guest can communicate with a protected guest using a combination of hypercalls and memory access to trapped regions. Hypercalls are exposed according to the SMCCC standard , with a range reserved for a vendor allocation by KVM. The following hypercalls are of particular importance to pKVM guests.

Generic hypercalls

  • PSCI provides a standard mechanism for the guest to control the lifecycle of its vCPUs including onlining, offlining, and system shutdown.
  • TRNG provides a standard mechanism for the guest to request entropy from the pKVM which relays the call to EL3. This mechanism is particularly useful where the host can't be trusted to virtualize a hardware random number generator (RNG).

pKVM hypercalls

  • Memory sharing with the host. All guest memory is initially inaccessible to the host, but host access is necessary for shared-memory communication and for paravirtualized devices that rely on shared buffers. Hypercalls for sharing and unsharing pages with the host allow the guest to decide exactly what parts of memory are made accessible to the rest of Android without the need for a handshake.
  • Memory relinquishment to the host. All guest memory usually belongs to the guest until it is destroyed. This state can be inadequate for long-lived VMs with memory requirements which vary over time. The relinquish hypercall allows a guest to explicitly transfer ownership of pages back to the host without requiring guest termination.
  • Memory access trapping to the host. Traditionally, if a KVM guest accesses an address that doesn't correspond to a valid memory region, then the vCPU thread exits to the host and the access is typically used for MMIO and emulated by the VMM in user space. To facilitate this handling, pKVM is required to advertise details about the faulting instruction such as its address, register parameters and potentially their contents back to the host, which could unintentionally expose sensitive data from a protected guest if the trap was not anticipated. pKVM solves this problem by treating these faults as fatal unless the guest has previously issued a hypercall to identify the faulting IPA range as one for which accesses are permitted to trap back to the host. This solution is referred to as the MMIO guard .

Virtual I/O device (virtio)

Virtio is a popular, portable, and mature standard for implementing and interacting with paravirtualized devices. The majority of devices exposed to protected guests are implemented using virtio. Virtio also underpins the vsock implementation used for communication between a protected guest and the rest of Android.

Virtio devices are typically implemented in the host's user space by the VMM, which intercepts trapped memory accesses from the guest to the MMIO interface of the virtio device and emulates the expected behavior. MMIO access is relatively expensive because each access to the device requires a round-trip to the VMM and back, so most of the actual data transfer between the device and guest occurs using a set of virtqueues in memory. A key assumption of virtio is that the host can access guest memory arbitrarily. This assumption is evident in the design of the virtqueue, which might contain pointers to buffers in the guest that the device emulation is intended to access directly.

Although the previously described memory sharing hypercalls could be used to share virtio data buffers from the guest to the host, this sharing is necessarily performed at page granularity and could end up exposing more data than required if the buffer size is less than that of a page. Instead, the guest is configured to allocate both the virtqueues and their corresponding data buffers from a fixed window of shared memory, with data being copied (bounced) to and from the window as required.

دستگاه مجازی

Figure 4. Virtio device

Interaction with TrustZone

Although guests aren't able to interact directly with TrustZone, the host must still be able to issue SMC calls into the secure world. These calls can specify physically addressed memory buffers that are inaccessible to the host. Because the secure software is generally unaware of the accessibility of the buffer, a malicious host could use this buffer to perform a confused deputy attack (analogous to a DMA attack). To prevent such attacks, pKVM traps all host SMC calls to EL2 and acts as a proxy between the host and the secure monitor at EL3.

PSCI calls from the host are forwarded to the EL3 firmware with minimal modifications. Specifically, the entry point for a CPU coming online or resuming from suspend is rewritten so that the stage 2 page table is installed at EL2 before returning to the host at EL1. During boot, this protection is enforced by pKVM.

This architecture relies on the SoC supporting PSCI, preferably through the use of an up-to-date version of TF-A as its EL3 firmware.

Firmware Framework for Arm (FF-A) standardizes interactions between the normal and secure worlds, particularly in the presence of a secure hypervisor. A major part of the specification defines a mechanism for sharing memory with the secure world, using both a common message format and a well-defined permissions model for the underlying pages. pKVM proxies FF-A messages to ensure that the host isn't attempting to share memory with the secure-side for which it doesn't have sufficient permissions.

This architecture relies on the secure world software enforcing the memory access model, to ensure that trusted apps and any other software running in the secure world can access memory only if it's either exclusively owned by the secure world or has been explicitly shared with it using FF-A. On a system with S-EL2, enforcing the memory access model should be done by a Secure Partition Manager Core (SPMC), such as Hafnium , which maintains stage 2 page tables for the secure world. On a system without S-EL2, the TEE can instead enforce a memory access model through its stage 1 page tables.

If the SMC call to EL2 isn't a PSCI call or FF-A defined message, unhandled SMCs are forwarded to EL3. The assumption is that the (necessarily trusted) secure firmware can handle unhandled SMCs safely because the firmware understands the precautions needed to maintain pVM isolation.

مانیتور ماشین مجازی

crosvm is a virtual machine monitor (VMM) which runs virtual machines through Linux's KVM interface. What makes crosvm unique is its focus on safety with the use of the Rust programming language and a sandbox around virtual devices to protect the host kernel. For more about crosvm, see its official documentation here .

File descriptors and ioctls

KVM exposes the /dev/kvm character device to userspace with ioctls that make up the KVM API. The ioctls belong to the following categories:

  • System ioctls query and set global attributes that affect the whole KVM subsystem, and create pVMs.
  • VM ioctls query and set attributes that create virtual CPUs (vCPUs) and devices, and affect an entire pVM, such as including memory layout and the number of virtual CPUs (vCPUs) and devices.
  • vCPU ioctls query and set attributes that control the operation of a single virtual CPU.
  • Device ioctls query and set attributes that control the operation of a single virtual device.

Each crosvm process runs exactly one instance of a virtual machine. This process uses the KVM_CREATE_VM system ioctl to create a VM file descriptor that can be used to issue pVM ioctls. A KVM_CREATE_VCPU or KVM_CREATE_DEVICE ioctl on a VM FD creates a vCPU/device and returns a file descriptor pointing to the new resource. ioctls on a vCPU or device FD can be used to control the device that was created using the ioctl on a VM FD. For vCPUs, this includes the important task of running guest code.

Internally, crosvm registers the VM's file descriptors with the kernel using the edge-triggered epoll interface. The kernel then notifies crosvm whenever there's a new event pending in any of the file descriptors.

pKVM adds a new capability, KVM_CAP_ARM_PROTECTED_VM , which can be used to get information about the pVM environment and set up protected mode for a VM. crosvm uses this during pVM creation if the --protected-vm flag is passed, to query and reserve the appropriate amount of memory for pVM firmware, and then to enable protected mode.

Memory allocation

One of the main responsibilities of a VMM is allocating the VM's memory and managing its memory layout. crosvm generates a fixed memory layout loosely described in the table below.

FDT in normal mode PHYS_MEMORY_END - 0x200000
فضای آزاد ...
رامدیسک ALIGN_UP(KERNEL_END, 0x1000000)
هسته 0x80080000
بوت لودر 0x80200000
FDT in BIOS mode 0x80000000
Physical memory base 0x80000000
pVM firmware 0x7FE00000
Device memory 0x10000 - 0x40000000

Physical memory is allocated with mmap and the memory is donated to the VM to populate its memory regions, called memslots , with the KVM_SET_USER_MEMORY_REGION ioctl. All guest pVM memory is therefore attributed to the crosvm instance that manages it and can result in the process being killed (terminating the VM) if the host starts running out of free memory. When a VM is stopped, the memory is automatically wiped by the hypervisor and returned to the host kernel.

Under regular KVM, the VMM retains access to all guest memory. With pKVM, guest memory is unmapped from the host physical address space when it's donated to the guest. The only exception is memory explicitly shared back by the guest, such as for virtio devices.

MMIO regions in the guest's address space are left unmapped. Access to these regions by the guest is trapped and results in an I/O event on the VM FD. This mechanism is used to implement virtual devices. In protected mode, the guest must acknowledge that a region of its address space is be used for MMIO using a hypercall, to reduce risk of accidental information leakage.

برنامه ریزی

Each virtual CPU is represented by a POSIX thread and scheduled by the host Linux scheduler. The thread calls the KVM_RUN ioctl on the vCPU FD, resulting in the hypervisor switching to the guest vCPU context. The host scheduler accounts for the time spent in a guest context as time used by the corresponding vCPU thread. KVM_RUN returns when there's an event that must be handled by the VMM, such as I/O, end of interrupt, or the vCPU halted. The VMM handles the event and calls KVM_RUN again.

During KVM_RUN , the thread remains preemptible by the host scheduler, except for execution of the EL2 hypervisor code, which isn't preemptible. The guest pVM itself has no mechanism for controlling this behavior.

Because all vCPU threads are scheduled like any other userspace tasks, they're subject to all standard QoS mechanisms. Specifically, each vCPU thread can be affined to physical CPUs, placed in cpusets, boosted or capped using utilization clamping, have their priority/scheduling policy changed, and more.

Virtual devices

crosvm supports a number of devices, including the following:

  • virtio-blk for composite disk images, read-only or read-write
  • vhost-vsock for communication with the host
  • virtio-pci as virtio transport
  • pl030 real time clock (RTC)
  • 16550a UART for serial communication

pVM firmware

The pVM firmware (pvmfw) is the first code executed by a pVM, similar to the boot ROM of a physical device. pvmfw's primary goal is to bootstrap secure boot and derive the pVM's unique secret. pvmfw isn't limited to use with any specific OS, such as Microdroid , as long as the OS is supported by crosvm and has been properly signed.

The pvmfw binary is stored in a flash partition of the same name and is updated using OTA .

Device boot

The following sequence of steps is added to the boot procedure of a pKVM-enabled device:

  1. The Android Bootloader (ABL) loads pvmfw from its partition into memory and verifies the image.
  2. The ABL obtains its Device Identifier Composition Engine (DICE) secrets (Compound Device Identifiers (CDIs) and DICE certificate chain) from a Root of Trust.
  3. The ABL derives the necessary CDIs for pvmfw, and appends them to the pvmfw binary.
  4. The ABL adds a linux,pkvm-guest-firmware-memory reserved memory region node to the DT, describing the location and size of the pvmfw binary and the secrets it derived in the previous step.
  5. The ABL hands control over to Linux and Linux initializes pKVM.
  6. pKVM unmaps pvmfw memory region from host's stage 2 page tables and protects it from the host (and guests) throughout device uptime.

After device boot, Microdroid is booted per the steps in the Boot sequence section of the Microdroid document.

pVM boot

When creating a pVM, crosvm (or another VMM) must create a sufficiently large memslot to be populated with the pvmfw image by the hypervisor. The VMM is also restricted in the list of registers whose initial value it can set (x0-x14 for the primary vCPU, none for secondary vCPUs). The remaining registers are reserved and are part of the hypervisor-pvmfw ABI.

When the pVM is run, the hypervisor first hands control of the primary vCPU over to pvmfw. The firmware expects crosvm to have loaded an AVB-signed kernel, which can be a bootloader or any other image, and an unsigned FDT to memory at known offsets. pvmfw validates the AVB signature and, if successful, generates a trusted device tree from the received FDT, wipes its secrets from memory, and branches to the entry point of the payload. If one of the verification steps fails, the firmware issues a PSCI SYSTEM_RESET hypercall.

Between boots, information about the pVM instance is stored in a partition (virtio-blk device) and encrypted with pvmfw's secret to ensure that, following a reboot, the secret is being provisioned to the correct instance.

،

Android provides a reference implementation of all the components needed to implement the Android Virtualization Framework. Currently this implementation is limited to ARM64. This page explains the framework architecture.

پس زمینه

The Arm architecture allows up to four exception levels, with exception level 0 (EL0) being the least privileged, and exception level 3 (EL3) the most. The largest portion of the Android codebase (all userspace components) runs at EL0. The rest of what is commonly called "Android" is the Linux kernel, which runs at EL1.

The EL2 layer allows the introduction of a hypervisor that enables isolating memory and devices into individual pVMs at EL1/EL0, with strong confidentiality and integrity guarantees.

هایپروایزر

The protected kernel-based virtual machine (pKVM) is built upon the Linux KVM hypervisor , which has been extended with the ability to restrict access to the payloads running in guest virtual machines marked 'protected' at the time of creation.

KVM/arm64 supports different execution modes depending on the availability of certain CPU features, namely, the Virtualization Host Extensions (VHE) (ARMv8.1 and later). In one of those modes, commonly known as the non-VHE mode, the hypervisor code is split out of the kernel image during boot and installed at EL2, whereas the kernel itself runs at EL1. Although part of the Linux codebase, the EL2 component of KVM is a small component in charge of the switch between multiple EL1s. The hypervisor component is compiled with Linux, but resides in a separate, dedicated memory section of the vmlinux image. pKVM leverages this design by extending the hypervisor code with new features allowing it to put restrictions on the Android host kernel and user space, and limiting host access to guest memory and the hypervisor.

pKVM vendor modules

A pKVM vendor module is a hardware-specific module containing device-specific functionality, such as input-output memory management unit (IOMMU) drivers. These modules let you port security features requiring exception level 2 (EL2) access to pKVM.

To learn how to implement and load a pKVM vendor module, refer to Implement a pKVM vendor module .

رویه بوت

The following figure depicts the pKVM boot procedure:

pKVM boot procedure

Figure 1. pKVM boot procedure

  1. The bootloader enters the generic kernel at EL2.
  2. The generic kernel detects that it's running at EL2 and deprivileges itself to EL1 while pKVM and its modules continue to run at EL2. Additionally, pKVM vendor modules are loaded at this time.
  3. The generic kernel proceeds to boot normally, loading all necessary device drivers until reaching user space. At this point, pKVM is in place and handles the stage-2 page tables.

The boot procedure trusts the bootloader to maintain the integrity of the kernel image only during early boot. When the kernel is deprivileged, it's no longer considered trusted by the hypervisor, which is then responsible for protecting itself even if the kernel is compromised.

Having the Android kernel and the hypervisor in the same binary image allows for a very tightly coupled communication interface between them. This tight coupling guarantees atomic updates of the two components, which avoids the need to keep the interface between them stable, and offers a great deal of flexibility without compromising long-term maintainability. The tight coupling also allows performance optimizations when both components can cooperate without impacting the security guarantees provided by the hypervisor.

Moreover, the adoption of GKI in the Android ecosystem automatically allows the pKVM hypervisor to be deployed to Android devices in the same binary as the kernel.

CPU memory access protection

The Arm architecture specifies a memory management unit (MMU) split in two independent stages, both of which can be used to implement address translation and access control to different parts of memory. The stage 1 MMU is controlled by EL1 and allows a first level of address translation. The stage 1 MMU is used by Linux to manage the virtual address space provided to each userspace process and to its own virtual address space.

The stage 2 MMU is controlled by EL2 and enables the application of a second address translation on the output address of the stage 1 MMU, resulting in a physical address (PA). The stage 2 translation can be used by hypervisors to control and translate memory accesses from all guest VMs. As shown in figure 2, when both stages of translation are enabled, the output address of the stage 1 is called an intermediate physical address (IPA) Note: The virtual address (VA) is translated into an IPA and then to a PA.

CPU memory access protection

Figure 2. CPU memory access protection

Historically, KVM runs with stage 2 translation enabled while running guests and with stage 2 disabled while running the host Linux kernel. This architecture allows memory accesses from the host stage 1 MMU to pass through the stage 2 MMU, hence allowing unrestricted access from the host to guest memory pages. On the other hand, pKVM enables stage 2 protection even in host context, and puts the hypervisor in charge of protecting guest memory pages instead of the host.

KVM makes full use of address translation at stage 2 to implement complex IPA/PA mappings for guests, which creates the illusion of contiguous memory for guests despite physical fragmentation. However, the usage of the stage 2 MMU for the host is restricted to access control only. The host stage 2 is identity-mapped, ensuring that contiguous memory in the host IPA space is contiguous in the PA space. This architecture allows the use of large mappings in the page table and consequently reduces pressure on the translation lookaside buffer (TLB). Because an identity mapping can be indexed by PA, the host stage 2 is also used to track page ownership directly in the page table.

Direct memory access (DMA) protection

As described previously, unmapping guest pages from the Linux host in the CPU page tables is a necessary but insufficient step for protecting guest memory. pKVM also needs to protect against memory accesses made by DMA-capable devices under the host kernel's control, and the possibility of a DMA attack initiated by a malicious host. To prevent such a device from accessing guest memory, pKVM requires input-output memory management unit (IOMMU) hardware for every DMA-capable device in the system, as shown in figure 3.

Dma memory access protection

Figure 3. DMA memory access protection

At a minimum, IOMMU hardware provides the means of granting and revoking read/write access for a device to physical memory at page granularity. However, this IOMMU hardware limits the use of devices in pVMs as they assume an identity-mapped stage 2.

To ensure isolation between virtual machines, memory transactions generated on behalf of different entities must be distinguishable by the IOMMU so that the appropriate set of page tables can be used for the translation.

In addition, reducing the amount of SoC-specific code at EL2 is a key strategy to reduce the overall trusted computing base (TCB) of pKVM and runs counter to the inclusion of IOMMU drivers in the hypervisor. To mitigate this issue, the host at EL1 is responsible for auxiliary IOMMU management tasks, such as power management, initialization and, where appropriate, interrupt handling.

However, putting the host in control of the device state places additional requirements on the programming interface of the IOMMU hardware to ensure that permission checks can't be bypassed by other means, for example, following a device reset.

A standard and well supported IOMMU for Arm devices that makes both isolation and direct assignment possible is the Arm System Memory Management Unit (SMMU) architecture. This architecture is the recommended reference solution.

Memory ownership

At boot time, all non-hypervisor memory is assumed to be owned by the host, and is tracked as such by the hypervisor. When a pVM is spawned, the host donates memory pages to allow it to boot and the hypervisor transitions the ownership of those pages from the host to the pVM. Thus, the hypervisor puts access-control restrictions in place in the host's stage 2 page table to prevent it from accessing the pages again, providing confidentiality to the guest.

Communication between the host and guests is made possible by controlled memory sharing between them. Guests are allowed to share some of their pages back with the host using a hypercall, which instructs the hypervisor to remap those pages in the host stage 2 page table. Similarly, the host's communication with TrustZone is made possible by memory sharing and/or lending operations, all of which are closely monitored and controlled by pKVM using the Firmware Framework for Arm (FF-A) specification .

Since a pVM's memory requirements can change over time, a hypercall is provided which allows ownership of specified pages belonging to the caller to be relinquished back to the host. In practice this hypercall is used with the virtio balloon protocol to allow the VMM to request memory back from the pVM, and for the pVM to notify the VMM of relinquished pages, in a controlled manner.

The hypervisor is responsible for tracking ownership of all memory pages in the system and whether they're being shared or lent to other entities. Most of this state tracking is done using metadata attached to the host and guests' stage 2 page tables, using reserved bits in the page table entries (PTEs) which, as their name suggests, are reserved for software use.

The host must ensure that it doesn't attempt to access pages that have been made inaccessible by the hypervisor. An illegal host access causes a synchronous exception to be injected into the host by the hypervisor, which can either result in the responsible userspace task receiving a SEGV signal, or the host kernel crashing. To prevent accidental accesses, pages donated to guests are made ineligible for swapping or merging by the host kernel.

Interrupt handling and timers

Interrupts are an essential part of the way a guest interacts with devices and for communication between CPUs, where interprocessor interrupts (IPIs) are the main communication mechanism. The KVM model is to delegate all the virtual interrupt management to the host in EL1, which for that purpose behaves as an untrusted part of the hypervisor.

pKVM offers a full Generic Interrupt Controller version 3 (GICv3) emulation based on the existing KVM code. Timer and IPIs are handled as part of this untrusted emulation code.

GICv3 support

The interface between EL1 and EL2 must ensure that the full interrupt state is visible to the EL1 host, including copies of the hypervisor registers related to interrupts. This visibility is typically accomplished using shared memory regions, one per virtual CPU (vCPU).

The system register runtime support code can be simplified to support only the Software Generated Interrupt Register (SGIR) and Deactivate Interrupt Register (DIR) register trapping. The architecture mandates that these registers always trap to EL2, while the other traps have so far only been useful to mitigate errata. Everything else is being handled in hardware.

On the MMIO side, everything is emulated at EL1, reusing all the current infrastructure in KVM. Finally, Wait for Interrupt (WFI) is always relayed to EL1, because this is one of the basic scheduling primitives KVM uses.

Timer support

The comparator value for the virtual timer must be exposed to EL1 on each trapping WFI so that EL1 can inject timer interrupts while the vCPU is blocked. The physical timer is entirely emulated, and all traps relayed to EL1.

MMIO handling

To communicate with the virtual machine monitor (VMM) and perform GIC emulation, MMIO traps must be relayed back to the host in EL1 for further triaging. pKVM requires the following:

  • IPA and size of the access
  • Data in case of a write
  • Endianness of the CPU at the point of trapping

Additionally, traps with a general purpose register (GPR) as a source/destination are relayed using an abstract transfer pseudo-register.

Guest interfaces

A guest can communicate with a protected guest using a combination of hypercalls and memory access to trapped regions. Hypercalls are exposed according to the SMCCC standard , with a range reserved for a vendor allocation by KVM. The following hypercalls are of particular importance to pKVM guests.

Generic hypercalls

  • PSCI provides a standard mechanism for the guest to control the lifecycle of its vCPUs including onlining, offlining, and system shutdown.
  • TRNG provides a standard mechanism for the guest to request entropy from the pKVM which relays the call to EL3. This mechanism is particularly useful where the host can't be trusted to virtualize a hardware random number generator (RNG).

pKVM hypercalls

  • Memory sharing with the host. All guest memory is initially inaccessible to the host, but host access is necessary for shared-memory communication and for paravirtualized devices that rely on shared buffers. Hypercalls for sharing and unsharing pages with the host allow the guest to decide exactly what parts of memory are made accessible to the rest of Android without the need for a handshake.
  • Memory relinquishment to the host. All guest memory usually belongs to the guest until it is destroyed. This state can be inadequate for long-lived VMs with memory requirements which vary over time. The relinquish hypercall allows a guest to explicitly transfer ownership of pages back to the host without requiring guest termination.
  • Memory access trapping to the host. Traditionally, if a KVM guest accesses an address that doesn't correspond to a valid memory region, then the vCPU thread exits to the host and the access is typically used for MMIO and emulated by the VMM in user space. To facilitate this handling, pKVM is required to advertise details about the faulting instruction such as its address, register parameters and potentially their contents back to the host, which could unintentionally expose sensitive data from a protected guest if the trap was not anticipated. pKVM solves this problem by treating these faults as fatal unless the guest has previously issued a hypercall to identify the faulting IPA range as one for which accesses are permitted to trap back to the host. This solution is referred to as the MMIO guard .

Virtual I/O device (virtio)

Virtio is a popular, portable, and mature standard for implementing and interacting with paravirtualized devices. The majority of devices exposed to protected guests are implemented using virtio. Virtio also underpins the vsock implementation used for communication between a protected guest and the rest of Android.

Virtio devices are typically implemented in the host's user space by the VMM, which intercepts trapped memory accesses from the guest to the MMIO interface of the virtio device and emulates the expected behavior. MMIO access is relatively expensive because each access to the device requires a round-trip to the VMM and back, so most of the actual data transfer between the device and guest occurs using a set of virtqueues in memory. A key assumption of virtio is that the host can access guest memory arbitrarily. This assumption is evident in the design of the virtqueue, which might contain pointers to buffers in the guest that the device emulation is intended to access directly.

Although the previously described memory sharing hypercalls could be used to share virtio data buffers from the guest to the host, this sharing is necessarily performed at page granularity and could end up exposing more data than required if the buffer size is less than that of a page. Instead, the guest is configured to allocate both the virtqueues and their corresponding data buffers from a fixed window of shared memory, with data being copied (bounced) to and from the window as required.

دستگاه مجازی

Figure 4. Virtio device

Interaction with TrustZone

Although guests aren't able to interact directly with TrustZone, the host must still be able to issue SMC calls into the secure world. These calls can specify physically addressed memory buffers that are inaccessible to the host. Because the secure software is generally unaware of the accessibility of the buffer, a malicious host could use this buffer to perform a confused deputy attack (analogous to a DMA attack). To prevent such attacks, pKVM traps all host SMC calls to EL2 and acts as a proxy between the host and the secure monitor at EL3.

PSCI calls from the host are forwarded to the EL3 firmware with minimal modifications. Specifically, the entry point for a CPU coming online or resuming from suspend is rewritten so that the stage 2 page table is installed at EL2 before returning to the host at EL1. During boot, this protection is enforced by pKVM.

This architecture relies on the SoC supporting PSCI, preferably through the use of an up-to-date version of TF-A as its EL3 firmware.

Firmware Framework for Arm (FF-A) standardizes interactions between the normal and secure worlds, particularly in the presence of a secure hypervisor. A major part of the specification defines a mechanism for sharing memory with the secure world, using both a common message format and a well-defined permissions model for the underlying pages. pKVM proxies FF-A messages to ensure that the host isn't attempting to share memory with the secure-side for which it doesn't have sufficient permissions.

This architecture relies on the secure world software enforcing the memory access model, to ensure that trusted apps and any other software running in the secure world can access memory only if it's either exclusively owned by the secure world or has been explicitly shared with it using FF-A. On a system with S-EL2, enforcing the memory access model should be done by a Secure Partition Manager Core (SPMC), such as Hafnium , which maintains stage 2 page tables for the secure world. On a system without S-EL2, the TEE can instead enforce a memory access model through its stage 1 page tables.

If the SMC call to EL2 isn't a PSCI call or FF-A defined message, unhandled SMCs are forwarded to EL3. The assumption is that the (necessarily trusted) secure firmware can handle unhandled SMCs safely because the firmware understands the precautions needed to maintain pVM isolation.

مانیتور ماشین مجازی

crosvm is a virtual machine monitor (VMM) which runs virtual machines through Linux's KVM interface. What makes crosvm unique is its focus on safety with the use of the Rust programming language and a sandbox around virtual devices to protect the host kernel. For more about crosvm, see its official documentation here .

File descriptors and ioctls

KVM exposes the /dev/kvm character device to userspace with ioctls that make up the KVM API. The ioctls belong to the following categories:

  • System ioctls query and set global attributes that affect the whole KVM subsystem, and create pVMs.
  • VM ioctls query and set attributes that create virtual CPUs (vCPUs) and devices, and affect an entire pVM, such as including memory layout and the number of virtual CPUs (vCPUs) and devices.
  • vCPU ioctls query and set attributes that control the operation of a single virtual CPU.
  • Device ioctls query and set attributes that control the operation of a single virtual device.

Each crosvm process runs exactly one instance of a virtual machine. This process uses the KVM_CREATE_VM system ioctl to create a VM file descriptor that can be used to issue pVM ioctls. A KVM_CREATE_VCPU or KVM_CREATE_DEVICE ioctl on a VM FD creates a vCPU/device and returns a file descriptor pointing to the new resource. ioctls on a vCPU or device FD can be used to control the device that was created using the ioctl on a VM FD. For vCPUs, this includes the important task of running guest code.

Internally, crosvm registers the VM's file descriptors with the kernel using the edge-triggered epoll interface. The kernel then notifies crosvm whenever there's a new event pending in any of the file descriptors.

pKVM adds a new capability, KVM_CAP_ARM_PROTECTED_VM , which can be used to get information about the pVM environment and set up protected mode for a VM. crosvm uses this during pVM creation if the --protected-vm flag is passed, to query and reserve the appropriate amount of memory for pVM firmware, and then to enable protected mode.

Memory allocation

One of the main responsibilities of a VMM is allocating the VM's memory and managing its memory layout. crosvm generates a fixed memory layout loosely described in the table below.

FDT in normal mode PHYS_MEMORY_END - 0x200000
فضای آزاد ...
رامدیسک ALIGN_UP(KERNEL_END, 0x1000000)
هسته 0x80080000
بوت لودر 0x80200000
FDT in BIOS mode 0x80000000
Physical memory base 0x80000000
pVM firmware 0x7FE00000
Device memory 0x10000 - 0x40000000

Physical memory is allocated with mmap and the memory is donated to the VM to populate its memory regions, called memslots , with the KVM_SET_USER_MEMORY_REGION ioctl. All guest pVM memory is therefore attributed to the crosvm instance that manages it and can result in the process being killed (terminating the VM) if the host starts running out of free memory. When a VM is stopped, the memory is automatically wiped by the hypervisor and returned to the host kernel.

Under regular KVM, the VMM retains access to all guest memory. With pKVM, guest memory is unmapped from the host physical address space when it's donated to the guest. The only exception is memory explicitly shared back by the guest, such as for virtio devices.

MMIO regions in the guest's address space are left unmapped. Access to these regions by the guest is trapped and results in an I/O event on the VM FD. This mechanism is used to implement virtual devices. In protected mode, the guest must acknowledge that a region of its address space is be used for MMIO using a hypercall, to reduce risk of accidental information leakage.

برنامه ریزی

Each virtual CPU is represented by a POSIX thread and scheduled by the host Linux scheduler. The thread calls the KVM_RUN ioctl on the vCPU FD, resulting in the hypervisor switching to the guest vCPU context. The host scheduler accounts for the time spent in a guest context as time used by the corresponding vCPU thread. KVM_RUN returns when there's an event that must be handled by the VMM, such as I/O, end of interrupt, or the vCPU halted. The VMM handles the event and calls KVM_RUN again.

During KVM_RUN , the thread remains preemptible by the host scheduler, except for execution of the EL2 hypervisor code, which isn't preemptible. The guest pVM itself has no mechanism for controlling this behavior.

Because all vCPU threads are scheduled like any other userspace tasks, they're subject to all standard QoS mechanisms. Specifically, each vCPU thread can be affined to physical CPUs, placed in cpusets, boosted or capped using utilization clamping, have their priority/scheduling policy changed, and more.

Virtual devices

crosvm supports a number of devices, including the following:

  • virtio-blk for composite disk images, read-only or read-write
  • vhost-vsock for communication with the host
  • virtio-pci as virtio transport
  • pl030 real time clock (RTC)
  • 16550a UART for serial communication

pVM firmware

The pVM firmware (pvmfw) is the first code executed by a pVM, similar to the boot ROM of a physical device. pvmfw's primary goal is to bootstrap secure boot and derive the pVM's unique secret. pvmfw isn't limited to use with any specific OS, such as Microdroid , as long as the OS is supported by crosvm and has been properly signed.

The pvmfw binary is stored in a flash partition of the same name and is updated using OTA .

Device boot

The following sequence of steps is added to the boot procedure of a pKVM-enabled device:

  1. The Android Bootloader (ABL) loads pvmfw from its partition into memory and verifies the image.
  2. The ABL obtains its Device Identifier Composition Engine (DICE) secrets (Compound Device Identifiers (CDIs) and DICE certificate chain) from a Root of Trust.
  3. The ABL derives the necessary CDIs for pvmfw, and appends them to the pvmfw binary.
  4. The ABL adds a linux,pkvm-guest-firmware-memory reserved memory region node to the DT, describing the location and size of the pvmfw binary and the secrets it derived in the previous step.
  5. The ABL hands control over to Linux and Linux initializes pKVM.
  6. pKVM unmaps pvmfw memory region from host's stage 2 page tables and protects it from the host (and guests) throughout device uptime.

After device boot, Microdroid is booted per the steps in the Boot sequence section of the Microdroid document.

pVM boot

When creating a pVM, crosvm (or another VMM) must create a sufficiently large memslot to be populated with the pvmfw image by the hypervisor. The VMM is also restricted in the list of registers whose initial value it can set (x0-x14 for the primary vCPU, none for secondary vCPUs). The remaining registers are reserved and are part of the hypervisor-pvmfw ABI.

When the pVM is run, the hypervisor first hands control of the primary vCPU over to pvmfw. The firmware expects crosvm to have loaded an AVB-signed kernel, which can be a bootloader or any other image, and an unsigned FDT to memory at known offsets. pvmfw validates the AVB signature and, if successful, generates a trusted device tree from the received FDT, wipes its secrets from memory, and branches to the entry point of the payload. If one of the verification steps fails, the firmware issues a PSCI SYSTEM_RESET hypercall.

Between boots, information about the pVM instance is stored in a partition (virtio-blk device) and encrypted with pvmfw's secret to ensure that, following a reboot, the secret is being provisioned to the correct instance.