Android cung cấp cách triển khai tham chiếu tất cả các thành phần cần thiết để triển khai Khung ảo hoá Android. Hiện tại, việc triển khai này chỉ giới hạn ở ARM64. Trang này giải thích về cấu trúc khung.
Thông tin khái quát
Kiến trúc Arm cho phép tối đa 4 cấp ngoại lệ, trong đó cấp ngoại lệ 0 (EL0) là cấp đặc quyền ít nhất và cấp ngoại lệ 3 (EL3) là có nhiều đặc quyền nhất. Phần lớn nhất của cơ sở mã Android (tất cả thành phần không gian người dùng) chạy ở EL0. Phần còn lại của hệ điều hành thường có tên là "Android" là nhân hệ điều hành 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 cô lập bộ nhớ và thiết bị vào các pVM riêng lẻ tại EL1/EL0, với các đảm bảo chắc chắn về tính bảo mật và tính toàn vẹn.
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 khả năng cung cấp một số tính năng của CPU, cụ thể là Tiện ích máy chủ ảo hoá (VHE) (ARMv8.1 trở lên). Ở một trong các chế độ đó, thường được gọi là chế độ không phải VHE, mã trình điều khiển ảo hoá sẽ được tách khỏi hình ảnh nhân hệ điều hành trong quá trình khởi động và được cài đặt tại EL2, trong khi nhân hệ điều hành tự 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ớ chuyên dụng riêng biệt 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 hạn chế đối với nhân máy chủ lưu trữ Android và không gian người dùng, đồ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 của nhà cung cấp pKVM là một mô-đun dành riêng cho phần cứng, có chứa chức năng dành riêng cho thiết bị, chẳng hạn như các trình điều khiển bộ 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 cấp ngoại lệ 2 (EL2) vào 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 bài viết 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ẽ đưa vào nhân hệ điều hành chung tại EL2.
- Hạt nhân chung phát hiện rằng nó đang chạy ở EL2 và tự tước đi 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 của 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. Hiện tại, pKVM đã được thiết lập và xử lý các bảng trang giai đoạn 2.
Quy trình khởi động tin tưởng trình tải khởi động để chỉ duy trì tính toàn vẹn của hình ảnh hạt nhân trong thời gian khởi động sớm. Khi nhân hệ điều hành bị hạn chế, trình điều khiển ảo hoá không còn coi nhân này là đáng tin cậy, sau đó có trách nhiệm tự bảo vệ ngay cả khi nhân bị xâm phạm.
Việc có nhân hệ điều hành Android và trình điều khiển ảo hoá trong cùng một hình ảnh nhị phân cho phép tạo ra một giao diện giao tiếp được kết hợp rất chặt chẽ giữa chúng. Mối ghép chặt chẽ này đảm bảo cập nhật nguyên tử 2 thành phần, giúp tránh phải giữ cho giao diện giữa các thành phần này ổn định, đồng thời mang lại tính linh hoạt cao 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 đả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 áp dụng GKI trong hệ sinh thái Android 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 nhân hệ điều hành.
Bảo vệ truy cập 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 được 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 bản dịch địa chỉ thứ hai trên địa chỉ đầu ra của MMU giai đoạn 1, tạo ra địa chỉ thực tế (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 quyền truy cập vào bộ nhớ từ tất cả các máy ảo khách. Như 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 được bật tính năng dịch giai đoạn 2 trong khi chạy ứng dụng khách và tắt giai đoạn 2 trong khi chạy nhân hệ điều hành Linux trên máy chủ. Cấu trúc này cho phép quyền truy cập vào bộ nhớ từ MMU giai đoạn 1 của máy chủ lưu trữ để chuyển 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 hỗ trợ tính năng bảo vệ giai đoạn 2 ngay cả trong ngữ cảnh máy chủ lưu trữ, đồng thời giao cho trình điều khiển ảo hoá chịu trách nhiệm bảo vệ các trang bộ nhớ khách thay vì máy chủ lưu trữ.
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ỉ được giới hạn trong phạm vi kiểm soát quyền truy cập. Giai đoạn 2 của máy chủ được ánh xạ danh tính, đảm bảo rằng bộ nhớ liền kề trong không gian IPA của máy chủ được tiếp giáp trong không gian PA. Cấu trúc này cho phép sử dụng các bản đồ ánh xạ lớn trong bảng trang, do đó, giảm áp lực lên vùng đệm giao diện dịch (TLB). Vì PA có thể lập chỉ mục ánh xạ danh tính, nên giai đoạn lưu trữ 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 một thiết bị như vậy truy cập vào bộ nhớ khách, pKVM yêu cầu phần cứng bộ quản lý bộ nhớ đầu vào-đầu ra (IOMMU) cho mọi thiết bị hỗ trợ DMA trong hệ thống, như minh hoạ trong hình 3.
Ở mức tối thiểu, phần cứng IOMMU cung cấp các phương thức cấp và thu hồi quyền đọc/ghi của một thiết bị vào bộ nhớ thực ở mức độ chi tiết của trang. Tuy nhiên, phần cứng IOMMU này giới hạn việc sử dụng thiết bị trong pVM vì chúng giả định giai đoạn 2 được ánh xạ danh tính.
Để đảm bảo tách biệt giữa các máy ảo, các giao dịch bộ nhớ được tạo ra thay mặt cho nhiều thực thể phải có thể phân biệt được bằng IOMMU để có thể sử dụng nhóm bảng trang thích hợp cho việc dịch.
Ngoài ra, việc giảm số lượng mã dành riêng cho SoC ở EL2 là một chiến lược chính để giảm số lượng 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ủ lưu trữ tại EL1 chịu trách nhiệm thực hiện các tác vụ quản lý IOMMU phụ trợ, chẳng hạn như quản lý nguồn điện, khởi động và xử lý gián đoạn khi thích hợp.
Tuy nhiên, việc để máy chủ kiểm soát trạng thái thiết bị sẽ đặt ra các yêu cầu bổ sung đối với giao diện lập trình của phần cứng IOMMU để đảm bảo rằng không thể bỏ qua hoạt động kiểm tra quyền bằng các phương thức khác, 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 khảo đượ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á đều đượ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á đặt các hạn chế kiểm soát quyền truy cập vào bảng trang giai đoạn 2 của máy chủ để ngăn không cho truy cập lại vào các trang, giúp bảo mật cho 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ẻ lại một số trang của họ với máy chủ thông qua một lệnh gọi siêu dữ liệu. Lệnh này hướng dẫn trình điều khiển ảo hoá liên kết 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ủ. Trên thực tế, siêu lệnh gọi này được dùng với giao thức bong bóng virtio để cho phép VMM yêu cầu bộ nhớ quay lại từ pVM, đồng thời để pVM thông báo cho VMM về các trang đã bị loại bỏ theo cách được kiểm soát.
Trình điều khiển ảo hoá có 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 chúng 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 nó không cố truy cập vào các trang đã bị trình điều khiển ảo hoá không thể truy cập được. Quyền truy cập bất hợp pháp vào máy chủ lưu trữ sẽ khiến trình điều khiển ảo hoá chèn vào máy chủ một ngoại lệ đồng bộ, điều này có thể khiến tác vụ không gian người dùng có trách nhiệm nhận được tín hiệu SEGV hoặc hạt nhân của máy chủ lưu trữ gặp sự cố. Để tránh việc vô tình truy cập, các trang được cung cấp cho khách sẽ không đủ điều kiện để hoán đổi hoặc hợp nhất bởi nhân hệ điều hành của máy chủ.
Giá trị gián đoạn quá trình xử lý và bộ tính giờ
Ngắt là một phần thiết yếu trong cách ứng dụng khách tương tác với thiết bị và để giao tiếp giữa các CPU, trong đó ngắt bộ liên bộ xử lý (IPI) là cơ chế giao tiếp chính. Mô hình KVM là uỷ quyền toàn bộ tính năng quản lý gián đoạn ảo cho máy chủ lưu trữ trong EL1. Máy chủ này hoạt động như một phần không đáng tin cậy của trình điều khiển ảo hoá với mục đích đó.
pKVM cung cấp chế độ mô phỏng Bộ điều khiển gián đoạn chung phiên bản 3 (GICv3) đầy đủ dựa trên mã KVM hiện có. Bộ tính 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. Chế độ 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ột vùng cho mỗi CPU ảo (vCPU).
Bạn có thể đơn giản hoá mã hỗ trợ thanh ghi thời gian chạy cho thanh ghi hệ thống để chỉ hỗ trợ tính năng Thanh ghi gián đoạn do phần mềm tạo (SGIR) và bẫy thanh ghi huỷ kích hoạt thanh ghi gián đoạn (DIR). Kiến trúc yêu cầu các thanh ghi này luôn bẫy được EL2, trong khi các bẫy khác cho đến nay chỉ hữu ích trong việc giảm thiểu lỗi sai lệch. Mọi thứ khác đều được xử lý bằng 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, Chờ gián đoạn (WFI) luôn được chuyển tiếp đến EL1 vì đây là một trong những dữ liệu lập lịch cơ bản mà KVM sử dụng.
Hỗ trợ bộ tính giờ
Giá trị so sánh của bộ tính giờ ảo phải được hiển thị với EL1 trên mỗi WFI bẫy để EL1 có thể chèn các ngắt bộ tính 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 quy trình mô phỏng GIC, các bẫy MMIO phải được chuyển tiếp trở lại máy chủ trong EL1 để phân loại thêm. pKVM đòi hỏi như sau:
- IPA và quy mô của quyền truy cập
- Dữ liệu trong trường hợp ghi
- Độ bền của CPU tại điểm bị mắc kẹt
Ngoài ra, các bẫy có thanh ghi mục đích chung (GPR) làm nguồn/đích sẽ được chuyển tiếp bằng cách sử dụng đăng ký giả chuyển trừu tượng.
Giao diện khách
Khách có thể giao tiếp với khách được bảo vệ bằng cách sử dụng kết hợp các lệnh gọi siêu âm (hyper) và quyền truy cập vào bộ nhớ đối với những vùng bị mắc kẹt. Siêu lệnh gọi được hiển thị theo tiêu chuẩn SMCCC, với phạm vi dành riêng cho việc phân bổ của nhà cung cấp bởi KVM. Các lệnh gọi siêu nhanh sau đây có vai trò đặc biệt quan trọng đối với khách pKVM.
Siêu lệnh gọi chung
- PSCI cung cấp một cơ chế tiêu chuẩn để chế độ khách kiểm soát vòng đời của các vCPU, bao gồm cả nội tuyến, 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, giúp 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ủ. 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. Siêu lệnh gọi để chia sẻ và không chia sẻ trang với máy chủ lưu trữ cho phép khách quyết định chính xác những phần của bộ nhớ có thể truy cập được vào phần còn lại của Android mà không cần bắt tay.
- Ngừng sử dụng bộ nhớ cho máy chủ. Mọi bộ nhớ của chế độ khách thường thuộc về chế độ khách cho đến khi bị huỷ. Trạng thái này có thể không đủ cho các máy ảo dài hạn với các yêu cầu về bộ nhớ thay đổi theo thời gian. Siêu lệnh gọi
relinquish
cho phép khách chuyển quyền sở hữu trang trở lại máy chủ một cách rõ ràng mà không yêu cầu chấm dứt khách. - Bẫy quyền truy cập vào bộ nhớ đến máy chủ. Thông thường, nếu một máy khách KVM truy cập vào 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. Để tạo điều kiện cho việc xử lý này, pKVM bắt buộc phải quảng cáo thông tin chi tiết về hướng dẫn lỗi, chẳng hạn như địa chỉ, tham số đăng ký và có thể nội dung của các lệnh đó trả về máy chủ. Đ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 chủ trước đó đã đưa ra lệnh gọi ngược để xác định dải IPA bị lỗi. Giải pháp này được gọi là 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ị tiếp xúc với khách được bảo vệ đều đượ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 chính của virtio là máy chủ lưu trữ có thể tuỳ ý truy cập vào bộ nhớ khách. Giả định này thể hiện rõ trong thiết kế của hàng đợi, có thể chứa các con trỏ đến các vùng đệm trong máy khách mà quy trình mô phỏng thiết bị dùng để truy cập trực tiếp.
Mặc dù các lệnh gọi siêu liên kết chia sẻ bộ nhớ được mô tả trước đó có thể dùng để chia sẻ vùng đệm dữ liệu virtio từ máy khách đến máy chủ, nhưng việc chia sẻ này cần thiết được thực hiện ở mức độ chi tiết của trang và có thể để lộ nhiều dữ liệu hơn mức cần thiết nếu dung lượng bộ nhớ đệm nhỏ hơn dung lượng của một 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ủ lưu trữ 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ớ xử lý vật lý mà máy chủ không thể truy cập được. Vì phần mềm bảo mật thường không nhận biết về khả năng hỗ trợ tiếp cận của vùng đệm, nên một máy chủ lưu trữ độc hại có thể sử dụng bộ đệm này để thực hiện một cuộc tấn công phân cấp gây nhầm lẫn (tương tự như cuộc tấn công theo Đạo luật thị trường kỹ thuật số (DMA). Để ngăn chặn các cuộc tấn công như vậy, pKVM sẽ bẫy tất cả các lệnh gọi SMC của máy chủ đến EL2 và hoạt động như một proxy giữa máy chủ lưu trữ và màn hình bảo mật tại EL3.
Các lệnh gọi PSCI từ máy chủ được chuyển tiếp đến chương trình cơ sở EL3 mà không cần sửa đổi nhiều. Cụ thể, điểm truy cập cho CPU đang kết nối mạng hoặc tiếp tục sau khi tạm ngưng được viết 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ủ tại EL1. Trong quá trình khởi động, biện pháp bảo vệ này được pKVM thực thi.
Cấu trúc này dựa vào SoC hỗ trợ PSCI, tốt nhất là sử dụng phiên bản TF-A mới nhất làm chương trình cơ sở 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 là 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 cấp quyền được xác định rõ ràng cho các trang cơ bản. pKVM proxy 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 phía bảo mật mà máy chủ không có đủ quyền.
Kiến 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 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 sở hữu độc quyền của thế giới bảo mật hoặc được chia sẻ rõ ràng với nó bằng FF-A. Trên hệ thống có S-EL2, việc thực thi mô hình truy cập bộ nhớ nên do Lõi quản lý phân vùng bảo mật (SPMC) thực hiện, chẳng hạn như Hafnium, vốn duy trì các bảng trang giai đoạn 2 cho thế giới bảo mật. Trên 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à chương trình cơ sở bảo mật (cần thiết phải đáng tin cậy) có thể xử lý an toàn các SMC chưa được xử lý vì chương trình cơ sở hiểu các biện pháp phòng ngừa cần thiết để duy trì chế độ cô lập pVM.
Màn hình máy ảo
crosvm là một màn hình máy ảo (VMM) chạy các máy ảo thông qua giao diện KVM của Linux. Điều làm cho crosvm trở nên độc đáo là tập trung vào tính an toàn bằng cách sử dụng ngôn ngữ lập trình Rust và một hộp cát xung quanh các thiết bị ảo để bảo vệ nhân hệ điều hành của máy chủ. Để 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à ioctls
KVM hiển thị thiết bị ký tự /dev/kvm
cho không gian người dùng bằng các ioctls tạo nên 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.
- Truy vấn và thiết lập các thuộc tính máy ảo 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ị.
- Truy vấn vCPU và đặt các thuộc tính kiểm soát hoạt động của một CPU ảo.
- Truy vấn về iOS thiết bị 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 chính xác một thực thể của máy ảo. Quy trình này sử dụng ioctl hệ thống KVM_CREATE_VM
để tạo 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 VM FD sẽ tạo vCPU/thiết bị và trả về chỉ số mô tả tệp trỏ đến tài nguyên mới. Bạn có thể sử dụng ioctl trên vCPU hoặc FD thiết bị để điều khiển thiết bị được tạo bằng ioctl trên VM FD. Đối với vCPU, phần này bao gồm tác 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 hệ điều hành 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 tính năng mới, KVM_CAP_ARM_PROTECTED_VM
, có thể dùng để lấy thông tin về môi trường pVM và thiết lập chế độ được bảo vệ cho máy ảo. crosvm sử dụng tính năng này trong quá trình tạo pVM nếu cờ --protected-vm
được truyền, để truy vấn và dự trữ dung lượng bộ nhớ thích hợp cho chương trình cơ sở pVM, sau đó bật chế độ được 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 | ...
|
Ổ đĩa | ALIGN_UP(KERNEL_END, 0x1000000)
|
Kernel | 0x80080000
|
Trình tải khởi động | 0x80200000
|
FDT ở chế độ BIOS | 0x80000000
|
Cơ sở bộ nhớ thực | 0x80000000
|
chương trình cơ sở pVM | 0x7FE00000
|
Bộ nhớ thiết bị | 0x10000 - 0x40000000
|
Bộ nhớ thực được phân bổ bằng mmap
và bộ nhớ này được cấp cho máy ảo để điền các vùng bộ nhớ của máy ảo (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ể dẫn đến việc 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 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 không được ánh xạ từ không gian địa chỉ thực của máy chủ khi được cấp cho khách. Ngoại lệ duy nhất là bộ nhớ được 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 khu vực MMIO trong không gian địa chỉ của khách không được lập bản đồ. Quyền truy cập của khách vào những khu vực này sẽ bị giữ lại và dẫn đến một sự kiện I/O trên VM FD. Cơ chế này được dùng để triển khai các thiết bị ảo. Ở chế độ được bảo vệ, máy khách phải xác nhận rằng một khu vực trong không gian địa chỉ của mình được dùng cho MMIO bằng cách sử dụng siêu lệnh gọi để giảm nguy cơ vô tình rò rỉ thông tin.
Lên lịch
Mỗi CPU ảo được biểu thị bằng một luồng POSIX và được trình lập lịch biểu Linux lưu trữ lên lị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ủ lưu trữ có tính đến thời gian dành cho 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 do VMM xử lý, chẳng hạn như I/O, kết thúc gián đoạn hoặc vCPU tạm dừng. VMM xử lý sự kiện và gọi lại KVM_RUN
.
Trong KVM_RUN
, luồng vẫn có thể bị trình lập lịch biểu của máy chủ giành trước, ngoại trừ việc thực thi mã trình điều khiển ảo hoá EL2 (mã này không được phép giành quyền trước). Bản thân pVM máy khách không có cơ chế kiểm soát hành vi này.
Vì tất cả các luồng vCPU được lên lịch giống như mọi tác vụ khác không gian người dùng, nên các luồng này phải tuân theo tất cả cá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à sự vận chuyển virtio
- đồng hồ thời gian thực pl030 (RTC)
- 16550a UART để giao tiếp nối tiếp
chương trình cơ sở pVM
Chương trình cơ sở pVM (pvmfw) là mã đầu tiên được 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 bảo mật bằng cách khởi động 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 hỗ trợ và có chữ ký của pVM.
Tệp nhị phân pvmfw được lưu trữ trong một phân vùng flash 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 mã thông báo bí mật cho Công cụ cấu trúc mã nhận dạng thiết bị (DICE) (kết hợp Giá trị nhận dạng thiết bị tổng hợp (CDI) và chuỗi chứng chỉ DICE) từ Root of Trust.
- ABL lấy các CDI cần thiết cho pvmfw và thêm các CDI đó vào nhị phân pvmfw.
- ABL thêm một nút vùng bộ nhớ dành riêng
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 bí mật mà tệp đó lấy được ở bước trước. - ABL trao quyền kiểm soát cho 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 đượ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ị giới hạn trong danh sách các thanh ghi có giá trị ban đầu có thể đặt (x0-x14 cho vCPU chính, không có cho vCPU phụ). Các thanh ghi còn lại được đặt trước và thuộc ABI ảo hoá pvmfw.
Khi pVM được chạy, trước tiên, trình điều khiển ảo hoá sẽ điều khiển vCPU chính cho pvmfw. Chương trình cơ sở này dự kiến rằng crosvm sẽ tải một nhân do AVB ký, 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 độ lệch đã 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 FDT khỏi bộ nhớ và các nhánh đến điểm vào của tải trọng. Nếu một trong các bước xác minh không thành công, chương trình cơ sở sẽ đưa ra một lệnh gọi siêu dữ liệu SYSTEM_RESET
PSCI.
Giữa các lần khởi động, thông tin về thực thể pVM được lưu trữ trong một phân vùng (thiết bị virtio-blk) và được mã hoá bằng mã bí mật của pvmfw để đảm bảo rằng sau khi khởi động lại, bí mật sẽ được cấp phép cho đúng thực thể.