ভার্চুয়ালাইজেশন তত্ত্ব — Popek-Goldberg থিওরেম ও Trap-and-Emulate
Virtualization Theory: The Popek-Goldberg Theorem and Trap-and-Emulate
Popek ও Goldberg-এর ১৯৭৪ সালের থিওরেম বলে দেয় একটা VMM কবে trap-and-emulate দিয়ে বানানো সম্ভব — শর্ত একটাই, sensitive instruction-এর সেট privileged instruction-এর সেটের subset হতে হবে; x86-এর POPF-এর মতো instruction (sensitive, কিন্তু privileged না) এই শর্ত ভেঙে দেখায় কেন classic x86 কখনো বিশুদ্ধ trap-and-emulate দিয়ে virtualize করা যায়নি।
আগে এটা বুঝি
এই module-এর (Level ১২, Virtualization, Containers ও Cloud) driving question একটাই বাক্যে ধরা যায়:
docker runলিখলে kernel-এ ঠিক কোন কোন syscall হয়?
এই প্রশ্নের সম্পূর্ণ উত্তর আসবে অনেক পরে — container-runtime-oci লেসনে, যখন আমরা clone(), namespace flag, আর OCI runtime spec একে একে খুলে দেখাব। কিন্তু তার আগে একটা আরও বড়, আরও পুরনো প্রশ্নের উত্তর দরকার, যেটার উপর docker run-এর পুরো জগৎ — VM, container, cloud, সবকিছু — দাঁড়িয়ে আছে:
“একটা হার্ডওয়্যারের উপর আরেকটা সম্পূর্ণ কম্পিউটার চালানো” — এই কথাটার মানে ঠিক কী, আর সবসময় কি এটা সম্ভব?
operating-systems/kernel-and-user-space লেসনে আমরা দেখেছিলাম একটা CPU-তে দুইটা privilege level থাকে — kernel space (ring ০) আর user space (ring ৩) — আর এই দেয়ালটা হার্ডওয়্যার নিজেই জোরপূর্বক প্রয়োগ করে। একটা user process কখনো সরাসরি disk driver চালাতে পারে না, page table বদলাতে পারে না — চেষ্টা করলে CPU trap করে, kernel-এ control ফিরে যায়। এই একই যন্ত্র — privilege level, trap — আজকের লেসনের কেন্দ্রে। প্রশ্নটা এবার আরেক ধাপ উপরে: user process না, বরং একটা সম্পূর্ণ guest operating system-কে যদি “user”-এর জায়গায় বসাই, তাহলে কী হয়? সেই guest OS নিজে তো ভাবে সে ring ০-এ আছে, সে-ই আসল kernel — অথচ বাস্তবে তাকে দেয়া হয়েছে ring ৩-এর জুতা।
১৯৭৪ সালে Gerald Popek আর Robert Goldberg এই প্রশ্নটাকে গণিতের ভাষায় বসিয়ে দিলেন, আর একটা আশ্চর্যজনকভাবে তীক্ষ্ণ উত্তর দিলেন — একটা architecture-এ “সহজ, দক্ষ” পদ্ধতিতে ভার্চুয়ালাইজেশন সম্ভব কি না, সেটা নির্ভর করে সেই architecture-এর instruction set-এর একটামাত্র structural property-র উপর। এই থিওরেমটাই আজকের লেসনের বিষয় — আর এর সবচেয়ে বিখ্যাত, সবচেয়ে বিরক্তিকর counterexample হলো x86-এর একটা প্রায়-নিরীহ instruction: POPF।
এই লেসন শেষে আপনি বুঝবেন কেন ২০০৫ সালের আগে x86-কে virtualize করা এত কঠিন কাজ ছিল যে VMware-এর মতো কোম্পানি পুরো একটা ব্যবসা গড়ে তুলেছিল শুধু সেই কঠিনতা সমাধান করে — আর কেন আজকের প্রতিটা cloud VM-এর নিচে (hypervisor-types, পরের লেসন থেকে শুরু করে) এই একই থিওরেমের ছায়া পড়ে আছে।
মূল ধারণা
Virtual Machine Monitor — সংজ্ঞাটা আগে পরিষ্কার করি
Virtual Machine Monitor (VMM), যাকে আজকাল আমরা প্রায়ই hypervisor বলি, হলো এমন একটা সফটওয়্যার (বা firmware) স্তর যা guest software-কে (সাধারণত একটা সম্পূর্ণ OS) এমন একটা পরিবেশ দেয় যা বাস্তব হার্ডওয়্যারের সাথে “essentially identical” — Popek আর Goldberg-এর নিজস্ব ভাষায়। “Essentially identical” মানে ঠিক কী? দুইটা ব্যতিক্রম বাদে, একদম হুবহু:
- Timing — guest-এর কোড কিছুটা ধীর চলতে পারে (VMM-এর overhead-এর কারণে), কিন্তু কোনো cycle-নির্ভুল guarantee কেউ প্রত্যাশা করে না।
- Resource availability — guest হয়তো পুরো মেশিনের memory বা CPU পাবে না, শুধু তার ভাগেরটুকু পাবে (অন্য guest-দের সাথে ভাগ করে)।
এই দুইটা বাদে, guest-এর ভেতরে চলা সফটওয়্যার — কোনো পরিবর্তন ছাড়াই — বাস্তব হার্ডওয়্যারে যেভাবে আচরণ করত, VMM-এর নিচেও ঠিক সেভাবেই আচরণ করবে। এই সংজ্ঞাটাই “virtualization”-কে “emulation” থেকে আলাদা করে — emulation-এ instruction-বাই-instruction ভিন্ন hardware-এর আচরণ নকল করা হয় (ধীর, কিন্তু নমনীয়, ভিন্ন ISA-তেও চলে); virtualization-এ guest-এর instruction একই ISA-তে সরাসরি hardware-এ চলে, শুধু sensitive মুহূর্তগুলোতে VMM মধ্যস্থতা করে (দ্রুত, কিন্তু একই ISA-তেই আটকে থাকে)। এই পার্থক্যটা misconception অংশে আবার আসবে।
Popek-Goldberg-এর তিনটা শর্ত
Popek-Goldberg থিওরেম একটা VMM-কে “সত্যিকারের VMM” বলার জন্য তিনটা আনুষ্ঠানিক শর্ত দেয় — তিনটাই একসাথে থাকতে হবে:
| শর্ত | নাম | বলে |
|---|---|---|
| ১ | Equivalence (Fidelity) | Guest software VMM-এর নিচে ঠিক সেভাবেই আচরণ করবে যেভাবে সে সরাসরি হার্ডওয়্যারে করত — timing আর resource-availability ছাড়া আর কোনো observable পার্থক্য নেই |
| ২ | Resource Control (Safety) | VMM-এর সম্পূর্ণ নিয়ন্ত্রণ থাকবে system resource-এর উপর — কোনো guest কখনো VMM-এর অনুমতি ছাড়া সরাসরি resource বদলাতে বা পড়তে পারবে না |
| ৩ | Efficiency (Performance) | Instruction-গুলোর একটা statistically dominant অংশ — অর্থাৎ প্রায় সবগুলো — VMM-এর কোনোরকম মধ্যস্থতা ছাড়াই সরাসরি হার্ডওয়্যারে execute হবে |
তিনটা শর্তের মধ্যে টান লক্ষ্য করুন — Equivalence আর Efficiency একে অপরের বিরুদ্ধে টানে। যদি VMM প্রতিটা instruction intercept করে software-এ interpret করত (pure emulation), Resource Control আর Equivalence সহজেই মিলত, কিন্তু Efficiency ভয়াবহভাবে ভেঙে পড়ত — প্রতিটা instruction-এর জন্য একটা software interpreter loop চালাতে হতো, হাজার গুণ ধীর। Popek-Goldberg-এর আসল প্রশ্ন: কবে এই তিনটা শর্ত একসাথে রক্ষা করা সম্ভব, বিশেষভাবে Efficiency বলি না দিয়ে?
Trap-and-Emulate — দক্ষতার আসল কৌশল
তাহলে কীভাবে Efficiency বজায় রেখে Equivalence আর Resource Control পাওয়া যায়? Popek-Goldberg-এর উত্তর হলো trap-and-emulate — kernel-and-user-space লেসনের সেই trap মেকানিজমের সরাসরি পুনর্ব্যবহার, শুধু নতুন প্রসঙ্গে:
- Guest OS-কে deprivilege করা হয় — guest যেই code সাধারণত ring ০-এ (kernel privilege) চালাত, VMM সেটাকে ring ১ বা ring ৩-এ (user/non-privileged) চালায়। VMM নিজে আসল ring ০-এ থাকে।
- বেশিরভাগ instruction (arithmetic, ordinary memory access, branch) privilege-নির্বিশেষে একইভাবে কাজ করে — এইগুলো guest-এ সরাসরি, বিনা বাধায়, পুরো হার্ডওয়্যার গতিতে execute হয়। এটাই Efficiency শর্তের বাস্তবায়ন — statistically dominant অংশ সরাসরি চলে।
- যেই মুহূর্তে guest একটা privileged instruction চালানোর চেষ্টা করে (যেমন page table বদলানো, interrupt controller ছোঁয়া), CPU দেখে guest এখন প্রকৃত ring ০-এ নেই — trap হয়। Control VMM-এর হাতে চলে যায়।
- VMM এই trap-টা ধরে, বোঝে guest ঠিক কী করতে চেয়েছিল, আর সেই effect-টা emulate করে — কিন্তু বাস্তব হার্ডওয়্যার resource-এ না, guest-এর নিজস্ব virtual state-এ (virtual page table, virtual interrupt flag, ইত্যাদি)। তারপর guest-কে আবার চালু করে দেয়, guest বুঝতেই পারে না মাঝখানে VMM হস্তক্ষেপ করেছিল।
এইটুকুই — trap + emulate — যদি নিখুঁতভাবে কাজ করে, guest-এর কাছে সবকিছু “essentially identical” মনে হবে (Equivalence), VMM-এর হাতেই থাকবে আসল resource (Resource Control), আর বেশিরভাগ instruction সরাসরি চলবে (Efficiency)। প্রশ্ন হলো — এটা কি সবসময় কাজ করে?
Sensitive বনাম Privileged — থিওরেমের মূল পার্থক্য
উত্তরটা নির্ভর করে দুইটা আলাদা ধরনের instruction classification-এর সম্পর্কের উপর:
- একটা instruction privileged যদি এটা kernel mode-এ trap ছাড়াই চলে, কিন্তু user mode-এ চালালে trap করে (হার্ডওয়্যার নিজেই এই ভাগ করে,
kernel-and-user-spaceলেসনের ring-check যন্ত্র)। - একটা instruction sensitive যদি এটা হয় (ক) system resource বদলায় (memory map, interrupt flag, timer, I/O device) — একে বলে control-sensitive, অথবা (খ) এর ফলাফল বা আচরণ CPU-র বর্তমান privilege mode বা resource configuration-এর উপর নির্ভর করে — একে বলে behavior-sensitive।
লক্ষ্য করুন এই দুইটা vocabulary সম্পূর্ণ স্বাধীন সংজ্ঞা — একটা instruction “sensitive” কি না তা নির্ধারণ করে তার semantic effect; “privileged” কি না তা নির্ধারণ করে হার্ডওয়্যার কীভাবে সেটা বাস্তবায়ন করেছে (trap করবে কি না)। একটা architecture ডিজাইন করার সময় এই দুইটা ঠিক এক না-ও হতে পারে — আর ঠিক এখানেই থিওরেমের হৃদয়:
যুক্তিটা কেন এমন, ধাপে ধাপে দেখা যাক। ধরুন architecture-এ sensitive ⊆ privileged শর্ত সত্যি। তাহলে প্রতিটা sensitive instruction, guest যেই deprivileged mode-এ চলছে, সেখানে চালানোর চেষ্টা করলেই trap করবে (যেহেতু সেটা privileged-ও বটে)। VMM প্রতিটা sensitive মুহূর্ত ধরতে পারবে, নিশ্চিতভাবে — কোনো sensitive instruction VMM-এর নজর এড়িয়ে যাবে না। তাই VMM-এর Resource Control আর Equivalence দুইটাই রক্ষা পায়, আর non-sensitive instruction-গুলো (যেগুলো সংখ্যায় বিশাল সংখ্যাগরিষ্ঠ) সরাসরি চলে বলে Efficiency-ও রক্ষা পায়।
এবার উল্টো দিকটা দেখুন — যদি একটা sensitive instruction থাকে যেটা privileged না। তাহলে সেই instruction guest-এর deprivileged mode-এও নীরবে, trap ছাড়াই চলবে। VMM কখনো জানবে না এটা ঘটেছে — কারণ trap-and-emulate পুরো পদ্ধতিটাই trap-এর উপর নির্ভরশীল, আর trap না হলে VMM-এর কাছে কোনো সংকেতই পৌঁছায় না। অথচ instruction-টা sensitive, মানে তার একটা প্রকৃত effect আছে system resource-এর উপর — যেই effect-টা এখন VMM-এর নিয়ন্ত্রণের বাইরে, guest-এর virtual state-এর সাথে সামঞ্জস্যহীনভাবে ঘটে গেল। Equivalence ভেঙে গেল (guest-এর ভাবা state আর VMM-এর ট্র্যাক-করা state আলাদা হয়ে গেল), অথবা Resource Control ভেঙে গেল (guest সরাসরি resource-এ প্রভাব ফেলল VMM-কে পাশ কাটিয়ে)। এই একটামাত্র instruction যথেষ্ট পুরো architecture-কে “classically virtualizable না” ঘোষণা করার জন্য।
ভেতরে কী ঘটছে
ভেতরে কী ঘটছে — একটা trapped instruction-এর জীবনচক্র
আগের অংশে trap-and-emulate-এর ধারণাটা বর্ণনা করেছি; এবার একটা নির্দিষ্ট instruction (ধরুন guest kernel CLI — Clear Interrupts — চালাচ্ছে, ইন্টারাপ্ট বন্ধ করতে চায়, একটা critical section-এ ঢোকার আগে) কোন কোন ধাপ পার হয় তা trace করি:
- guest kernel executes CLIGuest নিজেকে ring ০ ভাবছে, স্বাভাবিকভাবে interrupt বন্ধ করার instruction চালাচ্ছে
- হার্ডওয়্যার privilege checkCPU দেখে বর্তমান real privilege ring ০ না (guest deprivileged) — CLI privileged instruction, তাই ট্র্যাপ শর্ত পূরণ হলো
- trap — control VMM-এCPU guest-কে থামায়, execution VMM-এর trap handler-এ jump করে (kernel-and-user-space লেসনের syscall trap-এর ঠিক একই হার্ডওয়্যার যন্ত্র, ভিন্ন প্রসঙ্গে)
- VMM: কোন instruction, কোন guest?VMM trapped instruction decode করে বোঝে এটা CLI ছিল, আর কোন guest (vCPU) থেকে এসেছে
- emulate — virtual state আপডেটVMM নিজের রাখা প্রতিটা guest-এর জন্য আলাদা virtual interrupt-flag structure-এ এই guest-এর virtual IF = 0 বসায় — real hardware IF ছোঁয় না
- guest-এ ফেরত (resume)VMM guest-এর পরবর্তী instruction থেকে execution আবার শুরু করে — guest বুঝতেই পারে না মাঝে VMM চলেছিল
লক্ষ্য করুন — এই পুরো প্রক্রিয়াটা guest-এর কাছে transparent। Guest দেখছে সে CLI চালিয়েছে, execution পরের instruction-এ চলে গেছে — ঠিক যেমনটা সে বাস্তব হার্ডওয়্যারে প্রত্যাশা করত। শুধু সময় একটু বেশি লেগেছে (trap-এর overhead) — Equivalence শর্তের ঠিক সেই “timing ছাড়া identical” clause।
কেন এই মেকানিজম নীরবে ভেঙে যেতে পারে
উপরের trace-টা কাজ করে কারণ CLI privileged (trap করে) এবং sensitive (interrupt state বদলায় — একটা system resource)। এখন কল্পনা করুন একটা দ্বিতীয় instruction, POPF (Pop Flags — stack থেকে একটা মান তুলে পুরো flags register-এ বসানো, যার মধ্যে interrupt-enable বিটও থাকে)। এটাও sensitive — interrupt state বদলাতে পারে। কিন্তু বাস্তব x86-এ (২০০৫-পূর্ব, VT-x আসার আগে) এটা privileged না — user mode-এ চালালে CPU trap করে না। তার বদলে, x86-এর architecture-নির্ধারিত আচরণ অনুযায়ী, user mode-এ (CPL > ০) POPF চললে interrupt-flag বিটের উপর লেখাটা silently drop হয়ে যায় — instruction “সফলভাবে” শেষ হয় (কোনো fault, কোনো exception, কোনো সংকেত), কিন্তু IF বিটটা বদলায়ই না।
guest: CLI (sensitive + privileged) guest: POPF (sensitive + NOT privileged)
│ │
▼ ▼
hardware privilege check hardware privilege check
│ fails (guest deprivileged) │ passes — POPF trap করে না!
▼ ▼
TRAP ──────────► VMM দেখে, emulate করে সরাসরি execute হয়, IF-write
virtual IF সঠিকভাবে বসে silently DROP — কোনো trap নেই
│
▼
VMM কখনো জানতেই পারল না
এই instruction ঘটেছেএইখানেই Equivalence ভাঙে — guest-এর মানসিক মডেল বলছে “আমি POPF চালিয়েছি, IF এখন আমার stack-এ save করা মান অনুযায়ী” (কারণ instruction fault করেনি, guest-এর কাছে সব স্বাভাবিক দেখাচ্ছে), কিন্তু VMM-এর ট্র্যাক করা virtual IF-এর মান পুরনোই রয়ে গেছে, কারণ VMM কখনো trap-ই পায়নি। দুইটা state আলাদা হয়ে গেল, নীরবে, কোনো error ছাড়াই। build অংশে এটাকে আমরা code চালিয়ে হাতে-কলমে দেখব।
উদাহরণ
x86-এর “sensitive কিন্তু privileged নয়” instruction-এর তালিকা
Robin ও Irvine-এর ২০০০ সালের গবেষণা (references-এ তালিকাভুক্ত) classic x86 (Pentium-era, VT-x-পূর্ব) architecture-এ এমন ১৭টা instruction চিহ্নিত করেছিল যেগুলো sensitive অথচ privileged না — এই ১৭টার প্রতিটাই একা একা x86-কে “classically virtualizable না” প্রমাণ করার জন্য যথেষ্ট। কয়েকটা উদাহরণ, কী সমস্যা তা-সহ:
| Instruction | কী করে | কেন sensitive | কেন privileged না |
|---|---|---|---|
POPF / PUSHF | Flags register pop/push (IF বিট-সহ) | Interrupt-enable state বদলায়/পড়ে — system resource | User mode-এ IF-write silently drop হয়, trap হয় না |
SGDT / SIDT | Global/Interrupt Descriptor Table-এর ঠিকানা পড়ে | System-এর memory layout (একটা resource configuration) প্রকাশ করে | এগুলো শুধু পড়ে — read-only হওয়ায় x86 ডিজাইনাররা এদের privileged বানাননি, কিন্তু guest-এর ভার্চুয়াল আর real ঠিকানা আলাদা হলে ফাঁস (leak) ঘটে |
SMSW | Machine Status Word পড়ে (protected mode বিট-সহ) | CPU-র বর্তমান mode configuration প্রকাশ করে | Read instruction, trap করার দরকার মনে হয়নি ডিজাইনে, কিন্তু virtual-বনাম-real mode গুলিয়ে যায় |
LAR / LSL | Segment descriptor-এর access rights/limit পড়ে | ফলাফল বর্তমান privilege level-এর উপর নির্ভরশীল (behavior-sensitive) | Ordinary user-level instruction হিসেবেই ডিজাইন করা, trap করে না |
লক্ষ্য করুন প্যাটার্নটা — এই instruction-গুলোর কোনোটাই “বিপজ্জনক” দেখতে না, কোনোটাই malicious কোড-এর signature না। এরা সবাই normal-looking, প্রায়ই read-only বা “শুধু flags/status পড়ার” instruction — ঠিক এই কারণেই x86-এর মূল ডিজাইনাররা (১৯৭০-৮০ দশকে, ভার্চুয়ালাইজেশনের কথা মাথায় না রেখে) এদের privileged বানাননি। এই instruction-গুলো ডিজাইন করা হয়েছিল single-OS, non-virtualized world-এর জন্য, যেখানে “guest ring ৩-এ বসে ring ০-এর কথা ভাবছে” — এই দৃশ্যকল্পটাই অস্তিত্বহীন ছিল।
ছবিতে
দুই architecture, একই কৌশল, ভিন্ন পরিণতি
CLASSICALLY VIRTUALIZABLE (sensitive ⊆ privileged, যেমন IBM System/370)
┌─────────────────────────────────────────────────────────────┐
│ guest instruction stream │
│ [ordinary] [ordinary] [SENSITIVE+priv] [ordinary] [SENSITIVE+priv] │
│ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ │
│ সরাসরি সরাসরি TRAP সরাসরি TRAP │
│ হার্ডওয়্যারে হার্ডওয়্যারে → VMM emulate হার্ডওয়্যারে → VMM emulate │
│ │
│ ফলাফল: VMM প্রতিটা sensitive মুহূর্ত দেখে — Equivalence অক্ষত │
└─────────────────────────────────────────────────────────────┘
NON-VIRTUALIZABLE (sensitive ⊄ privileged, classic x86)
┌─────────────────────────────────────────────────────────────┐
│ guest instruction stream │
│ [ordinary] [ordinary] [SENSITIVE+priv] [ordinary] [SENSITIVE, NOT priv] │
│ │ │ │ │ │ │
│ ▼ ▼ ▼ ▼ ▼ │
│ সরাসরি সরাসরি TRAP সরাসরি সরাসরি চলে, │
│ হার্ডওয়্যারে হার্ডওয়্যারে → VMM emulate হার্ডওয়্যারে কোনো TRAP নেই! │
│ │ │
│ VMM কখনো জানল না এটা ঘটেছে ◄───┘
│ ফলাফল: guest-এর state আর VMM-এর tracked state আলাদা — Equivalence ভাঙল │
└─────────────────────────────────────────────────────────────┘এই দুই ছবির মধ্যে পার্থক্য একটামাত্র instruction — কিন্তু সেই একটা instructionই যথেষ্ট পুরো architecture-এর classification বদলে দিতে। এটাই থিওরেমের practical নির্মমতা: classically virtualizable হওয়া একটা all-or-nothing সম্পত্তি, একটা architecture-এর, “প্রায় virtualizable” বলে কিছু নেই।
নিজে চালিয়ে দেখুন
আপনার নিজের CPU হার্ডওয়্যার-ভিত্তিক ভার্চুয়ালাইজেশন সাপোর্ট করে কি না দেখুন
x86-এর classic trap-and-emulate ভাঙার সমস্যাটা সমাধান হয়েছিল ২০০৫-০৬ সালে, Intel VT-x আর AMD-V আসার পর — একটা নতুন CPU mode (VMX root/non-root) যেখানে সব sensitive instruction (POPF-সহ) guest mode-এ trap করে, থিওরেমের শর্তটা হার্ডওয়্যারে জোরপূর্বক সত্য করে দিয়ে (পরের module-এর hardware-virtualization লেসনের বিষয়)। আপনার নিজের মেশিনে এই সাপোর্ট আছে কি না দেখা যায় সরাসরি:
macOS-এ (Intel বা Apple Silicon):
sysctl kern.hv_supportএই কমান্ডটা এই লেসন লেখার মেশিনে (macOS, Intel Core i7-9750H) সত্যিই চালানো হয়েছে — প্রকৃত output:
kern.hv_support: 11 মানে Hypervisor.framework (macOS-এর নিজস্ব VMM API, hypervisor-types লেসনে বিস্তারিত) হার্ডওয়্যার-সাপোর্টেড ভার্চুয়ালাইজেশন ব্যবহার করতে পারবে — অর্থাৎ এই CPU-তে VT-x (Intel) বা এর সমতুল্য (Apple Silicon-এ ARM-এর নিজস্ব virtualization extension) সক্রিয়।
Linux-এ (সমতুল্য চেক):
grep -Eo '(vmx|svm)' /proc/cpuinfo | sort -uvmx (Intel) বা svm (AMD) দেখালে হার্ডওয়্যার-সাপোর্ট বিদ্যমান — output প্রতিটা মেশিনে ভিন্ন হবে (CPU model, BIOS-এ virtualization enable করা আছে কি না তার উপর নির্ভর করে), তাই এখানে একটা নির্দিষ্ট প্রত্যাশিত output দেওয়া সম্ভব না — নিজের মেশিনে চালিয়ে দেখুন vmx বা svm (বা দুটোই না) আসে কি না।
যদি সাপোর্ট না থাকে (পুরনো CPU, বা BIOS-এ নিষ্ক্রিয়) তাহলে কী? থিওরেমের ভাষায়: x86 sensitive ⊆ privileged শর্ত মেটায় না, আর হার্ডওয়্যার-সহায়তাও নেই — তখন VMM-কে বাধ্য হয়ে হয় (ক) binary translation করতে হবে (sensitive-but-unprivileged instruction-গুলো runtime-এ খুঁজে বের করে অন্য, নিরাপদ instruction দিয়ে প্রতিস্থাপন — VMware-এর ১৯৯৮-২০০৬ সময়ের মূল কৌশল), বা (খ) guest OS-এর সোর্স বদলে দিতে হবে যাতে সে sensitive instruction-এর বদলে সরাসরি VMM-কে call করে (paravirtualization — Xen-এর কৌশল)। দুইটা পথই পরের কয়েকটা লেসনে বিস্তারিত আসবে।
আধুনিক CPU-তে এই সমস্যার সমাধান কীভাবে হার্ডওয়্যারে বেক করা হয়েছে (VT-x/AMD-V — পরের module-এর hardware-virtualization লেসনের বিষয়) তার একটা সরাসরি, নিজের মেশিনে-যাচাইযোগ্য প্রমাণ — sensitive-but-unprivileged সমস্যাটা যে বাস্তব ছিল, আর তার সমাধান যে আজ প্রতিটা ল্যাপটপে বসানো আছে।
নিজে বানান
Trap-and-Emulate Simulator — একটা toy CPU-তে থিওরেমের ব্যর্থতা নিজের চোখে দেখুন
- একটা TinyCPU ক্লাস বানান — privilege bit (০=kernel, ১=user) আর privileged_set (কোন instruction trap করবে তার তালিকা)
- execute() মেথডে privilege check বসান — instruction privileged_set-এ থাকলে এবং privilege != 0 হলে Trap exception তুলুন
- একটা run_native() ফাংশন বানান — guest সরাসরি kernel privilege-এ, কোনো VMM ছাড়া, এটাই "সঠিক" ground truth আচরণ
- একটা run_under_vmm() ফাংশন বানান — guest-কে privilege=1-এ চালান, Trap ধরে VMM নিজের virtual state (vmm_if) emulate করুক, guest-এর নিজের বিশ্বাস (guest_believes_if) আলাদাভাবে ট্র্যাক করুন
- একই instruction sequence দুইটা privileged_set দিয়ে চালান — একবার POPF privileged না রেখে (বাস্তব x86), একবার POPF-ও privileged রেখে (কাল্পনিক পরিষ্কার ISA) — এবং guest বনাম VMM state-এর mismatch তুলনা করুন
এই simulator-টা kernel-and-user-space লেসনের privilege-check যন্ত্রের সরাসরি সম্প্রসারণ — শুধু এবার privilege check ব্যর্থ হলে (trap) সেটা ধরার জন্য একটা VMM বসানো হয়েছে, আর প্রশ্নটা হলো: guest যা ভাবছে ঘটেছে, আর VMM যা ট্র্যাক করছে ঘটেছে — এই দুইটা কখন আলাদা হয়ে যায়।
# trap_and_emulate_sim.py
# Popek-Goldberg trap-and-emulate — একটা toy CPU-তে "sensitive but not
# privileged" instruction (POPF-এর মতো) trap-and-emulate-কে কীভাবে নীরবে
# ভাঙে তার concrete simulation।
class Trap(Exception):
"""CPU privilege check ব্যর্থ হলে এই exception-ই আসল 'trap'।"""
def __init__(self, opcode, operand):
self.opcode = opcode
self.operand = operand
class TinyCPU:
"""
privilege: 0 = kernel (সব instruction অবাধে চলে), 1 = user
privileged_set-এ যা আছে শুধু তা-ই user mode-এ trap করে — POPF
ইচ্ছাকৃতভাবে privileged_set-এর বাইরে রাখা যাবে (x86-এর বাস্তব আচরণ)।
"""
def __init__(self, privileged_set):
self.privilege = 0
self.if_flag = 0 # real, hardware-owned interrupt-enable bit
self.privileged_set = privileged_set
def execute(self, opcode, operand):
if opcode in self.privileged_set and self.privilege != 0:
raise Trap(opcode, operand) # সত্যিকারের hardware trap
# trap না হলে — user mode-এও — instruction "সফলভাবে" চলে (fault নেই)
if opcode in ("STI", "CLI"):
self.if_flag = operand
elif opcode == "POPF":
if self.privilege == 0:
self.if_flag = operand # kernel mode: সত্যিকারের POPF ঠিকঠাক লেখে
# else: user mode-এ IF-write silently DROP — কোনো fault, কোনো সংকেত নেই
def run_native(program):
"""Baseline — guest kernel সরাসরি real hardware-এ, kernel privilege-এ
চলছে (VMM নেই)। এটাই 'সঠিক' ground truth আচরণ।"""
cpu = TinyCPU(privileged_set={"STI", "CLI"})
trace = []
for opcode, operand in program:
cpu.execute(opcode, operand)
trace.append((opcode, cpu.if_flag))
return trace
def run_under_vmm(program, privileged_set):
"""
Guest kernel-কে VMM privilege=1 (deprivileged/user mode)-এ চালায় —
trap-and-emulate কৌশল। privileged_set-এ থাকা instruction user mode-এ
trap করলে VMM-এর trap handler emulate করে তার নিজস্ব 'virtual' IF
(vmm_if) ঠিকভাবে বসায় — সেই vmm_if-ই আসল interrupt delivery নিয়ন্ত্রণ
করে। যেই instruction privileged_set-এর বাইরে অথচ sensitive (আমাদের
POPF), সেটা trap-ই করে না — VMM কখনো জানতেই পারে না instruction-টা
ঘটেছে।
guest_believes_if সবসময়ই instruction-এর operand — কারণ guest-এর
দৃষ্টিকোণ থেকে trap-and-emulate transparent হওয়ার কথা: trap হোক বা
silent no-op হোক, guest কোনো fault দেখে না, তাই ধরে নেয় IF এখন তার
intended মান।
"""
cpu = TinyCPU(privileged_set=privileged_set)
cpu.privilege = 1 # guest kernel deprivileged
vmm_if = 0 # VMM-tracked virtual IF
trace = []
for opcode, operand in program:
try:
cpu.execute(opcode, operand)
trapped = False
except Trap as t:
trapped = True
# --- VMM trap handler: guest-এর হয়ে emulate করে ---
vmm_if = t.operand
cpu.privilege = 1 # emulate শেষে guest আবার user mode-এ ফেরে
guest_believes_if = operand # guest-এর mental model — fault দেখেনি, তাই সফল ধরে নেয়
trace.append((opcode, trapped, guest_believes_if, vmm_if))
return trace
PROGRAM = [
("STI", 1), # ইন্টারাপ্ট চালু করো
("CLI", 0), # একটা critical section-এ ঢোকার আগে বন্ধ করো
("POPF", 1), # আগের save করা flags ফেরত আনো (তখন IF=1 ছিল) — বেরিয়ে আসার সময়
]
def show(rows, header):
print(header)
print(f"{'opcode':<7}{'trapped?':<10}{'guest ভাবছে IF=':<18}{'VMM-tracked IF=':<18}{'ফলাফল'}")
for opcode, trapped, guest_if, vmm_if in rows:
verdict = "MATCH" if guest_if == vmm_if else "MISMATCH <-- Popek-Goldberg ভঙ্গ!"
print(f"{opcode:<7}{str(trapped):<10}{guest_if:<18}{vmm_if:<18}{verdict}")
print()
print("== ধাপ ১: native ground truth (guest সরাসরি kernel privilege-এ, VMM নেই) ==")
for opcode, if_after in run_native(PROGRAM):
print(f" {opcode:<5} -> real if_flag = {if_after}")
print()
show(
run_under_vmm(PROGRAM, {"STI", "CLI"}),
"== ধাপ ২: ISA-A (বাস্তব x86-এর মতো) — privileged_set = {STI, CLI}; POPF privileged নয় ==",
)
show(
run_under_vmm(PROGRAM, {"STI", "CLI", "POPF"}),
"== ধাপ ৩: ISA-B (কাল্পনিক 'পরিষ্কার' ISA) — privileged_set = {STI, CLI, POPF} ==",
)প্রকৃত output (এই স্ক্রিপ্টটা হাতে চালিয়ে যাচাই করা হয়েছে, হুবহু এটাই আসবে):
== ধাপ ১: native ground truth (guest সরাসরি kernel privilege-এ, VMM নেই) ==
STI -> real if_flag = 1
CLI -> real if_flag = 0
POPF -> real if_flag = 1
== ধাপ ২: ISA-A (বাস্তব x86-এর মতো) — privileged_set = {STI, CLI}; POPF privileged নয় ==
opcode trapped? guest ভাবছে IF= VMM-tracked IF= ফলাফল
STI True 1 1 MATCH
CLI True 0 0 MATCH
POPF False 1 0 MISMATCH <-- Popek-Goldberg ভঙ্গ!
== ধাপ ৩: ISA-B (কাল্পনিক 'পরিষ্কার' ISA) — privileged_set = {STI, CLI, POPF} ==
opcode trapped? guest ভাবছে IF= VMM-tracked IF= ফলাফল
STI True 1 1 MATCH
CLI True 0 0 MATCH
POPF True 1 1 MATCHতিনটা পর্যবেক্ষণ, খুব সাবধানে পড়ুন:
১. ধাপ ১ (native) দেখাচ্ছে “সঠিক” আচরণ কী হওয়ার কথা — শেষ পর্যন্ত if_flag = 1, কারণ guest নিজে সরাসরি kernel-এ চলছে, কোনো middle-man নেই।
২. ধাপ ২ (ISA-A)-তে STI আর CLI দুইটাই trap করে, VMM ঠিকভাবে emulate করে — MATCH। কিন্তু POPF trap করে না (privileged_set-এর বাইরে) — guest ভাবছে IF এখন 1 (instruction fault করেনি), কিন্তু VMM-এর tracked vmm_if এখনও 0 (CLI-এর পর যা বসেছিল, POPF সেটা কখনো ছুঁতেই পারেনি VMM-এর দৃষ্টিতে) — MISMATCH। এই একটা লাইনই পুরো থিওরেমের ব্যর্থতা-শর্তের প্রমাণ, সংখ্যায়।
৩. ধাপ ৩ (ISA-B)-তে শুধু একটা জিনিস বদলেছে — POPF-কেও privileged_set-এ ঢুকিয়ে দেওয়া হয়েছে (একটা কাল্পনিক, “পরিষ্কার” ISA যেখানে sensitive ⊆ privileged সত্যিই সত্য)। ফলাফল: তিনটা ধাপেই MATCH — trap-and-emulate নিখুঁতভাবে কাজ করে। এই একটামাত্র পরিবর্তনই — একটা instruction-কে privileged বানানো — পুরো architecture-কে “ভাঙা” থেকে “classically virtualizable”-এ নিয়ে গেছে। এটাই থিওরেমের practical অর্থ: এটা কোনো abstract গণিত না, একটা পরীক্ষাযোগ্য, ইঞ্জিনিয়ারিং-স্তরের শর্ত।
নিজে বাড়ান: SGDT-এর মতো একটা read-only sensitive instruction যোগ করুন যা if_flag না বদলে বরং একটা কাল্পনিক mem_layout variable পড়ে — দেখুন read-sensitivity একই প্যাটার্নে কীভাবে তথ্য-ফাঁস (leak) তৈরি করে, state-mismatch না হলেও।
বাস্তব সিস্টেমে
থিওরেম বাস্তব ইতিহাসে
-
IBM System/370 (১৯৭০-এর দশক) — Popek-Goldberg থিওরেমের আসল অনুপ্রেরণা। IBM সচেতনভাবে এই architecture ডিজাইন করেছিল যাতে সব sensitive instruction privileged হয় — VM/370 hypervisor (আজকের z/VM-এর পূর্বসূরি) তাই শুরু থেকেই বিশুদ্ধ trap-and-emulate দিয়ে কাজ করতে পেরেছিল, কোনো software workaround ছাড়াই।
-
VMware-এর জন্ম (১৯৯৮) — x86 classically virtualizable না হওয়া সত্ত্বেও ভার্চুয়ালাইজেশন সম্ভব করার জন্য VMware binary translation ব্যবহার করেছিল — guest kernel code runtime-এ স্ক্যান করে sensitive-but-unprivileged instruction-গুলো (POPF-সহ পুরো ১৭টার তালিকা) খুঁজে বের করে, সেগুলোকে এমন code দিয়ে প্রতিস্থাপন করত যা সরাসরি VMM-কে trap করাত। এই কৌশলটাই দশ বছর ধরে (২০০৬ পর্যন্ত) x86 virtualization-এর একমাত্র ব্যবহারিক পথ ছিল, আর একটা গোটা কোম্পানির (VMware) ব্যবসায়িক ভিত্তি।
-
Xen ও paravirtualization (২০০৩) — সম্পূর্ণ ভিন্ন সমাধান: guest OS-এর সোর্স কোড বদলে দাও, যাতে সে কখনো sensitive-but-unprivileged instruction চালানোরই চেষ্টা না করে — বদলে সরাসরি একটা explicit hypercall (VMM-এর সাথে সহযোগিতামূলক function call) করে। এতে guest unmodified থাকে না (Linux kernel-এর একটা paravirt-aware সংস্করণ দরকার হয়), কিন্তু trap-detect করার দরকারই পড়ে না — সমস্যাটা গোড়া থেকেই এড়ানো হয়।
Xen and the Art of Virtualizationপেপার (references-এ) এই কৌশলের জন্মপত্র। -
Intel VT-x ও AMD-V (২০০৫-০৬) — চূড়ান্ত সমাধান, হার্ডওয়্যারে: একটা নতুন CPU mode যোগ করা হলো (VMX root/non-root operation) যেখানে guest সত্যিকারের ring ০-এ চলে (তার নিজের privilege অক্ষত), কিন্তু একটা দ্বিতীয়, উপরের স্তরের control (VMCS — Virtual Machine Control Structure) নির্ধারণ করে দেয় কোন instruction VM exit ঘটাবে। ফলাফল:
POPF-সহ সব sensitive instruction, চাইলে, VMM-এর কাছে trap করানো যায় — থিওরেমের শর্তটা সফটওয়্যার কৌশল ছাড়াই, সরাসরি হার্ডওয়্যারে জোরপূর্বক সত্য করে দেওয়া হলো। এই লেসনের experiment-এ যাচাই করাhv_support/vmxফ্ল্যাগ ঠিক এই মেকানিজমের উপস্থিতি নির্দেশ করে — module-এরhardware-virtualizationলেসনে এর ভেতরটা বিস্তারিত খোলা হবে। -
ARM-এর নিজস্ব সমস্যা ও সমাধান — ARM-এরও (pre-virtualization-extension) নিজস্ব sensitive-but-unprivileged instruction ছিল (উদাহরণ: কিছু status-register read/write instruction যেগুলো user mode-এ ভিন্ন আচরণ করত কিন্তু trap করত না)। ARM নিজস্ব Virtualization Extensions (VE, ARMv7-এ শুরু, ARMv8-এ পরিণত) যোগ করেছিল ঠিক একই সমস্যার সমাধানে — x86-এর VT-x-এর সমান্তরাল ইতিহাস, প্রমাণ করে এটা x86-নির্দিষ্ট কোনো দুর্বলতা না, বরং যেকোনো ভার্চুয়ালাইজেশন-চিন্তা-ছাড়া-ডিজাইন-করা ISA-র সাধারণ ঝুঁকি।
-
RISC-V H-extension (Hypervisor Extension, ২০২১-এ ratified) — সবচেয়ে সাম্প্রতিক, সবচেয়ে সচেতন উদাহরণ: RISC-V ডিজাইনাররা Popek-Goldberg থিওরেম প্রথম থেকেই মাথায় রেখে H-extension ডিজাইন করেছেন, IBM System/370-এর মতোই — sensitive ⊆ privileged শর্ত প্রথম দিন থেকেই সত্য, x86-এর মতো ২৫ বছর পরে প্যাচ করার দরকার নেই।
যে ভুলগুলো সবাই করে
“Popek-Goldberg থিওরেম বলে x86-কে কখনো virtualize করা যাবে না।”
সম্পূর্ণ ভুল, আর এই ভুলটা থিওরেমের precise claim গুলিয়ে ফেলা থেকে আসে। থিওরেম বলে x86 classically virtualizable না — মানে বিশুদ্ধ trap-and-emulate পদ্ধতিতে, কোনো software workaround ছাড়া, সরাসরি একটা দক্ষ VMM বানানো যায় না। এটা “virtualization অসম্ভব” বলে না। বাস্তবে x86 virtualize করা হয়েছে তিনটা আলাদা পথে — binary translation (VMware), paravirtualization (Xen), আর অবশেষে hardware extension (VT-x/AMD-V) — প্রতিটাই থিওরেমের শর্তটাকে ভিন্নভাবে পাশ কাটিয়ে বা পূরণ করে দক্ষতা ফিরিয়ে এনেছে। থিওরেম শুধু বলে দেয় কোন পথে সহজ সমাধান নেই, “কোনো সমাধান নেই” বলে না।
“Virtualization আর emulation মূলত একই জিনিস।”
দুইটার performance profile আকাশ-পাতাল আলাদা, আর এই লেসনের পুরো আলোচনা এই পার্থক্যের উপরই দাঁড়িয়ে। Emulation-এ guest-এর প্রতিটা instruction software-এ interpret বা translate করা হয় (প্রায়ই সম্পূর্ণ ভিন্ন একটা target ISA-তে অনুবাদ করে — যেমন ARM binary-কে x86-এ চালানো) — নমনীয়, যেকোনো ISA-বনাম-ISA সম্ভব, কিন্তু ধীর (প্রতিটা instruction-এর দাম কয়েকগুণ বেশি)। Virtualization (এই লেসনের অর্থে) guest-এর instruction একই ISA-তে সরাসরি হার্ডওয়্যারে চালায়, শুধু sensitive মুহূর্তে VMM মধ্যস্থতা করে — Popek-Goldberg-এর Efficiency শর্তের ঠিক এই সংজ্ঞা। QEMU-র মতো টুল আসলে দুই মোডেই চলতে পারে (qemu-system pure emulation বনাম qemu-system + KVM hardware-accelerated virtualization) — একই টুল, সম্পূর্ণ ভিন্ন performance, ঠিক এই পার্থক্যের কারণে।
“Sensitive instruction মানেই বিরল, বিপজ্জনক, বা 'exotic' কোনো instruction — সাধারণ কোডে এসবের মুখোমুখি হওয়ার সম্ভাবনা কম।”
Example অংশের তালিকা আবার দেখুন — POPF, PUSHF, SGDT, SMSW — এগুলো compiler-generated কোডে, বিশেষত context-switch, exception handling, বা এমনকি সাধারণ function prologue/epilogue-এ (flags save/restore করার সময়) নিয়মিত আসে। “Sensitive” একটা formal, semantic সংজ্ঞা — instruction-টা system resource বদলায় কি না, বা তার আচরণ mode-নির্ভর কি না — এটা “বিপজ্জনক দেখতে” কি না তার সাথে সম্পর্কহীন। ঠিক এই কারণেই x86-এর sensitive-but-unprivileged সমস্যাটা এত ব্যাপক ছিল — সমস্যাটা কোনো বিরল edge-case instruction-এ ছিল না, ছিল একদম সাধারণ, প্রতিদিনের flags-handling কোডে।
“Trap মানেই কিছু একটা ভুল হয়েছে, বা একটা error/crash।”
kernel-and-user-space লেসনে দেখা syscall trap-ও, আজকের VMM trap-ও — দুইটাই সম্পূর্ণ পরিকল্পিত, নিয়ন্ত্রিত মেকানিজম, এরর না। Trap মানে “control একটা উচ্চতর privilege level-এ হস্তান্তরিত হলো, কারণ বর্তমান কোড নিজে সেই কাজটা করার অনুমতি রাখে না” — এটা program-এর স্বাভাবিক, প্রত্যাশিত জীবনচক্রের অংশ। এই লেসনের build অংশে দেখা Trap exception-টাও ঠিক এই অর্থে — এটা bug না, এটাই পুরো trap-and-emulate কৌশলের কার্যকরী অংশ। সমস্যা তখনই হয় যখন trap হওয়ার কথা ছিল অথচ হয়নি (POPF-এর ঘটনা) — trap নিজে কখনোই সমস্যা না, trap-এর অনুপস্থিতি সমস্যা।
বুঝেছেন কি না দেখুন
1Popek-Goldberg থিওরেমের তিনটা শর্তের নাম বলুন, আর প্রতিটা এক বাক্যে ব্যাখ্যা করুন।
স্মরণ
Equivalence — guest software VMM-এর নিচে ঠিক সেভাবেই আচরণ করবে যেমন সরাসরি হার্ডওয়্যারে করত (timing/resource-availability বাদে)। Resource Control — VMM-এর সম্পূর্ণ নিয়ন্ত্রণ থাকবে system resource-এর উপর, guest কখনো সরাসরি বদলাতে পারবে না। Efficiency — instruction-গুলোর একটা statistically dominant অংশ VMM-এর মধ্যস্থতা ছাড়াই সরাসরি হার্ডওয়্যারে চলবে।
2থিওরেমের শর্তটা লেখা হয় “sensitive ⊆ privileged” — উল্টোদিকে “privileged ⊆ sensitive” নয়। এই দিকটা কেন গুরুত্বপূর্ণ? একটা instruction privileged কিন্তু sensitive না হলে কী সমস্যা হয় (যদি হয়)?
যুক্তি
সমস্যা হয় না — একটা instruction privileged কিন্তু sensitive না হলে (যেমন HLT, CPU থামিয়ে দেওয়া — কোনো resource বদলায় না বা mode-নির্ভর ফলাফল দেয় না, কিন্তু ডিজাইনাররা তাও একে privileged বানিয়েছেন) সেটা trap করবে, VMM emulate করবে (সহজভাবে, যেহেতু সত্যিকারের কোনো sensitive effect নেই) — একটু বাড়তি overhead হয়তো, কিন্তু Equivalence বা Resource Control ভাঙে না। বিপদ শুধু উল্টোদিকে — sensitive instruction privileged না হলে, কারণ তখনই সেটা VMM-এর নজর এড়িয়ে সরাসরি চলে যায়, trap-and-emulate-এর পুরো ভিত্তি (trap-নির্ভরতা) ব্যর্থ হয়। তাই শর্তটা একমুখী: privileged instruction-এর “অতিরিক্ত সতর্কতা” নিরাপদ, sensitive instruction-এর “trap-এর অভাব” বিপজ্জনক।
3Build অংশের ISA-A output-এ STI আর CLI দুইটাই trapped=True এবং MATCH দেখাচ্ছে, কিন্তু POPF-এর ক্ষেত্রে trapped=False এবং MISMATCH। এই তিনটা instruction-ই sensitive (তিনটাই IF বিট বদলাতে চায়)। কোন property-র পার্থক্যে এই ভিন্ন ফলাফল?
প্রয়োগ
STI আর CLI দুইটাই trapped=True এবং MATCH দেখাচ্ছে, কিন্তু POPF-এর ক্ষেত্রে trapped=False এবং MISMATCH। এই তিনটা instruction-ই sensitive (তিনটাই IF বিট বদলাতে চায়)। কোন property-র পার্থক্যে এই ভিন্ন ফলাফল?Privileged property-র পার্থক্যে। STI আর CLI ISA-A-র privileged_set = {STI, CLI}-তে আছে — তাই user mode-এ চেষ্টা করলে trap করে, VMM emulate করার সুযোগ পায়, MATCH হয়। POPF sensitive হলেও privileged_set-এর বাইরে — তাই কখনো trap করে না, VMM কখনো জানতেই পারে না, guest-এর ধারণা আর VMM-এর tracked state আলাদা হয়ে যায় — MISMATCH। তিনটাই sensitive, কিন্তু privileged-এর দিক থেকে POPF আলাদা — ঠিক এটাই থিওরেমের কেন্দ্রীয় distinction।
4Build-এর simulator-এ guest_believes_if কখনো trapped কিনা তার উপর নির্ভর করে না — সবসময় শুধু operand। কেন এই ডিজাইন-সিদ্ধান্তটা লেসনের মূল যুক্তির জন্য জরুরি?
যুক্তি
guest_believes_if কখনো trapped কিনা তার উপর নির্ভর করে না — সবসময় শুধু operand। কেন এই ডিজাইন-সিদ্ধান্তটা লেসনের মূল যুক্তির জন্য জরুরি?কারণ এটাই ঠিক দেখায় trap-and-emulate কেন “স্বচ্ছ” (transparent) হওয়ার কথা ছিল — guest কখনো জানে না ভেতরে trap হয়েছে না silent no-op হয়েছে, কারণ দুই ক্ষেত্রেই instruction guest-এর কাছে “সফল” (fault-হীন) দেখায়। যদি guest_believes_if trap-এর status অনুযায়ী বদলাতো, সেটা ভুলভাবে ধরে নিত guest কোনোভাবে জানে ভেতরের mechanism কী ঘটেছে — বাস্তবে guest কখনো এই তথ্য পায় না, এবং ঠিক এই না-পাওয়াটাই Equivalence ভাঙার পুরো কারণ। Mismatch সনাক্ত করার জন্য আমাদের (পাঠক হিসেবে, সিমুলেশনের বাইরে থেকে) omniscient view দরকার — guest নিজে কখনো এই mismatch দেখতে পারে না, এটাই সবচেয়ে ভীতিকর অংশ।
5এই লেসনের experiment-এ ধরুন আপনার মেশিনে kern.hv_support: 0 (বা Linux-এ vmx/svm কিছুই দেখা যায় না) — অর্থাৎ হার্ডওয়্যার-ভিত্তিক ভার্চুয়ালাইজেশন সাপোর্ট নেই। থিওরেমের ভাষায়, এই মেশিনে x86 VM চালাতে চাইলে কোন দুইটা বিকল্প পথ আছে, আর প্রতিটার মূল্য কী?
প্রয়োগ
kern.hv_support: 0 (বা Linux-এ vmx/svm কিছুই দেখা যায় না) — অর্থাৎ হার্ডওয়্যার-ভিত্তিক ভার্চুয়ালাইজেশন সাপোর্ট নেই। থিওরেমের ভাষায়, এই মেশিনে x86 VM চালাতে চাইলে কোন দুইটা বিকল্প পথ আছে, আর প্রতিটার মূল্য কী?দুইটা পথ (realworld অংশ থেকে): (১) Binary translation (VMware-এর ঐতিহাসিক কৌশল) — guest code runtime-এ স্ক্যান করে sensitive-but-unprivileged instruction (POPF-সহ) খুঁজে replace করে ঠিকভাবে trap করানো — মূল্য: বাড়তি CPU overhead প্রতিটা translate-করা instruction-এ, আর translation engine-এর জটিলতা। (২) Paravirtualization (Xen-এর কৌশল) — guest OS-এর সোর্স বদলে দেওয়া যাতে সে সরাসরি hypercall করে, sensitive instruction এড়িয়েই যায় — মূল্য: guest আর “unmodified” থাকে না, একটা paravirt-aware kernel দরকার (সাধারণ Windows-এর জন্য এটা প্রায়ই সম্ভব না, শুধু open-source guest-এ বাস্তবসম্মত)। দুইটা পথই hardware VT-x/AMD-V-এর তুলনায় ধীর বা সীমাবদ্ধ — ঠিক এই কারণেই হার্ডওয়্যার সমাধান আসার পর দুইটাই বাণিজ্যিকভাবে প্রায় পরিত্যক্ত।
6ধরুন আপনি একটা নতুন, কাল্পনিক ISA ডিজাইন করছেন এবং প্রথম দিন থেকেই classically virtualizable রাখতে চান (RISC-V H-extension-এর মতো)। Robin ও Irvine-এর x86-বিশ্লেষণ পদ্ধতি অনুসরণ করে — আপনি কীভাবে নিয়মতান্ত্রিকভাবে যাচাই করবেন আপনার ISA-র প্রতিটা instruction sensitive ⊆ privileged শর্ত মানছে? কোন ধরনের instruction-এর প্রতি সবচেয়ে বেশি সন্দেহপ্রবণ হওয়া উচিত?
ডিজাইন
পদ্ধতি: প্রতিটা instruction-কে দুইটা independent প্রশ্ন দিয়ে classify করতে হবে — (ক) এটা কি কোনো system resource (memory map, interrupt state, timer, I/O, privilege-level নিজেই) বদলায় বা তার মান প্রকাশ করে (sensitive?), আর (খ) হার্ডওয়্যার ডিজাইনে এটা কি user mode-এ trap করতে বাধ্য (privileged?)। প্রতিটা instruction যেখানে উত্তর (sensitive=হ্যাঁ, privileged=না), সেটাকে privileged তালিকায় যোগ করতে হবে অথবা redesign করতে হবে। সবচেয়ে বেশি সন্দেহপ্রবণ হওয়া উচিত এমন instruction-এর প্রতি যেগুলো (১) শুধু পড়ে (read-only) — ডিজাইনাররা প্রায়ই ধরে নেন read-only মানেই নিরাপদ, ভুলে যান read করা তথ্যটাই resource-configuration হতে পারে (SGDT-এর মতো); (২) flags/status register touch করে — এই register-গুলো প্রায়ই “সাধারণ” মনে হয়, কিন্তু এদের মধ্যে system-level বিটও (interrupt-enable) মেশানো থাকে (POPF-এর মতো); (৩) legacy/backward-compatibility-এর জন্য রাখা পুরনো instruction, যেগুলো আধুনিক privilege মডেল ডিজাইন হওয়ার আগে তৈরি হয়েছিল।
এরপর কী
এরপর কী
আজ আমরা জেনেছি একটা architecture কবে, নীতিগতভাবে, trap-and-emulate দিয়ে দক্ষভাবে virtualize করা যায় — sensitive ⊆ privileged শর্ত, আর x86 কেন ঐতিহাসিকভাবে সেই শর্ত মেটায়নি। কিন্তু এখনো একটা প্রশ্ন সম্পূর্ণ এড়িয়ে গেছি: এই virtualizing-এর কাজটা আসলে কে করে? “VMM” শব্দটা বারবার ব্যবহার করেছি যেন এটা একটাই জিনিস — বাস্তবে VMM/hypervisor সফটওয়্যার হিসেবে দুইটা কাঠামোগতভাবে সম্পূর্ণ ভিন্ন ধরনের হতে পারে।
পরের লেসনে (hypervisor-types) আমরা এই পার্থক্যটা খুলে দেখব — Type 1 (bare-metal) hypervisor, যেটা সরাসরি হার্ডওয়্যারে বসে, নিজেই একটা ছোট OS-এর মতো (Xen, ESXi, KVM-অর্থে Linux kernel নিজেই), বনাম Type 2 (hosted) hypervisor, যেটা একটা সাধারণ OS-এর ভেতরে একটা প্রসেস হিসেবে চলে (VirtualBox, VMware Workstation)। আজকের trap-and-emulate মেকানিজম দুই ধরনেই প্রযোজ্য — পার্থক্যটা VMM নিজে কোথায় বসে, kernel-এর কতটা কাছে, আর সেই অবস্থানের performance ও isolation-এর দাম কী।
আরও পড়ুন
- Formal Requirements for Virtualizable Third Generation Architectures — Gerald J. Popek, Robert P. Goldberg — Communications of the ACM 17(7), 1974 · এই পুরো লেসনের ভিত্তি — VMM-এর তিনটা শর্ত আর trap-and-emulate-এর iff condition-এর মূল উৎস
- Analysis of the Intel Pentium's Ability to Support a Secure Virtual Machine Monitor — John Scott Robin, Cynthia E. Irvine — USENIX Security Symposium, 2000 · x86-এর ১৭টা sensitive-but-unprivileged instruction-এর মূল catalog — POPF ঠিক এখানেই প্রথম formal তালিকাভুক্ত হয়
- A Comparison of Software and Hardware Techniques for x86 Virtualization — Keith Adams, Ole Agesen (VMware) — ASPLOS 2006 · Binary translation বনাম VT-x hardware assist — trap-and-emulate ব্যর্থ হওয়ার পরে বাস্তব শিল্পে যে দুই পথ নেওয়া হয়েছিল তাদের সরাসরি তুলনা
- Xen and the Art of Virtualization — Paul Barham et al. — SOSP 2003 · Paravirtualization-এর জন্মপত্র — sensitive-but-unprivileged সমস্যার তৃতীয় সমাধান, পরের লেসনগুলোর ভিত্তি