Kiến trúc AVF

Android cung cấp một cách triển khai tham chiếu cho tất cả các thành phần cần thiết để triển khai Khung ảo hoá cho 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 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ó ít đặc quyền nhất và cấp độ ngoại lệ 3 (EL3) có nhiều đặc quyền nhất. Phần lớn nhất của cơ sở mã Android (tất cả các thành phần không gian người dùng) chạy ở EL0. Phần còn lại của cái mà người ta thường 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 giám sát siêu dữ liệu giúp tách biệt bộ nhớ và thiết bị thành các pVM riêng lẻ tại EL1/EL0, với khả năng bảo mật và tính toàn vẹn mạnh mẽ.

Trình điều khiển ảo hoá

Máy ảo dựa trên nhân được bảo vệ (pKVM) được xây dựng dựa trên trình giám sát ảo KVM của Linux. Trình giám sát ảo này đã được mở rộng để có thể hạn chế quyền truy cập vào các tải trọng đang 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 tính khả dụng của một số tính năng CPU nhất định, cụ thể là Tiện ích lưu trữ ảo (VHE) (ARMv8.1 trở lên). Trong một trong những chế độ đó (thường được gọi là chế độ không VHE), mã trình giám sát ảo sẽ được tách ra khỏi hình ảnh nhân trong quá trình khởi động và được cài đặt tại EL2, trong khi bản thân 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 ả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 ảo hoá với các tính năng mới, cho phép mã này đặt ra các hạn chế đối với nhân máy chủ 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 ảo hoá.

Các mô-đun của nhà cung cấp pKVM

Mô-đun nhà cung cấp pKVM là một 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 cấp độ ngoại lệ 2 (EL2) sang pKVM.

Để tìm hiểu cách triển khai và tải một 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:

Quy trình khởi động pKVM

Hình 1. Quy trình khởi động pKVM

  1. Trình tải khởi động sẽ nhập hạt nhân chung ở EL2.
  2. Nhân chung phát hiện thấ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 vào thời điểm này.
  3. Nhân chung 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 đến không gian người dùng. Tại thời điểm này, 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 để duy trì tính toàn vẹn của hình ảnh nhân chỉ trong quá trình khởi động ban đầu. Khi nhân bị tước đặc quyền, trình giám sát ảo sẽ không còn coi nhân là đáng tin cậy nữa. Sau đó, trình giám sát ảo sẽ chịu trách nhiệm tự bảo vệ ngay cả khi nhân bị xâm nhập.

Việc có nhân Android và trình giám sát siêu ảo trong cùng một hình ảnh nhị phân cho phép có một giao diện giao tiếp được liên kết chặt chẽ giữa chúng. Việc kết hợp chặt chẽ này đảm bảo các bản cập nhật riêng lẻ của hai thành phần, giúp bạn không cần phải duy trì giao diện ổn định giữa chúng và mang lại nhiều tính linh hoạt mà không ảnh hưởng đến khả năng duy trì lâu dài. Việc liên kết chặt chẽ này 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 giám sát siêu vi cung cấp.

Ngoài ra, việc áp dụng GKI trong hệ sinh thái Android sẽ tự động cho phép triển khai trình giám sát máy ảo pKVM cho các thiết bị Android trong cùng một tệp nhị phân như nhân.

Bảo vệ quyền truy cập vào bộ nhớ CPU

Cấu 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 này đều có thể được dùng để triển khai tính năng 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 kiểm soát bởi EL1 và cho phép dịch địa chỉ ở cấp độ đầu tiên. MMU giai đoạn 1 được Linux dùng để quản lý không gian địa chỉ ảo được cung cấp cho từng 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 được kiểm soát bởi EL2 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, dẫn đến địa chỉ thực (PA). Trình ảo hoá có thể dùng bản dịch giai đoạn 2 để kiểm soát và dịch các hoạt động truy cập bộ nhớ từ tất cả cá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ỉ thực trung gian (IPA). Lưu ý: Địa chỉ ảo (VA) được dịch thành IPA rồi thành PA.

Bảo vệ quyền truy cập vào bộ nhớ CPU

Hình 2. Bảo vệ quyền truy cập vào bộ nhớ CPU

Trước đây, KVM chạy với bản dịch giai đoạn 2 được bật trong khi chạy các khách và với giai đoạn 2 bị tắt trong 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ủ lưu trữ truyền qua MMU giai đoạn 2, do đó cho phép máy chủ lưu trữ truy cập không hạn chế vào 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ủ lưu trữ và đặt siêu giám sát viên 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 các á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 mặc dù 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ỉ giới hạn ở quyền kiểm soát truy cập. Giai đoạn 2 của máy chủ lưu trữ đượ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ủ lưu trữ là 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 bộ đệm tra cứu dịch (TLB). Vì PA có thể lập chỉ mục một mối liên kết danh tính, nên giai đoạn 2 của máy chủ lưu trữ cũng được dùng để theo dõi quyền sở hữu trang ngay trong bảng trang.

Bảo vệ quyền truy cập trực tiếp vào bộ nhớ (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à một bước cần thiết nhưng chưa đủ để bảo vệ bộ nhớ khách. pKVM cũng cần bảo vệ chống lại các hoạt động truy cập bộ nhớ do các thiết bị có khả năng DMA thực hiện trong quyền kiểm soát của nhân máy chủ, cũng như khả năng xảy ra cuộc tấn công DMA do một máy chủ độc hại khởi xướng. Để 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ó khả năng DMA trong hệ thống, như minh hoạ trong hình 3.

Bảo vệ quyền truy cập vào bộ nhớ DMA

Hình 3. Bảo vệ quyền truy cập vào bộ nhớ DMA

Tối thiểu, phần cứng IOMMU cung cấp phương tiện cấp và thu hồi quyền truy cập đọc/ghi cho một thiết bị vào bộ nhớ vật lý ở 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 các thiết bị trong pVM vì chúng giả định giai đoạn 2 được ánh xạ danh tính.

Để đảm bảo sự cô lập 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 cho các thực thể khác nhau để có thể sử dụng bộ bảng trang thích hợp cho quá trình dịch.

Ngoài ra, việc giảm lượng mã dành riêng cho SoC ở EL2 là một chiến lược quan trọng để giảm cơ sở điện toán đáng tin cậy (TCB) tổng thể của pKVM và đi ngược lại việc đưa trình điều khiển IOMMU vào trình ả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 về các tác vụ quản lý IOMMU phụ trợ, chẳng hạn như quản lý nguồn, khởi tạo và xử lý gián đoạn (nếu có).

Tuy nhiên, việc đặt máy chủ lưu trữ vào chế độ 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 tiện khác không thể bỏ qua quy trình kiểm tra quyền, chẳng hạn như sau khi thiết bị được đặt lại.

Một IOMMU tiêu chuẩn và được hỗ trợ tốt cho các thiết bị Arm, giúp cả hoạt động cô lập và chỉ định trực tiếp đều có thể thực hiện được là kiến trúc Đơn vị 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 kỷ niệm

Tại thời điểm khởi động, giả sử tất cả bộ nhớ không phải hypervisor đều thuộc sở hữu của máy chủ và được hypervisor theo dõi như vậy. Khi một pVM được tạo, máy chủ sẽ cấp phát các trang bộ nhớ để cho phép pVM khởi động và trình giám sát siêu vi sẽ chuyển quyền sở hữu các trang đó từ máy chủ sang pVM. Do đó, trình giám sát máy ảo sẽ áp dụng cá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ủ lưu trữ để ngăn máy chủ lưu trữ truy cập lại vào các trang, đảm bảo tính bảo mật cho khách.

Việc giao tiếp giữa người tổ chức và khách được thực hiện nhờ tính nă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ủ bằng cách sử dụng một hypercall. Hypercall này hướng dẫn trình giám sát siêu vi ánh xạ lại các trang đó trong bảng trang giai đoạn 2 của máy chủ. Tương tự, hoạt động giao tiếp của máy chủ lưu trữ với TrustZone được thực hiện nhờ các hoạt động chia sẻ và/hoặc cho mượn bộ nhớ. Tất cả các hoạt động này đều được pKVM giám sát và kiểm soát chặt chẽ bằng cách sử dụng quy cách Khung phần mềm cơ sở cho Arm (FF-A).

Vì yêu cầu về bộ nhớ của pVM có thể thay đổi theo thời gian, nên một hypercall được cung cấp cho phép quyền sở hữu các trang được chỉ định thuộc về người gọi được trả lại cho máy chủ lưu trữ. Trên thực tế, hypercall này được dùng với giao thức balloon virtio để cho phép VMM yêu cầu bộ nhớ từ pVM và để pVM thông báo cho VMM về các trang đã từ bỏ theo cách có kiểm soát.

Hypervisor 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 hoạt động theo dõi trạng thái này được thực hiện bằng cách sử dụng siêu dữ liệu được đính kèm vào bảng trang giai đoạn 2 của máy chủ và khách, bằng cách sử dụng các bit dành riêng trong các mục bảng trang (PTE). Theo tên gọi, các bit này được dành riêng cho việc sử dụng phần mềm.

Máy chủ lưu trữ phải đảm bảo rằng máy chủ này không cố gắng truy cập vào những trang mà siêu giám sát đã không cho phép truy cập. Quyền truy cập trái phép vào máy chủ sẽ khiến siêu giám sát viên 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 được tín hiệu SEGV hoặc khiến nhân máy chủ gặp sự cố. Để ngăn chặn trường hợp truy cập nhầm, nhân hệ điều hành sẽ không cho phép hoán đổi hoặc hợp nhất các trang được cấp cho khách.

Xử lý gián đoạn và bộ hẹn giờ

Gián đoạn là một phần thiết yếu trong cách một khách tương tác với các thiết bị và để giao tiếp giữa các CPU, trong đó gián đoạn giữa các bộ 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ý gián đoạn ảo cho máy chủ lưu trữ ở EL1. Vì mục đích đó, mô hình 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á.

pKVM cung cấp một phiên 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 rằng trạng thái ngắt đầy đủ hiển thị cho máy chủ EL1, bao gồm cả các bản sao của thanh ghi trình giám sát siêu ảo có liên quan đến các gián đoạn. 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 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ợ việc bẫy thanh ghi Thanh ghi ngắt do phần mềm tạo (SGIR) và Thanh ghi vô hiệu hoá ngắt (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 trong việc giảm thiểu lỗi. Mọi thứ khác đều được xử lý bằng phần cứng.

Về phía MMIO, mọi thứ đều được mô phỏng ở 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) 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 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ắt lỗi để EL1 có thể chèn các gián đoạn bộ hẹn giờ trong khi vCPU bị chặn. Bộ hẹn giờ thực được mô phỏng hoàn toàn và tất cả các bẫy đượ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 hoạt động mô phỏng GIC, các bẫy MMIO phải được chuyển tiếp trở lại máy chủ ở EL1 để phân loại thêm. pKVM yêu cầu những điều sau:

  • IPA và kích thước của quyền truy cập
  • Dữ liệu trong trường hợp ghi
  • Endianness của CPU tại thời điểm bẫy

Ngoài ra, các bẫy có thanh ghi đa năng (GPR) làm nguồn/đích đến sẽ được chuyển tiếp bằng cách sử dụng một thanh ghi 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 kết hợp các siêu lệnh và quyền truy cập bộ nhớ vào các vùng bị chặn. Hypercall được hiển thị theo tiêu chuẩn SMCCC, với một dải được KVM dành riêng cho việc phân bổ của nhà cung cấp. Các lệnh gọi siêu dữ liệu sau đây có tầm quan trọng đặc biệt đối với các khách pKVM.

Hypercall chung

  • PSCI cung cấp một cơ chế tiêu chuẩn để khách kiểm soát vòng đời của các vCPU, bao gồm cả việc trực tuyến, ngoại tuyến và tắt hệ thống.
  • TRNG cung cấp một cơ chế tiêu chuẩn để 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 trong trường hợp 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.

pKVM hypercall

  • Chia sẻ kỷ niệm với người tổ chức. Ban đầu, máy chủ không thể truy cập vào tất cả bộ nhớ của khách, nhưng máy chủ cần có quyền truy cập để giao tiếp bằng bộ nhớ dùng chung và cho các thiết bị bán ảo hoá dựa vào bộ đệm dùng chung. Hypercall để chia sẻ và huỷ chia sẻ các trang với máy chủ cho phép khách quyết định chính xác những phần nào của bộ nhớ được cung cấp cho phần còn lại của Android mà không cần bắt tay.
  • Nhường bộ nhớ cho máy chủ. Thông thường, mọi bộ nhớ của khách đều thuộc về khách cho đến khi bộ nhớ đó bị huỷ. Trạng thái này có thể không phù hợp với các VM có thời gian tồn tại lâu 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 relinquish cho phép khách chuyển quyền sở hữu các trang một cách rõ ràng trở lại máy chủ mà không cần phải chấm dứt khách.
  • Bẫy truy cập bộ nhớ vào máy chủ. Theo truyền thống, nếu một khách KVM truy cập vào một địa chỉ không tương ứng với một vùng nhớ hợp lệ, thì luồng vCPU sẽ thoát ra 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 người dùng. Để tạo điều kiện thuận lợi cho việc xử lý này, pKVM phải quảng cáo thông tin chi tiết về chỉ dẫn gây lỗi, chẳng hạn như địa chỉ, tham số đăng ký và có thể là nội dung của chỉ dẫn đó cho 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ột khách được bảo vệ nếu bẫy không được dự đoán. pKVM giải quyết vấn đề này bằng cách coi những lỗi này là lỗi nghiêm trọng, trừ phi khách đã phát hành một siêu lệnh gọi để xác định phạm vi IPA gây lỗi là phạm vi mà các lượt truy cập được phép bẫy trở lại máy chủ lưu trữ. Giải pháp này được gọi là MMIO guard (cơ chế bảo vệ MMIO).

Thiết bị I/O ảo (virtio)

Virtio là một tiêu chuẩn phổ biến, di động và hoàn chỉnh để triển khai và tương tác với các thiết bị bán ảo hoá. 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 hỗ trợ việc triển khai vsock được dùng để giao tiếp giữa một 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ủ. VMM này chặn các hoạt động truy cập bộ nhớ bị chặn từ 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ần truy cập vào thiết bị đều yêu cầu một chuyến đi khứ hồi đến VMM và quay lại. Vì vậy, hầu hết hoạt động truyền dữ liệu thực tế giữa thiết bị và khách đều diễn ra bằng cách sử dụng một nhóm virtqueue trong bộ nhớ. Giả định chính 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 các con trỏ đến các vùng đệm trong khách mà hoạt động mô phỏng thiết bị dự kiến sẽ truy cập trực tiếp.

Mặc dù các siêu lệnh gọi chia sẻ bộ nhớ được mô tả trước đó có thể được dùng để chia sẻ các vùng đệm dữ liệu virtio từ máy khách đến máy chủ lưu trữ, nhưng hoạt động chia sẻ này nhất thiết phải được thực hiện ở mức độ chi tiết của trang và có thể dẫn đến việc hiển thị nhiều dữ liệu hơn mức cần thiết nếu kích thước vùng đệm nhỏ hơn kích thước của một trang. Thay vào đó, khách được định cấu hình để phân bổ cả virtqueue và các 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 được sao chép (truyền) đến và đi từ cửa sổ khi cần.

Thiết bị ảo

Hình 4. Thiết bị Virtio

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 các 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 các vùng đệm bộ nhớ được định địa chỉ vật lý mà máy chủ không truy cập được. Vì phần mềm bảo mật thường không biết khả năng truy cập vào vùng đệm, nên một máy chủ độc hại có thể sử dụng vùng đệm này để thực hiện một cuộc tấn công đại diện bị nhầm lẫn (tương tự như một cuộc tấn công DMA). Để ngăn chặn các cuộc tấn công như vậy, pKVM sẽ chặn tất cả các lệnh gọi SMC của máy chủ lưu trữ đến EL2 và đóng vai trò là một proxy giữa máy chủ lưu trữ và màn hình bảo mật ở 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 với những sửa đổi tối thiểu. Cụ thể, điểm truy cập cho một CPU đang trực tuyến hoặc tiếp tục từ trạng thái tạm ngưng sẽ đượ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ủ 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 chương trình cơ sở EL3.

Khung phần mềm cơ sở cho Arm (FF-A) chuẩn hoá các hoạt động tương tác giữa thế giới bình thường và thế giới bảo mật, đặc biệt là khi có một siêu giám sát viên bảo mật. Một phần quan trọng của quy cách này 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 chung và mô hình quyền được xác định rõ cho các trang cơ bản. pKVM sẽ chuyển tiếp các thông báo FF-A để đảm bảo rằng máy chủ 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.

Cấu trúc này dựa vào phần mềm thế giới an toàn để thực thi mô hình truy cập bộ nhớ, nhằm đả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 an toàn chỉ có thể truy cập vào bộ nhớ nếu bộ nhớ đó thuộc sở hữu riêng của thế giới an toàn hoặc đã được chia sẻ rõ ràng với thế giới an toà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ớ phải được thực hiện bởi Secure Partition Manager Core (SPMC) (Lõi Trình quản lý phân vùng bảo mật), 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ột 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 sụn bảo mật (nhất thiết phải đáng tin cậy) có thể xử lý an toàn các SMC chưa được xử lý vì phần sụn hiểu rõ các biện pháp phòng ngừa cần thiết để duy trì khả năng cách ly pVM.

Trình giám sát máy ảo

crosvm là một trình giám sát máy ảo (VMM) chạy các máy ảo thông qua giao diện KVM của Linux. Điều khiến crosvm trở nên độc đáo là trọng tâm của nó về sự an toàn khi 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ủ lưu trữ. Để biết thêm về crosvm, hãy xem tài liệu chính thức của công cụ này tại đây.

Bộ mô tả tệp và ioctl

KVM cung cấp thiết bị ký tự /dev/kvm cho không gian người dùng bằng các ioctl tạo nên API KVM. Các ioctl thuộc các danh mục sau:

  • Truy vấn và thiết lập các thuộc tính chung ảnh hưởng đến toàn bộ hệ thống con KVM và tạo pVM.
  • VM ioctl 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 duy nhất.
  • Device ioctl 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 duy nhất.

Mỗi quy trình crosvm chạy chính xác một phiên bản của máy ảo. Quy trình này sử dụng ioctl hệ thống KVM_CREATE_VM để tạo một bộ mô tả tệp VM có thể dùng để phát hành ioctl pVM. Một KVM_CREATE_VCPU hoặc KVM_CREATE_DEVICE ioctl trên một VM FD sẽ tạo ra một vCPU/thiết bị và trả về một bộ mô tả tệp trỏ đến tài nguyên mới. ioctl trên một vCPU hoặc thiết bị FD có thể được dùng để kiểm soát thiết bị được tạo bằng ioctl trên một VM FD. Đối với vCPU, việc này bao gồm nhiệm vụ quan trọng là chạy mã khách.

Trong nội bộ, crosvm đăng ký các bộ 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 theo cạnh. Sau đó, nhân 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 bất kỳ bộ mô tả tệp nào.

pKVM bổ sung một chức năng mới là KVM_CAP_ARM_PROTECTED_VM. Chức năng này 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ột 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 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 VM và quản lý bố cục bộ nhớ của VM. crosvm tạo một bố cục bộ nhớ cố định được mô tả sơ bộ 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
Địa chỉ cơ sở bộ nhớ thực 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ớ này được chuyển cho VM để điền vào các vùng nhớ (gọi là memslot) bằng KVM_SET_USER_MEMORY_REGION ioctl. Do đó, tất cả bộ nhớ pVM của khách đều được gán cho phiên bản crosvm quản lý bộ nhớ đó và có thể dẫn đến việc quy trình bị huỷ (kết thúc máy ảo) nếu máy chủ bắt đầu hết bộ nhớ trống. Khi một máy ảo bị dừng, bộ nhớ sẽ tự động bị xoá bởi trình giám sát siêu dữ liệu và được trả về cho nhân 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ẽ được huỷ ánh xạ khỏi không gian địa chỉ thực của máy chủ khi được chuyển cho khách. Ngoại lệ duy nhất là bộ nhớ được khách chia sẻ 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 được để trống. Khách không truy cập được vào các vùng này và dẫn đến sự kiện I/O trên FD của VM. Cơ chế này được dùng để triển khai các thiết bị ảo. Ở chế độ được bảo vệ, khách phải xác nhận rằng một vùng trong không gian địa chỉ của vùng đó sẽ được dùng cho MMIO bằng cách sử dụng một siêu lệnh gọi, để giảm nguy cơ rò rỉ thông tin do vô tình.

Lập lịch

Mỗi CPU ảo được biểu thị bằng một luồng POSIX và được lập lịch bởi trình lập lịch Linux của máy chủ lưu trữ. Luồng gọi ioctl KVM_RUN trên FD vCPU, dẫn đến việc trình ảo hoá chuyển sang ngữ cảnh vCPU khách. Trình lập lịch lưu trữ tính thời gian đã dùng trong ngữ cảnh khách dưới dạng thời gian mà luồng vCPU tương ứng đã dùng. KVM_RUN sẽ trả về khi có một sự kiện mà VMM phải 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, luồng vẫn có thể bị bộ lập lịch của máy chủ lưu trữ ưu tiên, ngoại trừ việc thực thi mã siêu giám sát EL2 (không thể ưu tiên). Bản thân pVM 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 đều được lên lịch như mọi tác vụ không gian người dùng khác, nên chúng 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 cpusets, được tăng cường hoặc giới hạn bằng cách sử dụng tính năng giới hạn mức sử dụng, thay đổi chính sách ưu tiên/lập lịch và nhiều tính năng khác.

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
  • pl030 real time clock (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 được pVM thực thi, tương tự như ROM khởi động của một thiết bị thực. Mục tiêu chính của pvmfw là khởi động quá trình khởi động an toàn và lấy được 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 sẽ được thêm vào quy trình khởi động của một thiết bị có hỗ trợ pKVM:

  1. Trình tải khởi động Android (ABL) tải pvmfw từ phân vùng của nó vào bộ nhớ và xác minh hình ảnh.
  2. ABL lấy các khoá bí mật của Công cụ tạo giá trị nhận dạng thiết bị (DICE) (Giá trị nhận dạng thiết bị kết hợp (CDI) và chuỗi chứng chỉ DICE) từ một Nguồn tin cậy.
  3. ABL lấy các CDI cần thiết cho pvmfw và nối chúng vào tệp nhị phân pvmfw.
  4. ABL thêm một nút vùng 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 và các bí mật mà tệp này lấy được ở bước trước.
  5. ABL chuyển quyền kiểm soát cho Linux và Linux khởi động pKVM.
  6. pKVM huỷ liên kết vùng nhớ pvmfw khỏi bảng trang giai đoạn 2 của máy chủ lưu trữ và bảo vệ vùng nhớ này khỏi máy chủ lưu trữ (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ẽ 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 pVM, crosvm (hoặc một VMM khác) phải tạo một memslot đủ lớn để được điền bằng hình ảnh pvmfw của trình giám sát ảo. VMM cũng bị hạn chế trong danh sách các thanh ghi mà nó có thể đặt giá trị ban đầu (x0-x14 cho vCPU chính, không có cho vCPU phụ). Các thanh ghi còn lại được dành riêng và là một phần của ABI hypervisor-pvmfw.

Khi pVM chạy, trước tiên, trình giám sát ảo sẽ chuyển quyền kiểm soát vCPU chính cho pvmfw. Phần sụn dự kiến crosvm sẽ tải mộ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 độ 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 cây này khỏi bộ nhớ và phân nhánh đến điểm nhậ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ì chương trình cơ sở sẽ đưa ra 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 cung cấp cho đúng phiên bản.