বুটলোডার/কার্নেল ডেভেলপ করার সময়, অন্তর্নিহিত আর্কিটেকচার বুঝা সফটওয়্যার ও হার্ডওয়্যারের মধ্যে পারফরম্যান্স অপ্টিমাইজেশন এবং কম্প্যাটিবিলিটির জন্য অত্যন্ত গুরুত্বপূর্ণ।
সিস্টেম ইনফরমেশন কোয়েরি ও রিট্রিভ করার জন্য ইঞ্জিনিয়ারদের কাছে উপলব্ধ একটি গুরুত্বপূর্ণ কিন্তু কখনও কখনও উপেক্ষিত টুল হলো CPUID ইনস্ট্রাকশন।
CPUID ইনস্ট্রাকশন কী?
CPUID ইনস্ট্রাকশন হলো একটি লো-লেভেল ইনস্ট্রাকশন, যা প্রতিটি আধুনিক x86 এবং x86-64 প্রসেসরের হৃদয়ে রয়েছে, যা সফটওয়্যারকে প্রসেসর এবং এর সাপোর্টেড ফিচার সম্পর্কে তথ্য জানতে দেয়।
এই ইনস্ট্রাকশন আহ্বান করলে আপনি প্রসেসরের মডেল, ফ্যামিলি, ইন্টারনাল ক্যাশে সাইজ, এবং সাপোর্টেড ফিচার যেমন SIMD বা হার্ডওয়্যার ভার্চুয়ালাইজেশন সম্পর্কে তথ্য পেতে পারেন। এটি পারফরম্যান্স অপ্টিমাইজ করতে এবং ডাইনামিক্যালি সাপোর্টেড ফিচার এনেবল/ডিজেবল করতে সাহায্য করতে পারে।
বুটলোডার বা কার্নেল ডেভেলপারদের জন্য, প্রসেসর কোন ফিচার সাপোর্ট করে সেটি বুঝা—যেমন হার্ডওয়্যার ভার্চুয়ালাইজেশন, ক্যাশে সাইজ, বা SIMD ইনস্ট্রাকশন—সিস্টেম দক্ষতার সাথে রান করানো এবং আপনার লিখা কোড বিভিন্ন CPU-তে কম্প্যাটিবল থাকানো নিশ্চিত করতে পারে। CPUID ইনস্ট্রাকশন ব্যবহার করে, আপনি আপনার কার্নেলের আচরণ স্পেসিফিক প্রসেসরের উপর নির্ভর করে ডাইনামিক্যালি অ্যাডজাস্ট করতে পারেন।
এই নিবন্ধে আপনি শিখবেন কীভাবে চেক করবেন CPUID ইনস্ট্রাকশন আপনার সিস্টেমের জন্য উপলব্ধ কিনা, এটি কীভাবে কাজ করে, এবং এটি ব্যবহার করে আপনি কী কী ইনফরমেশন পেতে পারেন।
পূর্বশর্ত
- অ্যাসেম্বলি ল্যাঙ্গুয়েজের কিছু জ্ঞান (এই উদাহরণে আমি FASM ব্যবহার করলাম)
- অপারেটিং সিস্টেম/কার্নেলের কিছু জ্ঞান
- লো-লেভেল ডিবাগিং টুলস (যেমন GDB) বা হার্ডওয়্যার এমুলেটর (যেমন QEMU) অ্যাক্সেস আপনার বুটলোডার/কার্নেল বিভিন্ন প্ল্যাটফর্মে টেস্ট করার জন্য।
স্টেপ ১: CPUID উপলব্ধতা চেক করা
CPUID ইনস্ট্রাকশন এক্সিকিউট করার আগে, প্রসেসর এটি সাপোর্ট করে কিনা তা নির্ধারণ করা গুরুত্বপূর্ণ, কারণ সব CPU-তে এই ফাংশনালিটি থাকে না। নিচের কোড EFLAGS রেজিস্টারের ID বিট (বিট ২১) মডিফাই এবং টেস্ট করে CPUID ইনস্ট্রাকশনের উপলব্ধতা চেক করে।
EFLAGS রেজিস্টারের প্রতিটি বিট দেখানোর একটি চিত্র wiki.osdev.org থেকে:
যদি প্রসেসর এই বিট টগল করার অনুমতি দেয়, তবে CPUID সাপোর্টেড; অন্যথায় নয়। ডিটেকশন প্রক্রিয়া যেভাবে কাজ করে:
(অনেক লোক মনে করে Real Mode-এ ৩২-বিট রেজিস্টার অ্যাক্সেসযোগ্য নয়। এটি সত্য নয়। সব ৩২-বিট রেজিস্টার ব্যবহারযোগ্য।)
cpuid_check:
pusha ; save state
pushfd ; Save EFLAGS
pushfd ; Store EFLAGS
xor dword [esp], 0x00200000 ; Invert the ID bit in stored EFLAGS
popfd ; Load stored EFLAGS (with ID bit inverted)
pushfd ; Store EFLAGS again (ID bit may or may not be inverted)
pop eax ; eax = modified EFLAGS (ID bit may or may not be inverted)
xor eax, [esp] ; eax = whichever bits were changed
popfd ; Restore original EFLAGS
and eax, 0x00200000 ; eax = zero if ID bit can't be changed, else non-zero
cmp eax, 0x00
je .cpuid_instruction_not_is_available
.cpuid_instruction_is_available:
; handle CPUID exists
.cpuid_instruction_not_is_available:
; handle CPUID isn't supported
.cpuid_check_end:
popa ; restore state
ret
pusha: সমস্ত জেনারেল পারপাস রেজিস্টার সংরক্ষণ করে যাতে শেষে মূল অবস্থা ফেরত দেয়া যায়।
pushfd: বর্তমান EFLAGS রেজিস্টার সংরক্ষণ করে।
pushfd: EFLAGS-র একটি কপি স্টোর করে।
xor dword [esp], 0x00200000: XOR অপারেটর ব্যবহার করে EFLAGS-র ID বিট (২১) ফ্লিপ করে।
popfd: ID বিট ইনভার্টেড অবস্থায় মডিফাইড EFLAGS রিস্টোর করে।
pushfd: মডিফাইড EFLAGS আবার স্টকে পুশ করে।
pop eax: মডিফাইড EFLAGS (ID বিট ইনভার্টেড হতে পারে বা নাও হতে পারে) EAX রেজিস্টারে রাখে।
xor eax, [esp]: XOR অপারেশনের পর, EAX-এ পরিবর্তিত বিটগুলো থাকবে।
popfd: মূল EFLAGS রিস্টোর করে।
and eax, 0x00200000: AND অপারেশন ২১ নম্বর বিট (ID বিট) আইসোলেট করে অন্যান্য বিট মাস্ক করে। এই অপারেশনের পর EAX রেজিস্টারে অথবা 0x00200000 থাকবে (যদি ২১ বিট পরিবর্তিত হয়, অর্থাৎ CPUID সাপোর্টেড) অথবা 0x00 (২১ বিট পরিবর্তিত হয়নি, CPUID সাপোর্টেড নয়)।
cmp eax, 0x00: CMP ইনস্ট্রাকশন পূর্বের অপারেশনের ফলাফল চেক করে। যদি EAX 0x00 সমান হয়, তবে ID বিট মডিফাই করা যাবে না এবং প্রসেসর CPUID ইনস্ট্রাকশন সাপোর্ট করে না। যদি শূন্য নয়, তবে ID বিট ফ্লিপ হয়েছে এবং আপনার প্রসেসর CPUID ইনস্ট্রাকশন সাপোর্ট করে।
CPU ফিচার বের করা
CPUID ইনস্ট্রাকশন EAX রেজিস্টারে ভিন্ন ভ্যালু দিয়ে ভিন্ন ইনফরমেশন রিটার্ন করে।
mov eax, 0x1
cpuid
EAX ১ সেট করে CPUID কল করলে, EDX-এ একটি বিটফিল্ড রিটার্ন হবে যেটিতে নিচের ভ্যালুগুলো থাকবে। বিভিন্ন ব্র্যান্ড এদের ভিন্ন অর্থ দিতে পারে (সোর্স: https://wiki.osdev.org/CPUID)
enum {
CPUID_FEAT_ECX_SSE3 = 1 << 0,
CPUID_FEAT_ECX_PCLMUL = 1 << 1,
CPUID_FEAT_ECX_DTES64 = 1 << 2,
CPUID_FEAT_ECX_MONITOR = 1 << 3,
CPUID_FEAT_ECX_DS_CPL = 1 << 4,
CPUID_FEAT_ECX_VMX = 1 << 5,
CPUID_FEAT_ECX_SMX = 1 << 6,
CPUID_FEAT_ECX_EST = 1 << 7,
CPUID_FEAT_ECX_TM2 = 1 << 8,
CPUID_FEAT_ECX_SSSE3 = 1 << 9,
CPUID_FEAT_ECX_CID = 1 << 10,
CPUID_FEAT_ECX_SDBG = 1 << 11,
CPUID_FEAT_ECX_FMA = 1 << 12,
CPUID_FEAT_ECX_CX16 = 1 << 13,
CPUID_FEAT_ECX_XTPR = 1 << 14,
CPUID_FEAT_ECX_PDCM = 1 << 15,
CPUID_FEAT_ECX_PCID = 1 << 17,
CPUID_FEAT_ECX_DCA = 1 << 18,
CPUID_FEAT_ECX_SSE4_1 = 1 << 19,
CPUID_FEAT_ECX_SSE4_2 = 1 << 20,
CPUID_FEAT_ECX_X2APIC = 1 << 21,
CPUID_FEAT_ECX_MOVBE = 1 << 22,
CPUID_FEAT_ECX_POPCNT = 1 << 23,
CPUID_FEAT_ECX_TSC = 1 << 24,
CPUID_FEAT_ECX_AES = 1 << 25,
CPUID_FEAT_ECX_XSAVE = 1 << 26,
CPUID_FEAT_ECX_OSXSAVE = 1 << 27,
CPUID_FEAT_ECX_AVX = 1 << 28,
CPUID_FEAT_ECX_F16C = 1 << 29,
CPUID_FEAT_ECX_RDRAND = 1 << 30,
CPUID_FEAT_ECX_HYPERVISOR = 1 << 31,
CPUID_FEAT_EDX_FPU = 1 << 0,
CPUID_FEAT_EDX_VME = 1 << 1,
CPUID_FEAT_EDX_DE = 1 << 2,
CPUID_FEAT_EDX_PSE = 1 << 3,
CPUID_FEAT_EDX_TSC = 1 << 4,
CPUID_FEAT_EDX_MSR = 1 << 5,
CPUID_FEAT_EDX_PAE = 1 << 6,
CPUID_FEAT_EDX_MCE = 1 << 7,
CPUID_FEAT_EDX_CX8 = 1 << 8,
CPUID_FEAT_EDX_APIC = 1 << 9,
CPUID_FEAT_EDX_SEP = 1 << 11,
CPUID_FEAT_EDX_MTRR = 1 << 12,
CPUID_FEAT_EDX_PGE = 1 << 13,
CPUID_FEAT_EDX_MCA = 1 << 14,
CPUID_FEAT_EDX_CMOV = 1 << 15,
CPUID_FEAT_EDX_PAT = 1 << 16,
CPUID_FEAT_EDX_PSE36 = 1 << 17,
CPUID_FEAT_EDX_PSN = 1 << 18,
CPUID_FEAT_EDX_CLFLUSH = 1 << 19,
CPUID_FEAT_EDX_DS = 1 << 21,
CPUID_FEAT_EDX_ACPI = 1 << 22,
CPUID_FEAT_EDX_MMX = 1 << 23,
CPUID_FEAT_EDX_FXSR = 1 << 24,
CPUID_FEAT_EDX_SSE = 1 << 25,
CPUID_FEAT_EDX_SSE2 = 1 << 26,
CPUID_FEAT_EDX_SS = 1 << 27,
CPUID_FEAT_EDX_HTT = 1 << 28,
CPUID_FEAT_EDX_TM = 1 << 29,
CPUID_FEAT_EDX_IA64 = 1 << 30,
CPUID_FEAT_EDX_PBE = 1 << 31
};
উপরোক্ত CPU ফিচারগুলোর সংক্ষিপ্ত ব্যাখ্যা:
PCLMUL, AES: দ্রুত এনক্রিপশন ও ডিক্রিপশনের জন্য ক্রিপ্টোগ্রাফিক ইনস্ট্রাকশন সেট।VMX, SMX: ভার্চুয়াল মেশিন চালানোর জন্য ভার্চুয়ালাইজেশন সাপোর্ট।SSE3, SSSE3, SSE4.1, SSE4.2, AVX: মাল্টিমিডিয়া, ম্যাথ, এবং ভেক্টর প্রসেসিংর জন্য দ্রুত SIMD ইনস্ট্রাকশন সেট।FMA: ফিউজড মাল্টিপ্লাই-অ্যাড, ফ্লোটিং-পয়েন্ট ক্যালকুলেশনে পারফরম্যান্স বাড়ায়।RDRAND: র্যান্ডম নাম্বার জেনারেটর।X2APIC: মাল্টিপ্রসেসর সিস্টেমে অ্যাডভান্সড ইন্টারাপ্ট হ্যান্ডলিং।PCID: কন্টেক্সট সুইচের সময় মেমরি ম্যানেজমেন্ট অপ্টিমাইজ করে।FPU: দ্রুত ম্যাথ অপারেশনের জন্য হার্ডওয়্যার ফ্লোটিং-পয়েন্ট ইউনিট।PAE: ফিজিক্যাল অ্যাড্রেস এক্সটেনশন, ৪ GB-ও বেশি মেমরি অ্যাড্রেস করার অনুমতি দেয়।HTT: একটি সিঙ্গেল CPU কোর একাধিক থ্রেড হ্যান্ডেল করতে দেয়।PAT, PGE: ক্যাশিং ও পেজ ম্যাপিং কন্ট্রোল করার মেমরি ম্যানেজমেন্ট ফিচার।MMX, SSE, SSE2: মাল্টিমিডিয়া প্রসেসিংর জন্য পুরনো SIMD ইনস্ট্রাকশন সেট।
CPU ভেন্ডর স্ট্রিং বের করা
CPU ভেন্ডর স্ট্রিং পেতে হলে CPUID ইনস্ট্রাকশন আহ্বান করার আগে EAX-এ 0x0 সেট করতে হবে।
mov eax, 0x0
cpuid
ভেন্ডর স্ট্রিং হলো একটি ইউনিক আইডেন্টিফায়ার যা AMD এবং Intel-এর মতো CPU ভেন্ডাররা ব্যবহার করে। উদাহরণ: GenuineIntel (Intel প্রসেসরের জন্য) বা AuthenticAMD (AMD প্রসেসরের জন্য)। এটি মূলত CPU-এর নির্মাতা নির্দেশ করে।
ভেন্ডর স্ট্রিং কার্নেলকে CPU নির্মাতা শনাক্ত করতে দেয় যা খুব উপকারী কারণ বিভিন্ন নির্মাতা কিছু ফিচার ভিন্নভাবে ইমপ্লিমেন্ট করে। সফটওয়্যার বা ড্রাইভারও CPU নির্মাতার উপর ভিত্তি করে ভিন্নভাবে ইন্টারঅ্যাক্ট করতে পারে কম্প্যাটিবিলিটি নিশ্চিত করতে।
এভাবে ব্যবহার করলে, ভেন্ডর আইডি স্ট্রিং EBX, EDX, ECX রেজিস্টারে রিটার্ন হবে। আপনি সেগুলো একটি বাফারে লিখে পূর্ণ ১২ ক্যারেক্টার স্ট্রিং পেতে পারেন।
উদাহরণ কোড:
স্টেপ ১: বাফার
১২ বাইট ধারণ করতে পারে এমন একটি বাফার তৈরি করুন:
buffer: db 12 dup(0), 0xA, 0xD, 0
স্টেপ ২: বাফার প্রিন্ট করা
একটি স্ট্রিং প্রিন্টিং ফাংশন তৈরি করি।
এই অ্যাসেম্বলি কোড স্ট্রিংটি ক্যারেক্টার বাই ক্যারেক্টার পড়ে এবং BIOS ইন্টারাপ্ট 0x10 ব্যবহার করে স্ক্রিনে প্রিন্ট করে। print ফাংশন লুপের মাধ্যমে স্ট্রিং পার করে এবং lodsb ইনস্ট্রাকশন ব্যবহার করে প্রতিটি ক্যারেক্টার al রেজিস্টারে লোড করে।
তারপর print_char ফাংশন ইন্টারাপ্ট 0x10 ব্যবহার করে সেটি স্ক্রিনে প্রিন্ট করে। কোড যখন স্ট্রিংয়ের শেষে (নাল টার্মিনেটর) পৌঁছায়, লুপ শেষ হয়।
print_string:
call print
ret
print:
.loop:
lodsb ; read character to al and then increment
cmp al, 0 ; check if we reached the end
je .done ; we reached null terminator, finish
call print_char ; print character
jmp .loop ; jump back into the loop
.done:
ret
print_char:
mov ah, 0eh
int 0x10
ret
স্টেপ ৩: বাফার ফিল ও প্রিন্ট করা
এখানে, pusha ইনস্ট্রাকশন দিয়ে বর্তমান স্টেট সংরক্ষণ করলে এবং EAX রেজিস্টারে 0x0 পাস করে cpuid কল করলে, আমরা ebx, edx, ecx-র কন্টেন্ট বাফারে স্টোর করতে পারি। তারপর print_string কল করে প্রিন্ট করি।
get_cpu_vendor:
pusha
mov eax, 0x0
cpuid
mov [buffer], ebx
mov [buffer + 4], edx
mov [buffer + 8], ecx
mov si, buffer
call print_string
popa
ret
আমার ইউটিউব চ্যানেল থেকে একটি ভিডিও যেখানে উপরের কোড ইমপ্লিমেন্ট ও ব্যাখ্যা করা হয়েছে বিস্তারিত।
EAX রেজিস্টারে পাস করা ভ্যালুর উপর ভিত্তি করে CPUID ইনস্ট্রাকশন কী কী ইনফরমেশন দিতে পারে, সে সম্পর্কে আরও তথ্য পাওয়া যাবে: https://gitlab.com/x86-cpuid.org/x86-cpuid-db
উপসংহার
CPUID ইনস্ট্রাকশন বুঝতে এবং ব্যবহার করতে পারলে, আপনি আপনার বুটলোডার/কার্নেলকে ব্যাপক রেঞ্জের প্রসেসরের সাথে আরও অ্যাডাপ্টেবল করতে পারেন। ইনস্ট্রাকশনের উপলব্ধতা ডিটেক্ট করা এবং গুরুত্বপূর্ণ সিস্টেম ইনফরমেশন রিট্রিভ করা—যেমন CPU ফিচার, ক্যাশে সাইজ, এবং সাপোর্টেড টেকনোলজি—পারফরম্যান্স এবং কম্প্যাটিবিলিটি উল্লেখযোগ্যভাবে বাড়াতে পারে।
এই নিবন্ধ পড়ার পর, আপনার কাছে CPUID ইনস্ট্রাকশন এক্সপ্লোর করতে এবং আপনার প্রজেক্টে ব্যবহার করতে টুলস ও জ্ঞান থাকা উচিত!
হ্যাপি কোডিং!
ফ্রি কোড ক্যাম্পের ওপেন সোর্স কারিকুলাম ৪০,০০০+ মানুষকে ডেভেলপার হিসেবে চাকরি পেতে সাহায্য করেছে। শুরু করুন।