Intel Dunia Virtual. Berlatih

Pada artikel ini saya ingin mempertimbangkan aspek praktis untuk membuat hypervisor sederhana berdasarkan teknologi virtualisasi perangkat keras Intel VMX.

Virtualisasi perangkat keras adalah bidang pemrograman sistem yang agak sempit dan tidak memiliki komunitas besar di Rusia. Saya berharap bahwa materi dalam artikel ini akan membantu mereka yang ingin menemukan virtualisasi perangkat keras dan kemungkinan yang disediakannya. Seperti yang dikatakan di awal, saya ingin mempertimbangkan hanya aspek praktis tanpa menyelam ke dalam teori, sehingga diasumsikan bahwa pembaca akrab dengan arsitektur x86-64 dan setidaknya memiliki gagasan umum tentang mekanisme VMX. Sumber untuk artikel .

Mari kita mulai dengan menetapkan sasaran untuk hypervisor:

  1. Berjalan sebelum memuat OS tamu
  2. Dukungan untuk satu prosesor logis dan 4 GB memori fisik tamu
  3. Memastikan bahwa OS tamu berfungsi dengan baik dengan perangkat yang diproyeksikan di area memori fisik
  4. Pemrosesan VMexits
  5. OS Tamu dari perintah pertama harus dijalankan di lingkungan virtual.
  6. Mengeluarkan informasi debugging melalui port COM (metode universal, mudah diterapkan)

Sebagai OS tamu, saya memilih Windows 7 x32, di mana batasan berikut ditetapkan:

  • Hanya satu inti CPU yang terlibat
  • Opsi PAE dinonaktifkan yang memungkinkan OS 32-bit untuk menggunakan jumlah memori fisik lebih dari 4GB
  • BIOS dalam mode lama, UEFI dinonaktifkan

Deskripsi bootloader


Agar hypervisor mulai ketika PC mulai, saya memilih cara termudah, yaitu, saya menuliskan bootloader saya di sektor MBR dari disk tempat OS tamu diinstal. Itu juga perlu untuk menempatkan kode hypervisor di suatu tempat pada disk. Dalam kasus saya, MBR asli membaca bootloader mulai dari sektor 2048, yang memberikan area bebas bersyarat untuk penulisan hingga (2047 * 512) Kb. Ini lebih dari cukup untuk mengakomodasi semua komponen hypervisor.

Di bawah ini adalah tata letak hypervisor pada disk, semua nilai diatur dalam sektor.



Proses unduhan adalah sebagai berikut:


  1. loader.mbr membaca kode loader loader.main dari disk dan mentransfer kontrol untuk itu.
  2. loader.main beralih ke mode lama, dan kemudian membaca tabel elemen loadload.table, berdasarkan yang memuat lebih lanjut komponen hypervisor ke dalam memori dilakukan.
  3. Setelah bootloader selesai bekerja di memori fisik di alamat 0x100000000 ada kode hypervisor, alamat ini dipilih sehingga rentang dari 0 hingga 0xFFFFFFFFFF dapat digunakan untuk pemetaan langsung ke memori fisik tamu.
  4. Windows mbr asli boot di alamat fisik 0x7C00.

Saya ingin menarik perhatian pada kenyataan bahwa bootloader setelah beralih ke mode lama tidak dapat lagi menggunakan layanan BIOS untuk bekerja dengan disk fisik, jadi saya menggunakan "Advance Host Controller Interface" untuk membaca disk.

Lebih detail tentang yang dapat ditemukan di sini .

Deskripsi Pekerjaan Hypervisor


Setelah hypervisor menerima kontrol, tugas pertamanya adalah menginisialisasi lingkungan di mana ia harus bekerja, untuk melakukan ini, fungsinya disebut secara berurutan:

  • InitLongModeGdt () - membuat dan memuat tabel 4 deskriptor: NULL, CS64, DS64, TSS64
  • InitLongModeIdt (isr_vector) - menginisialisasi 32 vektor interupsi pertama oleh penangan umum, atau lebih tepatnya, rintisannya
  • InitLongModeTSS () - menginisialisasi segmen status tugas
  • InitLongModePages () - inisialisasi paging:

    [0x00000000 - 0xFFFFFFFF] - ukuran halaman 2MB, cacat cache;
    [0x100000000 - 0x13FFFFFFF] - ukuran halaman 2 MB, tulis balasan cache, halaman global;
    [0x140000000 - n] - tidak ada;
  • InitControlAndSegmenRegs () - memuat ulang register segmen

Selanjutnya, Anda perlu memastikan bahwa prosesor mendukung VMX, verifikasi dilakukan oleh fungsi CheckVMXConditions () :

  • CPUID.1: ECX.VMX [bit 5] harus diatur ke 1
  • Dalam register MSR, IA32_FEATURE_CONTROL bit 2 harus disetel - memungkinkan VMXON di luar operasi SMX dan bit 0 - Lock (relevan ketika debugging di Bochs)

Jika semuanya beres dan hypervisor berjalan pada prosesor yang mendukung virtualisasi perangkat keras, buka inisialisasi awal VMX, lihat fungsi InitVMX () :

  • Area memori yang diciptakan VMXON dan VMCS (struktur data kontrol mesin virtual) berukuran 4.096 byte. Pengidentifikasi revisi VMCS yang diambil dari MSR IA32_VMX_BASIC direkam dalam 31 bit pertama dari setiap area.
  • Diperiksa bahwa dalam sistem register CR0 dan CR4 semua bit diatur sesuai dengan persyaratan VMX.
  • Prosesor logis dimasukkan ke mode root vmx oleh perintah VMXON (alamat fisik wilayah VMXON sebagai argumen).
  • Perintah VMCLEAR (VMCS) menetapkan status peluncuran VMCS ke Clear, dan perintah tersebut menetapkan nilai-nilai khusus implementasi ke VMCS.
  • Perintah VMPTRLD (VMCS) memuat alamat VMCS saat ini yang dilewatkan sebagai argumen ke pointer-VMCS saat ini.

Eksekusi OS tamu akan dimulai dalam mode nyata dari alamat 0x7C00 di mana, seperti yang kita ingat, loader.main loader menempatkan win7.mbr. Untuk membuat ulang lingkungan virtual yang identik dengan yang di mana mbr biasanya dieksekusi, fungsi InitGuestRegisterState () dipanggil, yang menetapkan register non-root vmx sebagai berikut:

CR0 = 0x10 CR3 = 0 CR4 = 0 DR7 = 0 RSP = 0xFFD6 RIP = 0x7C00 RFLAGS = 0x82 ES.base = 0 CS.base = 0 SS.base = 0 DS.base = 0 FS.base = 0 GS.base = 0 LDTR.base = 0 TR.base = 0 ES.limit = 0xFFFFFFFF CS.limit = 0xFFFF SS.limit = 0xFFFF DS.limit = 0xFFFFFFFF FS.limit = 0xFFFF GS.limit = 0xFFFF LDTR.limit = 0xFFFF TR.limit = 0xFFFF ES.access rights = 0xF093 CS.access rights = 0x93 SS.access rights = 0x93 DS.access rights = 0xF093 FS.access rights = 0x93 GS.access rights = 0x93 LDTR.access rights = 0x82 TR.access rights = 0x8B ES.selector = 0 CS.selector = 0 SS.selector = 0 DS.selector = 0 FS.selector = 0 GS.selector = 0 LDTR.selector = 0 TR.selector = 0 GDTR.base = 0 IDTR.base = 0 GDTR.limit = 0 IDTR.limit = 0x3FF 

Perlu dicatat bahwa bidang batas cache deskriptor untuk register segmen DS dan ES adalah 0xFFFFFFFF. Ini adalah contoh penggunaan mode tidak nyata - fitur prosesor x86 yang memungkinkan Anda untuk melewati batas segmen dalam mode nyata. Anda dapat membaca lebih lanjut tentang ini di sini .

Saat berada dalam mode bukan-root vmx, OS tamu dapat menghadapi situasi di mana perlu untuk mengembalikan kontrol ke host dalam mode root vmx. Dalam kasus ini, keluar VM terjadi selama kondisi saat ini dari vmx non-root disimpan dan vmx-root dimuat. Inisialisasi vmx-root dilakukan oleh fungsi InitHostStateArea () , yang menetapkan nilai register berikut:

 CR0 = 0x80000039 CR3 = PML4_addr CR4 = 0x420A1 RSP =     STACK64 RIP =   VMEXIT_handler ES.selector = 0x10 CS.selector = 0x08 SS.selector = 0x10 DS.selector = 0x10 FS.selector = 0x10 GS.selector = 0x10 TR.selector = 0x18 TR.base =  TSS GDTR.base =  GDT64 IDTR.base =  IDTR 

Selanjutnya, penciptaan ruang alamat fisik tamu dilakukan ( fungsi InitEPT () ). Ini adalah salah satu momen paling penting ketika membuat hypervisor, karena ukuran atau tipe pengaturan yang salah pada salah satu lokasi memori dapat menyebabkan kesalahan yang mungkin tidak langsung terwujud, tetapi dengan probabilitas tinggi akan menyebabkan rem atau pembekuan OS tamu yang tidak terduga. Secara umum, ada sedikit kesenangan di sini dan lebih baik membayar perhatian yang cukup untuk menyetel memori.

Gambar berikut menunjukkan model ruang alamat fisik tamu:



Jadi apa yang kita lihat di sini:

  • [0 - 0xFFFFFFFF] seluruh rentang ruang alamat tamu. Tipe Default: tulis kembali
  • [0xA0000 - 0xBFFFFF] - Ram video. Ketik: tidak mudah terbakar
  • [0xBA647000 - 0xFFFFFFFF] - Perangkat ram. Ketik: tidak mudah terbakar
  • [0x0000000 - 0xCFFFFFFF] - Video ram. Jenis: tulis kombinasi
  • [0xD0000000 - 0xD1FFFFFF] - Video ram. Jenis: tulis kombinasi
  • [0xFA000000 - 0xFAFFFFFF] - Ram video. Jenis: tulis kombinasi

Saya mengambil informasi untuk membuat area seperti itu dari utilitas RAMMap (tab Physical Ranges). Saya juga menggunakan data dari Windows Device Manager. Tentu saja, pada PC lain, kisaran alamat cenderung berbeda. Adapun tipe memori tamu, dalam implementasi saya, tipe ditentukan hanya oleh nilai yang ditentukan dalam tabel EPT. Ini sederhana, tetapi tidak sepenuhnya benar, dan secara umum jenis memori yang ingin diinstal oleh OS tamu di halamannya harus diperhitungkan.

Setelah pembuatan ruang alamat tamu selesai, Anda dapat melanjutkan ke pengaturan bidang kontrol Eksekusi VM (InitExecutionControlFields () fungsi). Ini adalah seperangkat opsi yang cukup besar yang memungkinkan Anda untuk mengatur kondisi operasi OS tamu dalam mode bukan-root vmx. Anda dapat, misalnya, melacak panggilan ke port input / output atau memantau perubahan dalam register MSR. Tetapi dalam kasus kami, saya hanya menggunakan kemampuan untuk mengontrol pengaturan bit tertentu dalam register CR0. Faktanya adalah bahwa 30 (CD) dan 29 (NW) bit adalah umum untuk kedua mode vmx non-root dan vmx root, dan jika OS tamu menetapkan bit-bit ini ke 1 ini akan berdampak negatif pada kinerja.

Proses mengkonfigurasi hypervisor hampir selesai, tetap hanya untuk menetapkan kontrol atas transisi ke mode tamu vmx non-root dan kembali ke mode host vmx root. Pengaturan diatur dalam fungsi:

Pengaturan InitVMEntryControl () untuk transisi ke vmx non-root:

  • Muat Tamu IA32_EFER
  • Muat Tamu IA32_PAT
  • Muat Tamu MSR (IA32_MTRR_PHYSBASE0, IA32_MTRR_PHYSMASK0, IA32_MTRR_DEF_TYPE)

Pengaturan InitVMExitControl () untuk beralih ke root vmx:

  • Muat Host IA32_EFER;
  • Simpan Tamu IA32_EFER;
  • Muat Host IA32_PAT;
  • Simpan Tamu IA32_PAT;
  • Host.CS.L = 1, Host.IA32_EFER.LME = 1, Host.IA32_EFER.LMA = 1;
  • Simpan MSR Tamu (IA32_MTRR_PHYSBASE0, IA32_MTRR_PHYSMASK0, IA32_MTRR_DEF_TYPE);
  • Muat Host MSR (IA32_MTRR_PHYSBASE0, IA32_MTRR_PHYSMASK0, IA32_MTRR_DEF_TYPE);

Sekarang setelah semua pengaturan selesai, fungsi VMLaunch () menempatkan prosesor dalam mode non-root vmx dan OS tamu mulai berjalan. Seperti yang saya sebutkan sebelumnya, kondisi dapat diatur dalam pengaturan kontrol eksekusi vm, dalam hal ini hypervisor akan mengembalikan kontrol ke dirinya sendiri dalam mode root vmx. Dalam contoh sederhana saya, saya memberikan OS tamu sepenuhnya kebebasan untuk bertindak, namun, dalam beberapa kasus, hypervisor masih perlu campur tangan dan menyesuaikan OS.

  1. Jika OS tamu mencoba untuk mengubah bit CD dan NW dalam register CR0, VM Exit handler
    mengoreksi data yang direkam dalam CR0. Bidang bayangan baca CR0 juga dimodifikasi sehingga saat membaca CR0 OS tamu menerima nilai yang direkam.
  2. Menjalankan perintah xsetbv. Perintah ini selalu memanggil VM Exit, terlepas dari pengaturannya, jadi saya baru saja menambahkan eksekusi dalam mode root vmx.
  3. Menjalankan perintah dewa asmara. Perintah ini juga memanggil keluar VM tanpa syarat. Tapi saya membuat perubahan kecil pada penangannya. Jika nilai dalam argumen eax adalah 0x80000002 - 0x80000004, cpuid tidak akan mengembalikan nama merek prosesor, tetapi baris: VMX Study Core :) Hasilnya dapat dilihat pada tangkapan layar:



Ringkasan


Hypervisor yang ditulis sebagai contoh untuk artikel ini cukup mampu mendukung operasi OS tamu yang stabil, meskipun tentu saja itu bukan solusi yang lengkap. Intel VT-d tidak digunakan, dukungan untuk hanya satu prosesor logis diterapkan, tidak ada kontrol atas gangguan dan pengoperasian perangkat periferal. Secara umum, saya tidak menggunakan hampir apa pun dari set alat yang kaya yang disediakan Intel untuk virtualisasi perangkat keras. Namun, jika komunitas tertarik, saya akan terus menulis tentang Intel VMX, terutama karena ada sesuatu untuk ditulis.

Ya, saya hampir lupa, nyaman untuk men-debug hypervisor dan komponennya menggunakan Bochs. Pada awalnya itu adalah alat yang sangat diperlukan. Sayangnya, mengunduh hypervisor di Bochs berbeda dengan mengunduh ke PC fisik. Pada suatu waktu, saya membuat majelis khusus untuk menyederhanakan proses ini, saya akan mencoba untuk menertibkan sumber dan menyatukannya dengan proyek dalam waktu dekat.

Itu saja. Terima kasih atas perhatian anda

Source: https://habr.com/ru/post/id419065/


All Articles