Android cung cấp cách triển khai tham chiếu của tất cả các thành phần cần thiết để triển khai Khung ảo hoá Android. Hiện tại, phương thức triển khai này chỉ giới hạn ở ARM64. Trang này giải thích cấu trúc khung.
Thông tin khái quát
Cấu trúc Arm cho phép tối đa 4 cấp độ ngoại lệ, trong đó cấp độ ngoại lệ 0 (EL0) có đặc quyền thấp nhất và cấp độ ngoại lệ 3 (EL3) có đặc quyền cao nhất. Phần lớn nhất của cơ sở mã Android (tất cả thành phần của không gian người dùng) chạy ở EL0. Phần còn lại thường được gọi là "Android" là nhân Linux, chạy ở EL1.
Lớp EL2 cho phép giới thiệu một trình điều khiển ảo hoá cho phép tách biệt bộ nhớ và thiết bị thành các pVM riêng lẻ ở EL1/EL0, với tính bảo mật và tính toàn vẹn được đảm bảo mạnh mẽ.
Trình điều khiển ảo hoá
Máy ảo dựa trên nhân hệ điều hành được bảo vệ (pKVM) được xây dựng dựa trên trình điều khiển ảo hoá KVM Linux. Trình điều khiển này đã được mở rộng với khả năng hạn chế quyền truy cập vào các tải trọng chạy trong máy ảo khách được đánh dấu là "được bảo vệ" tại thời điểm tạo.
KVM/arm64 hỗ trợ nhiều chế độ thực thi tuỳ thuộc vào việc có sẵn một số tính năng CPU nhất định, cụ thể là Tiện ích máy chủ ảo hoá (VHE) (ARMv8.1 trở lên). Trong một trong những chế độ đó, thường được gọi là chế độ không phải VHE, mã trình điều khiển ảo hoá được tách ra khỏi hình ảnh hạt nhân trong quá trình khởi động và được cài đặt tại EL2, trong khi chính hạt nhân chạy ở EL1. Mặc dù là một phần của cơ sở mã Linux, nhưng thành phần EL2 của KVM là một thành phần nhỏ chịu trách nhiệm chuyển đổi giữa nhiều EL1. Thành phần trình điều khiển ảo hoá được biên dịch bằng Linux, nhưng nằm trong một phần bộ nhớ riêng biệt, chuyên dụng của hình ảnh vmlinux
. pKVM tận dụng thiết kế này bằng cách mở rộng mã trình điều khiển ảo hoá bằng các tính năng mới cho phép đặt các quy tắc hạn chế đối với hạt nhân máy chủ và không gian người dùng Android, đồng thời giới hạn quyền truy cập của máy chủ vào bộ nhớ khách và trình điều khiển ảo hoá.
Mô-đun nhà cung cấp pKVM
Mô-đun nhà cung cấp pKVM là mô-đun dành riêng cho phần cứng chứa chức năng dành riêng cho thiết bị, chẳng hạn như trình điều khiển đơn vị quản lý bộ nhớ đầu vào-đầu ra (IOMMU). Các mô-đun này cho phép bạn chuyển các tính năng bảo mật yêu cầu quyền truy cập ngoại lệ cấp 2 (EL2) sang pKVM.
Để tìm hiểu cách triển khai và tải mô-đun nhà cung cấp pKVM, hãy tham khảo phần Triển khai mô-đun nhà cung cấp pKVM.
Quy trình khởi động
Hình sau đây mô tả quy trình khởi động pKVM:
- Trình tải khởi động sẽ chuyển sang hạt nhân chung ở EL2.
- Hạt nhân chung phát hiện rằng hạt nhân này đang chạy ở EL2 và tự giảm đặc quyền xuống EL1 trong khi pKVM và các mô-đun của nó tiếp tục chạy ở EL2. Ngoài ra, các mô-đun nhà cung cấp pKVM cũng được tải tại thời điểm này.
- Hạt nhân chung sẽ tiếp tục khởi động bình thường, tải tất cả trình điều khiển thiết bị cần thiết cho đến khi đạt không gian của người dùng. Tại thời điểm này, pKVM đã sẵn sàng và xử lý các bảng trang giai đoạn 2.
Quy trình khởi động chỉ tin tưởng trình tải khởi động để duy trì tính toàn vẹn của hình ảnh hạt nhân trong quá trình khởi động ban đầu. Khi bị giảm đặc quyền, nhân hệ điều hành sẽ không còn được trình điều khiển ảo hoá coi là đáng tin cậy. Sau đó, trình điều khiển ảo hoá sẽ chịu trách nhiệm tự bảo vệ ngay cả khi nhân hệ điều hành bị xâm phạm.
Việc có nhân Android và trình điều khiển ảo hoá trong cùng một hình ảnh nhị phân cho phép giao diện giao tiếp được ghép nối rất chặt chẽ giữa chúng. Mối ghép chặt chẽ này đảm bảo việc cập nhật nguyên tử của hai thành phần, giúp tránh việc cần phải duy trì giao diện giữa các thành phần đó ở trạng thái ổn định và mang lại nhiều tính linh hoạt mà không ảnh hưởng đến khả năng bảo trì lâu dài. Việc ghép nối chặt chẽ cũng cho phép tối ưu hoá hiệu suất khi cả hai thành phần có thể hợp tác mà không ảnh hưởng đến các đảm bảo bảo mật do trình điều khiển ảo hoá cung cấp.
Hơn nữa, việc sử dụng GKI trong hệ sinh thái Android sẽ tự động cho phép triển khai trình điều khiển ảo hoá pKVM cho các thiết bị Android trong cùng một tệp nhị phân với hạt nhân.
Bảo vệ quyền truy cập vào bộ nhớ CPU
Kiến trúc Arm chỉ định một đơn vị quản lý bộ nhớ (MMU) được chia thành hai giai đoạn độc lập, cả hai giai đoạn đều có thể dùng để triển khai dịch địa chỉ và kiểm soát quyền truy cập vào các phần khác nhau của bộ nhớ. MMU giai đoạn 1 do EL1 kiểm soát và cho phép dịch địa chỉ cấp đầu tiên. MMU giai đoạn 1 được Linux sử dụng để quản lý không gian địa chỉ ảo được cung cấp cho mỗi quy trình không gian người dùng và cho không gian địa chỉ ảo của riêng quy trình đó.
MMU giai đoạn 2 do EL2 kiểm soát và cho phép áp dụng một bản dịch địa chỉ thứ hai trên địa chỉ đầu ra của MMU giai đoạn 1, dẫn đến một địa chỉ thực (PA). Trình điều khiển ảo hoá có thể sử dụng bản dịch giai đoạn 2 để kiểm soát và dịch các quyền truy cập bộ nhớ từ tất cả máy ảo khách. Như minh hoạ trong hình 2, khi cả hai giai đoạn dịch đều được bật, địa chỉ đầu ra của giai đoạn 1 được gọi là địa chỉ vật lý trung gian (IPA). Lưu ý: Địa chỉ ảo (VA) được dịch thành IPA rồi thành PA.
Trước đây, KVM chạy với tính năng dịch giai đoạn 2 được bật khi chạy máy khách và tắt giai đoạn 2 khi chạy nhân Linux của máy chủ. Cấu trúc này cho phép các hoạt động truy cập bộ nhớ từ MMU giai đoạn 1 của máy chủ đi qua MMU giai đoạn 2, do đó cho phép truy cập không hạn chế từ máy chủ đến các trang bộ nhớ của khách. Mặt khác, pKVM cho phép bảo vệ giai đoạn 2 ngay cả trong ngữ cảnh máy chủ và giao cho trình điều khiển ảo hoá chịu trách nhiệm bảo vệ các trang bộ nhớ của khách thay vì máy chủ.
KVM tận dụng tối đa tính năng dịch địa chỉ ở giai đoạn 2 để triển khai ánh xạ IPA/PA phức tạp cho khách, tạo ảo giác về bộ nhớ liền kề cho khách bất kể sự phân mảnh vật lý. Tuy nhiên, việc sử dụng MMU giai đoạn 2 cho máy chủ chỉ bị hạn chế ở việc kiểm soát quyền truy cập. Giai đoạn 2 của máy chủ được ánh xạ theo danh tính, đảm bảo rằng bộ nhớ liền kề trong không gian IPA của máy chủ liền kề trong không gian PA. Cấu trúc này cho phép sử dụng các ánh xạ lớn trong bảng trang và do đó làm giảm áp lực lên vùng đệm xem trước bản dịch (TLB). Vì PA có thể lập chỉ mục mối liên kết danh tính, nên máy chủ lưu trữ giai đoạn 2 cũng được dùng để theo dõi quyền sở hữu trang ngay trong bảng trang.
Bảo vệ truy cập bộ nhớ trực tiếp (DMA)
Như đã mô tả trước đó, việc huỷ ánh xạ các trang khách khỏi máy chủ Linux trong bảng trang CPU là cần thiết nhưng không đủ để bảo vệ bộ nhớ khách. pKVM cũng cần bảo vệ khỏi hoạt động truy cập vào bộ nhớ do các thiết bị có hỗ trợ DMA thực hiện dưới sự kiểm soát của nhân máy chủ và khả năng tấn công theo DMA do máy chủ độc hại khởi tạo. Để ngăn thiết bị như vậy truy cập vào bộ nhớ khách, pKVM yêu cầu phần cứng đơn vị quản lý bộ nhớ đầu vào-đầu ra (IOMMU) cho mọi thiết bị có thể sử dụng DMA trong hệ thống, như minh hoạ trong hình 3.
Ít nhất, phần cứng IOMMU cung cấp phương thức cấp và thu hồi quyền đọc/ghi cho thiết bị vào bộ nhớ vật lý ở mức độ chi tiết theo trang. Tuy nhiên, phần cứng IOMMU này giới hạn việc sử dụng các thiết bị trong pVM vì chúng giả định một giai đoạn 2 được ánh xạ danh tính.
Để đảm bảo tính tách biệt giữa các máy ảo, IOMMU phải phân biệt được các giao dịch bộ nhớ được tạo thay mặt cho các thực thể khác nhau để có thể sử dụng tập hợp bảng trang thích hợp cho việc dịch.
Ngoài ra, giảm số lượng mã dành riêng cho SoC ở EL2 là một chiến lược chính để giảm cơ sở điện toán đáng tin cậy (TCB) tổng thể của pKVM và chạy bộ đếm ngược với việc đưa các trình điều khiển IOMMU vào trình điều khiển ảo hoá. Để giảm thiểu vấn đề này, máy chủ tại EL1 chịu trách nhiệm về các tác vụ quản lý IOMMU phụ trợ, chẳng hạn như quản lý nguồn, khởi chạy và xử lý ngắt (nếu thích hợp).
Tuy nhiên, việc đặt máy chủ lưu trữ kiểm soát trạng thái thiết bị sẽ đặt thêm các yêu cầu đối với giao diện lập trình của phần cứng IOMMU để đảm bảo rằng các phương thức khác không thể bỏ qua việc kiểm tra quyền, chẳng hạn như sau khi đặt lại thiết bị.
Một IOMMU tiêu chuẩn và được hỗ trợ tốt cho các thiết bị Arm giúp khả năng tách biệt và chỉ định trực tiếp là cấu trúc Bộ quản lý bộ nhớ hệ thống (SMMU) của Arm. Cấu trúc này là giải pháp tham chiếu được đề xuất.
Quyền sở hữu bộ nhớ
Tại thời điểm khởi động, tất cả bộ nhớ không phải của trình điều khiển ảo hoá được giả định là thuộc sở hữu của máy chủ và được trình điều khiển ảo hoá theo dõi như vậy. Khi một pVM được tạo, máy chủ lưu trữ sẽ đóng góp các trang bộ nhớ để cho phép khởi động và trình điều khiển ảo hoá sẽ chuyển đổi quyền sở hữu của các trang đó từ máy chủ lưu trữ sang pVM. Do đó, trình điều khiển ảo hoá sẽ áp dụng các quy tắc hạn chế kiểm soát quyền truy cập trong bảng trang giai đoạn 2 của máy chủ để ngăn máy chủ truy cập lại vào các trang, đảm bảo tính bảo mật cho máy khách.
Máy chủ và khách có thể giao tiếp thông qua hoạt động chia sẻ bộ nhớ có kiểm soát giữa họ. Khách được phép chia sẻ một số trang của họ với máy chủ lưu trữ bằng cách sử dụng lệnh gọi siêu dữ liệu. Lệnh này sẽ hướng dẫn trình điều khiển ảo hoá ánh xạ lại các trang đó trong bảng trang giai đoạn 2 của máy chủ lưu trữ. Tương tự, máy chủ lưu trữ có thể giao tiếp với TrustZone qua các hoạt động chia sẻ và/hoặc cho mượn bộ nhớ, tất cả đều được pKVM giám sát và kiểm soát chặt chẽ thông qua quy cách của Khung chương trình cơ sở cho Arm (FF-A).
Vì các yêu cầu về bộ nhớ của pVM có thể thay đổi theo thời gian, nên một lệnh gọi nhanh được cung cấp cho phép quyền sở hữu các trang được chỉ định thuộc về phương thức gọi bị từ bỏ về máy chủ. Trong thực tế, lệnh gọi siêu dữ liệu này được sử dụng với giao thức bóng bay virtio để cho phép VMM yêu cầu bộ nhớ trở lại từ pVM và để pVM thông báo cho VMM về các trang đã từ bỏ theo cách có kiểm soát.
Trình điều khiển ảo hoá chịu trách nhiệm theo dõi quyền sở hữu của tất cả các trang bộ nhớ trong hệ thống và liệu các trang đó có đang được chia sẻ hay cho các thực thể khác mượn hay không. Hầu hết quá trình theo dõi trạng thái này được thực hiện bằng siêu dữ liệu đính kèm vào bảng trang giai đoạn 2 của máy chủ và khách, sử dụng các bit dành riêng trong các mục nhập bảng trang (PTE) được dành riêng cho mục đích sử dụng phần mềm.
Máy chủ lưu trữ phải đảm bảo rằng máy chủ lưu trữ không cố gắng truy cập vào các trang mà trình điều khiển ảo hoá đã chặn truy cập. Việc truy cập trái phép vào máy chủ sẽ khiến trình điều khiển ảo hoá chèn một ngoại lệ đồng bộ vào máy chủ. Điều này có thể dẫn đến việc tác vụ không gian người dùng chịu trách nhiệm nhận tín hiệu SEGV hoặc hạt nhân máy chủ gặp sự cố. Để ngăn chặn việc truy cập vô tình, các trang được tặng cho khách sẽ không đủ điều kiện để hoán đổi hoặc hợp nhất theo hạt nhân máy chủ.
Xử lý ngắt và bộ hẹn giờ
Các ngắt là một phần thiết yếu trong cách khách tương tác với các thiết bị và để giao tiếp giữa các CPU, trong đó các ngắt liên xử lý (IPI) là cơ chế giao tiếp chính. Mô hình KVM là uỷ quyền tất cả hoạt động quản lý ngắt ảo cho máy chủ trong EL1. Đối với mục đích đó, máy chủ hoạt động như một phần không đáng tin cậy của trình điều khiển ảo hoá.
pKVM cung cấp một bản mô phỏng đầy đủ của Bộ điều khiển ngắt chung phiên bản 3 (GICv3) dựa trên mã KVM hiện có. Bộ hẹn giờ và IPI được xử lý trong mã mô phỏng không đáng tin cậy này.
Hỗ trợ GICv3
Giao diện giữa EL1 và EL2 phải đảm bảo máy chủ EL1 nhìn thấy toàn bộ trạng thái gián đoạn, bao gồm cả các bản sao của thanh ghi trình điều khiển ảo hoá liên quan đến các lượt ngắt. Khả năng hiển thị này thường được thực hiện bằng cách sử dụng các vùng bộ nhớ dùng chung, mỗi vùng cho một CPU ảo (vCPU).
Bạn có thể đơn giản hoá mã hỗ trợ thời gian chạy của thanh ghi hệ thống để chỉ hỗ trợ tính năng chặn thanh ghi ngắt do phần mềm tạo (SGIR) và thanh ghi ngắt vô hiệu hoá (DIR). Cấu trúc này yêu cầu các thanh ghi này luôn bẫy đến EL2, trong khi các bẫy khác cho đến nay chỉ hữu ích để giảm thiểu lỗi. Mọi thứ khác đang được xử lý trong phần cứng.
Về phía MMIO, mọi thứ được mô phỏng tại EL1, sử dụng lại tất cả cơ sở hạ tầng hiện tại trong KVM. Cuối cùng, Wait for Interrupt (WFI) (Chờ ngắt) luôn được chuyển tiếp đến EL1, vì đây là một trong những nguyên tắc lập lịch biểu cơ bản mà KVM sử dụng.
Hỗ trợ bộ hẹn giờ
Giá trị bộ so sánh cho bộ hẹn giờ ảo phải được hiển thị cho EL1 trên mỗi WFI bẫy để EL1 có thể chèn các ngắt bộ hẹn giờ trong khi vCPU bị chặn. Đồng hồ hẹn giờ vật lý hoàn toàn được mô phỏng và tất cả các bẫy đều được chuyển tiếp đến EL1.
Xử lý MMIO
Để giao tiếp với trình giám sát máy ảo (VMM) và thực hiện mô phỏng GIC, các bẫy MMIO phải được chuyển tiếp lại cho máy chủ lưu trữ trong EL1 để phân loại thêm. pKVM yêu cầu những điều sau:
- IPA và quy mô của quyền truy cập
- Dữ liệu trong trường hợp ghi
- Thứ tự byte của CPU tại thời điểm bẫy
Ngoài ra, các bẫy có thanh ghi dùng cho nhiều mục đích (GPR) làm nguồn/đích sẽ được chuyển tiếp bằng cách sử dụng thanh ghi giả chuyển đổi trừu tượng.
Giao diện khách
Một khách có thể giao tiếp với một khách được bảo vệ bằng cách kết hợp các lệnh gọi siêu dữ liệu và quyền truy cập bộ nhớ vào các vùng bị chặn. Lệnh gọi siêu dữ liệu được hiển thị theo tiêu chuẩn SMCCC, với một phạm vi được dành riêng cho việc phân bổ nhà cung cấp theo KVM. Các lệnh gọi siêu dữ liệu sau đây đặc biệt quan trọng đối với khách pKVM.
Lệnh gọi siêu dữ liệu chung
- PSCI cung cấp một cơ chế tiêu chuẩn để máy khách kiểm soát vòng đời của vCPU, bao gồm cả việc đưa lên mạng, ngoại tuyến và tắt hệ thống.
- TRNG cung cấp một cơ chế tiêu chuẩn để máy khách yêu cầu entropy từ pKVM. Cơ chế này sẽ chuyển tiếp lệnh gọi đến EL3. Cơ chế này đặc biệt hữu ích khi không thể tin tưởng máy chủ để ảo hoá trình tạo số ngẫu nhiên (RNG) của phần cứng.
Siêu lệnh gọi pKVM
- Chia sẻ bộ nhớ với máy chủ lưu trữ. Máy chủ không thể truy cập vào tất cả bộ nhớ khách ban đầu, nhưng cần có quyền truy cập của máy chủ để giao tiếp với bộ nhớ dùng chung và cho các thiết bị ảo hoá song song dựa vào vùng đệm dùng chung. Lệnh gọi siêu dữ liệu để chia sẻ và huỷ chia sẻ trang với máy chủ cho phép máy khách quyết định chính xác những phần bộ nhớ mà phần còn lại của Android có thể truy cập mà không cần bắt tay.
- Chuyển giao bộ nhớ cho máy chủ lưu trữ. Tất cả bộ nhớ khách thường thuộc về khách cho đến khi bị huỷ. Trạng thái này có thể không phù hợp với các máy ảo có thời gian hoạt động dài với các yêu cầu về bộ nhớ thay đổi theo thời gian. Lệnh gọi siêu dữ liệu
relinquish
cho phép khách chuyển quyền sở hữu các trang trở lại máy chủ lưu trữ một cách rõ ràng mà không cần yêu cầu khách chấm dứt. - Lập trình phát hiện truy cập bộ nhớ cho máy chủ. Thông thường, nếu một máy khách KVM truy cập một địa chỉ không tương ứng với một vùng bộ nhớ hợp lệ, thì luồng vCPU sẽ thoát đến máy chủ và quyền truy cập thường được dùng cho MMIO và được VMM mô phỏng trong không gian của người dùng. Để hỗ trợ việc xử lý này, pKVM bắt buộc phải quảng cáo thông tin chi tiết về lệnh lỗi, chẳng hạn như địa chỉ, tham số đăng ký và có thể là nội dung của lệnh đó trở lại máy chủ lưu trữ. Điều này có thể vô tình làm lộ dữ liệu nhạy cảm từ máy khách được bảo vệ nếu không lường trước được bẫy. pKVM giải quyết vấn đề này bằng cách coi các lỗi này là nghiêm trọng, trừ phi máy khách trước đó đã phát hành một lệnh gọi siêu dữ liệu để xác định phạm vi IPA bị lỗi là phạm vi được phép truy cập để bẫy trở lại máy chủ lưu trữ. Giải pháp này được gọi là trình bảo vệ MMIO.
Thiết bị I/O ảo (virtio)
Virtio là một tiêu chuẩn phổ biến, linh hoạt và đã phát triển để triển khai và tương tác với các thiết bị ảo hoá song song. Phần lớn các thiết bị hiển thị với khách được bảo vệ được triển khai bằng virtio. Virtio cũng là cơ sở cho việc triển khai vsock dùng để giao tiếp giữa một ứng dụng khách được bảo vệ và phần còn lại của Android.
Các thiết bị Virtio thường được VMM triển khai trong không gian người dùng của máy chủ. Tính năng này chặn quyền truy cập vào bộ nhớ bị mắc kẹt từ máy khách đến giao diện MMIO của thiết bị virtio và mô phỏng hành vi dự kiến. Quyền truy cập MMIO tương đối tốn kém vì mỗi lượt truy cập vào thiết bị yêu cầu phải thực hiện một lượt khứ hồi đến VMM và quay lại. Do đó, hầu hết hoạt động chuyển dữ liệu thực tế giữa thiết bị và máy khách đều diễn ra thông qua một tập hợp các hoạt động trong bộ nhớ. Một giả định quan trọng của virtio là máy chủ có thể truy cập vào bộ nhớ khách một cách tuỳ ý. Giả định này thể hiện rõ trong thiết kế của virtqueue, có thể chứa con trỏ đến vùng đệm trong máy khách mà hoạt động mô phỏng thiết bị dự định truy cập trực tiếp.
Mặc dù các lệnh gọi siêu dữ liệu chia sẻ bộ nhớ được mô tả trước đó có thể được dùng để chia sẻ bộ đệm dữ liệu virtio từ máy khách đến máy chủ, nhưng việc chia sẻ này cần được thực hiện ở mức độ chi tiết của trang và có thể hiển thị nhiều dữ liệu hơn mức cần thiết nếu kích thước bộ đệm nhỏ hơn kích thước của trang. Thay vào đó, máy khách được định cấu hình để phân bổ cả ưu điểm và vùng đệm dữ liệu tương ứng từ một cửa sổ cố định của bộ nhớ dùng chung, với dữ liệu sẽ được sao chép (thoát) vào và ra khỏi cửa sổ theo yêu cầu.
Tương tác với TrustZone
Mặc dù khách không thể tương tác trực tiếp với TrustZone, nhưng máy chủ vẫn phải có thể đưa ra lệnh gọi SMC vào thế giới bảo mật. Các lệnh gọi này có thể chỉ định vùng đệm bộ nhớ được định địa chỉ vật lý mà máy chủ không thể truy cập. Vì phần mềm bảo mật thường không biết khả năng truy cập của vùng đệm, nên máy chủ độc hại có thể sử dụng vùng đệm này để thực hiện cuộc tấn công đại diện bị nhầm lẫn (tương tự như cuộc tấn công DMA). Để ngăn chặn những cuộc tấn công như vậy, pKVM sẽ chặn tất cả lệnh gọi SMC của máy chủ đến EL2 và đóng vai trò là proxy giữa máy chủ và màn hình bảo mật ở EL3.
Các lệnh gọi PSCI từ máy chủ lưu trữ được chuyển tiếp đến phần mềm EL3 với các sửa đổi tối thiểu. Cụ thể, điểm truy cập cho CPU đang hoạt động hoặc tiếp tục từ trạng thái tạm ngưng được ghi lại để bảng trang giai đoạn 2 được cài đặt tại EL2 trước khi quay lại máy chủ lưu trữ tại EL1. Trong quá trình khởi động, pKVM sẽ thực thi biện pháp bảo vệ này.
Cấu trúc này dựa vào SoC hỗ trợ PSCI, tốt nhất là thông qua việc sử dụng phiên bản mới nhất của TF-A làm phần mềm EL3.
Khung chương trình cơ sở cho Arm (FF-A) chuẩn hoá các hoạt động tương tác giữa thế giới thông thường và thế giới bảo mật, đặc biệt là khi có trình điều khiển ảo hoá bảo mật. Một phần chính của quy cách xác định cơ chế chia sẻ bộ nhớ với thế giới bảo mật, sử dụng cả định dạng thông báo phổ biến và mô hình quyền được xác định rõ ràng cho các trang cơ bản. pKVM sẽ proxy các thông báo FF-A để đảm bảo rằng máy chủ lưu trữ không cố gắng chia sẻ bộ nhớ với bên bảo mật mà không có đủ quyền.
Cấu trúc này dựa vào phần mềm thế giới bảo mật thực thi mô hình truy cập vào bộ nhớ để đảm bảo rằng các ứng dụng đáng tin cậy và mọi phần mềm khác chạy trong thế giới bảo mật chỉ có thể truy cập vào bộ nhớ nếu bộ nhớ đó thuộc quyền sở hữu riêng của thế giới bảo mật hoặc đã được chia sẻ rõ ràng với thế giới bảo mật bằng cách sử dụng FF-A. Trên hệ thống có S-EL2, việc thực thi mô hình truy cập bộ nhớ phải do Lõi trình quản lý phân vùng bảo mật (SPMC) thực hiện, chẳng hạn như Hafnium, duy trì bảng trang giai đoạn 2 cho thế giới bảo mật. Trên một hệ thống không có S-EL2, TEE có thể thực thi mô hình truy cập bộ nhớ thông qua các bảng trang giai đoạn 1.
Nếu lệnh gọi SMC đến EL2 không phải là lệnh gọi PSCI hoặc thông báo do FF-A xác định, thì các SMC chưa được xử lý sẽ được chuyển tiếp đến EL3. Giả định là phần mềm cơ sở bảo mật (cần phải đáng tin cậy) có thể xử lý an toàn các SMC chưa được xử lý vì phần mềm cơ sở hiểu được các biện pháp phòng ngừa cần thiết để duy trì tính năng cô lập pVM.
Màn hình máy ảo
crosvm là một trình giám sát máy ảo (VMM) chạy máy ảo thông qua giao diện KVM của Linux. Điểm độc đáo của crosvm là tập trung vào sự an toàn bằng cách sử dụng ngôn ngữ lập trình Rust và hộp cát xung quanh các thiết bị ảo để bảo vệ nhân máy chủ lưu trữ. Để biết thêm thông tin về crosvm, hãy xem tài liệu chính thức về crosvm tại đây.
Chỉ số mô tả tệp và ioctl
KVM hiển thị thiết bị ký tự /dev/kvm
cho không gian người dùng bằng các ioctl tạo thành API KVM. Các ioctl này thuộc các loại sau:
- Truy vấn ioctls hệ thống và đặt các thuộc tính toàn cục ảnh hưởng đến toàn bộ hệ thống con KVM và tạo các pVM.
- Các ioctl của máy ảo truy vấn và đặt các thuộc tính tạo CPU ảo (vCPU) và thiết bị, đồng thời ảnh hưởng đến toàn bộ pVM, chẳng hạn như bao gồm bố cục bộ nhớ và số lượng CPU ảo (vCPU) và thiết bị.
- vCPU ioctls truy vấn và đặt các thuộc tính kiểm soát hoạt động của một CPU ảo.
- ioctls của thiết bị truy vấn và đặt các thuộc tính kiểm soát hoạt động của một thiết bị ảo.
Mỗi quy trình crosvm chạy đúng một phiên bản của máy ảo. Quá trình này sử dụng ioctl hệ thống KVM_CREATE_VM
để tạo một chỉ số mô tả tệp máy ảo có thể dùng để phát hành ioctl pVM. ioctl KVM_CREATE_VCPU
hoặc KVM_CREATE_DEVICE
trên FD máy ảo sẽ tạo một vCPU/thiết bị và trả về một chỉ số mô tả tệp trỏ đến tài nguyên mới. Bạn có thể dùng ioctl trên vCPU hoặc FD thiết bị để kiểm soát thiết bị được tạo bằng ioctl trên FD máy ảo. Đối với vCPU, điều này bao gồm cả nhiệm vụ quan trọng là chạy mã khách.
Trong nội bộ, crosvm đăng ký chỉ số mô tả tệp của máy ảo với nhân bằng cách sử dụng giao diện epoll
được kích hoạt cạnh. Sau đó, nhân hệ điều hành sẽ thông báo cho crosvm bất cứ khi nào có một sự kiện mới đang chờ xử lý trong mọi chỉ số mô tả tệp.
pKVM thêm một chức năng mới, KVM_CAP_ARM_PROTECTED_VM
, có thể dùng để
tìm hiểu thông tin về môi trường pVM và thiết lập chế độ bảo vệ cho máy ảo.
crosvm sử dụng chức năng này trong quá trình tạo pVM nếu cờ --protected-vm
được truyền,
để truy vấn và đặt trước dung lượng bộ nhớ thích hợp cho
phần mềm cơ sở pVM, sau đó bật chế độ bảo vệ.
Phân bổ bộ nhớ
Một trong những trách nhiệm chính của VMM là phân bổ bộ nhớ của máy ảo và quản lý bố cục bộ nhớ của máy ảo. crosvm tạo một bố cục bộ nhớ cố định được mô tả không chặt chẽ trong bảng dưới đây.
FDT ở chế độ thông thường | PHYS_MEMORY_END - 0x200000
|
K.g trống | ...
|
Ramdisk | ALIGN_UP(KERNEL_END, 0x1000000)
|
Kernel | 0x80080000
|
Trình tải khởi động | 0x80200000
|
FDT ở chế độ BIOS | 0x80000000
|
Cơ sở bộ nhớ vật lý | 0x80000000
|
Chương trình cơ sở pVM | 0x7FE00000
|
Bộ nhớ thiết bị | 0x10000 - 0x40000000
|
Bộ nhớ vật lý được phân bổ bằng mmap
và bộ nhớ được tặng cho máy ảo để điền sẵn các vùng bộ nhớ, được gọi là memslots, bằng ioctl KVM_SET_USER_MEMORY_REGION
. Do đó, tất cả bộ nhớ pVM của máy khách được phân bổ cho thực thể crosvm quản lý bộ nhớ đó và có thể khiến quá trình bị tắt (chấm dứt máy ảo) nếu máy chủ lưu trữ bắt đầu hết bộ nhớ trống. Khi máy ảo bị dừng, bộ nhớ sẽ tự động bị trình điều khiển ảo hoá xoá sạch và được trả về nhân hệ điều hành của máy chủ.
Trong KVM thông thường, VMM vẫn có quyền truy cập vào tất cả bộ nhớ của khách. Với pKVM, bộ nhớ khách sẽ không được liên kết với không gian địa chỉ thực của máy chủ khi được tặng cho khách. Ngoại lệ duy nhất là bộ nhớ được máy khách chia sẻ lại một cách rõ ràng, chẳng hạn như đối với các thiết bị virtio.
Các vùng MMIO trong không gian địa chỉ của khách không được liên kết. Quyền truy cập của máy khách vào các vùng này sẽ bị chặn và dẫn đến một sự kiện I/O trên FD của máy ảo. Cơ chế này được dùng để triển khai thiết bị ảo. Ở chế độ bảo vệ, máy khách phải xác nhận rằng một vùng trong không gian địa chỉ của máy khách được dùng cho MMIO bằng cách sử dụng lệnh gọi siêu dữ liệu để giảm nguy cơ rò rỉ thông tin do nhầm lẫn.
Lập lịch
Mỗi CPU ảo được biểu thị bằng một luồng POSIX và được lên lịch bởi trình lập lịch biểu Linux của máy chủ. Luồng này gọi ioctl KVM_RUN
trên vCPU FD, dẫn đến việc trình điều khiển ảo hoá chuyển sang ngữ cảnh vCPU khách. Trình lập lịch biểu của máy chủ tính thời gian trong ngữ cảnh khách là thời gian mà luồng vCPU tương ứng sử dụng. KVM_RUN
trả về khi có một sự kiện phải được VMM xử lý, chẳng hạn như I/O, kết thúc ngắt hoặc vCPU bị tạm dừng. VMM xử lý sự kiện và gọi lại KVM_RUN
.
Trong KVM_RUN
, trình lập lịch biểu của máy chủ vẫn có thể chiếm quyền thực thi luồng, ngoại trừ việc thực thi mã trình điều khiển ảo hoá EL2 không thể bị chiếm quyền thực thi. Bản thân pVM khách không có cơ chế nào để kiểm soát hành vi này.
Vì tất cả luồng vCPU đều được lên lịch như mọi tác vụ không gian người dùng khác, nên các luồng này phải tuân theo tất cả cơ chế QoS tiêu chuẩn. Cụ thể, mỗi luồng vCPU có thể được liên kết với CPU thực, được đặt trong cpuset, được tăng hoặc giới hạn bằng cách sử dụng kẹp sử dụng, thay đổi chính sách ưu tiên/lập lịch, v.v.
Thiết bị ảo
crosvm hỗ trợ một số thiết bị, bao gồm:
- virtio-blk cho hình ảnh đĩa tổng hợp, chỉ đọc hoặc đọc-ghi
- vhost-vsock để giao tiếp với máy chủ
- virtio-pci làm phương thức truyền virtio
- đồng hồ thời gian thực pl030 (RTC)
- UART 16550a để giao tiếp nối tiếp
Chương trình cơ sở pVM
Phần mềm cơ sở pVM (pvmfw) là mã đầu tiên do pVM thực thi, tương tự như ROM khởi động của thiết bị thực. Mục tiêu chính của pvmfw là khởi động từ đầu chế độ khởi động an toàn và lấy bí mật duy nhất của pVM. pvmfw không bị giới hạn sử dụng với bất kỳ hệ điều hành cụ thể nào, chẳng hạn như Microdroid, miễn là hệ điều hành đó được crosvm hỗ trợ và đã được ký đúng cách.
Tệp nhị phân pvmfw được lưu trữ trong một phân vùng flash có cùng tên và được cập nhật bằng OTA.
Khởi động thiết bị
Trình tự các bước sau đây được thêm vào quy trình khởi động của thiết bị hỗ trợ pKVM:
- Trình tải khởi động Android (ABL) tải pvmfw từ phân vùng vào bộ nhớ và xác minh hình ảnh.
- ABL lấy các bí mật của Công cụ kết hợp giá trị nhận dạng thiết bị (DICE) (Giá trị nhận dạng thiết bị phức hợp (CDI) và chuỗi chứng chỉ DICE) từ một Điểm gốc đáng tin cậy.
- ABL lấy các CDI cần thiết cho pvmfw và thêm các CDI đó vào tệp nhị phân pvmfw.
- ABL thêm một nút vùng bộ nhớ được đặt trước
linux,pkvm-guest-firmware-memory
vào DT, mô tả vị trí và kích thước của tệp nhị phân pvmfw cũng như các khoá bí mật mà tệp này lấy được ở bước trước. - ABL chuyển quyền kiểm soát sang Linux và Linux khởi chạy pKVM.
- pKVM huỷ liên kết vùng bộ nhớ pvmfw khỏi các bảng trang giai đoạn 2 của máy chủ và bảo vệ vùng đó khỏi máy chủ (và khách) trong suốt thời gian hoạt động của thiết bị.
Sau khi khởi động thiết bị, Microdroid sẽ được khởi động theo các bước trong phần Trình tự khởi động của tài liệu Microdroid.
khởi động pVM
Khi tạo một pVM, crosvm (hoặc một VMM khác) phải tạo một bộ nhớ đủ lớn để được trình điều khiển ảo hoá điền sẵn hình ảnh pvmfw. VMM cũng bị hạn chế trong danh sách các thanh ghi có thể đặt giá trị ban đầu (x0-x14 cho vCPU chính, không có giá trị nào cho vCPU phụ). Các thanh ghi còn lại được đặt trước và là một phần của ABI hypervisor-pvmfw.
Khi pVM chạy, trước tiên, trình điều khiển ảo hoá sẽ chuyển quyền kiểm soát vCPU chính sang pvmfw. Firmware dự kiến crosvm đã tải một hạt nhân đã ký AVB, có thể là trình tải khởi động hoặc bất kỳ hình ảnh nào khác và một FDT chưa ký vào bộ nhớ tại các độ dời đã biết. pvmfw xác thực chữ ký AVB và nếu thành công, sẽ tạo một cây thiết bị đáng tin cậy từ FDT đã nhận, xoá các bí mật của cây thiết bị đó khỏi bộ nhớ và chuyển đến điểm truy cập của tải trọng. Nếu một trong các bước xác minh không thành công, thì phần mềm sẽ phát hành một lệnh gọi siêu dữ liệu PSCI SYSTEM_RESET
.
Giữa các lần khởi động, thông tin về phiên bản pVM được lưu trữ trong một phân vùng (thiết bị virtio-blk) và được mã hoá bằng khoá bí mật của pvmfw để đảm bảo rằng sau khi khởi động lại, khoá bí mật sẽ được cấp cho phiên bản chính xác.