Intel x86 Assembly Language & Microarchitecture
वास्तविक बनाम संरक्षित मोड
खोज…
वास्तविक मोड
जब इंटेल ने मूल x86, 8086 (और 8088 व्युत्पन्न) को डिजाइन किया, तो उन्होंने 16-बिट प्रोसेसर को 16 बिट से अधिक मूल्य के पते तक पहुंचने की अनुमति देने के लिए सेगमेंटेशन को शामिल किया। उन्होंने 16-बिट पते को दिए गए 16-बिट सेगमेंट रजिस्टर के सापेक्ष किया, जिसमें से उन्होंने चार को परिभाषित किया: कोड सेगमेंट ( CS ), डेटा सेगमेंट ( DS ), अतिरिक्त सेगमेंट ( ES ) और स्टैक सेगमेंट ( SS )। ।
अधिकांश निर्देशों में निहित है कि किस सेगमेंट रजिस्टर का उपयोग करना है: निर्देशों को कोड सेगमेंट, PUSH और POP से स्टेक सेगमेंट से हटा दिया गया, और सरल डेटा संदर्भों ने डेटा सेगमेंट को निहित कर दिया - हालांकि यह किसी भी अन्य सेगमेंट में मेमोरी तक पहुंचने के लिए ओवरराइड किया जा सकता है।
कार्यान्वयन सरल था: प्रत्येक मेमोरी एक्सेस के लिए, सीपीयू निहित (या स्पष्ट) सेगमेंट रजिस्टर को ले जाएगा, इसे बाईं ओर चार स्थानों पर शिफ्ट करें, फिर संकेतित पते में जोड़ें:
+-------------------+---------+
Segment | 16-bit value | 0 0 0 0 |
+-------------------+---------+
PLUS
+---------+-------------------+
Address | 0 0 0 0 | 16-bit value |
+---------+-------------------+
EQUALS
+-----------------------------+
Result | 20-bit memory address |
+-----------------------------+
यह विभिन्न तकनीकों के लिए अनुमत है:
- सभी के लिए कोड, डेटा और स्टैक का परस्पर उपयोग योग्य होना चाहिए (
CS,DSऔरSSसभी का समान मूल्य था); - कोड, डेटा और स्टैक को एक दूसरे से अलग रखना (
CS,DSऔरSSसभी 4K (या अधिक) एक दूसरे से अलग - याद रखें कि यह 16 से गुणा हो जाता है, इसलिए यह 64K है)।
यह विचित्र ओवरलैप और सभी प्रकार की अजीब चीजों को भी अनुमति देता है!
जब 80286 का आविष्कार किया गया था, तो उसने इस विरासत मोड (जिसे अब "रियल मोड" कहा जाता है) का समर्थन किया, लेकिन "संरक्षित मोड" (qv) नामक एक नया मोड जोड़ा।
गौर करने वाली महत्वपूर्ण बात यह है कि रियल मोड में:
- किसी भी मेमोरी पते तक पहुँच थी, बस एक सेगमेंट रजिस्टर के अंदर सही मूल्य डालकर और 16-बिट पते को एक्सेस करके;
- "संरक्षण" की सीमा प्रोग्रामर को अलग-अलग उद्देश्यों के लिए स्मृति के विभिन्न क्षेत्रों को अलग करने की अनुमति देने के लिए थी, और गलती से गलत डेटा को लिखना मुश्किल बना दिया - जबकि अभी भी ऐसा करना संभव है।
दूसरे शब्दों में ... बिल्कुल भी संरक्षित नहीं!
सुरक्षित प्रकार
परिचय
जब 80286 का आविष्कार किया गया था, तो उसने विरासत 8086 सेगमेंटेशन (जिसे अब "रियल मोड" कहा जाता है) का समर्थन किया, और "संरक्षित मोड" नामक एक नया मोड जोड़ा। यह मोड हर x86 प्रोसेसर में रहा है, यद्यपि 32- और 64-बिट एड्रेसिंग जैसे विभिन्न सुधारों के साथ बढ़ाया गया।
डिज़ाइन
संरक्षित मोड में, सरल "एडेड एड्रेस टू शिफ्ट्ड सेगमेंट रजिस्टर वैल्यू" को पूरी तरह से हटा दिया गया था। उन्होंने सेगमेंट रजिस्टरों को रखा, लेकिन एक पते की गणना करने के लिए उनका उपयोग करने के बजाय, उन्होंने उन्हें तालिका में अनुक्रमित करने के लिए उपयोग किया (वास्तव में, दो में से एक ...) जिसने सेगमेंट को एक्सेस करने के लिए परिभाषित किया। यह परिभाषा न केवल यह बताती है कि सेगमेंट मेमोरी में कहां है (बेस और लिमिट का उपयोग करके), लेकिन यह भी कि यह किस प्रकार का सेगमेंट था (कोड, डेटा, स्टैक या सिस्टम) और किस प्रकार के प्रोग्राम इसे एक्सेस कर सकते हैं (ओएस कर्नेल, सामान्य प्रोग्राम , डिवाइस ड्राइवर, आदि)।
सेगमेंट रजिस्टर
प्रत्येक 16-बिट सेगमेंट रजिस्टर निम्नलिखित फॉर्म पर लिया गया है:
+------------+-----+------+
| Desc Index | G/L | Priv |
+------------+-----+------+
Desc Index = 13-bit index into a Descriptor Table (described below)
G/L = 1-bit flag for which Descriptor Table to Index: Global or Local
Priv = 2-bit field defining the Privilege level for access
वैश्विक / स्थानीय
ग्लोबल / स्थानीय बिट यह परिभाषित करता है कि एक्सेस डिस्क्रिप्टरर्स की ग्लोबल टेबल में है (जिसे ग्लोबल डिस्क्रिप्टर टेबल या जीडीटी कहा जाता है), या एक स्थानीय डिस्क्रिप्टर टेबल (एलडीटी)। LDT के लिए विचार यह था कि प्रत्येक प्रोग्राम की अपनी डिस्क्रिप्टर टेबल हो सकती है - OS woud एक ग्लोबल सेट ऑफ़ सेगमेंट को परिभाषित करता है, और प्रत्येक प्रोग्राम का अपना स्थानीय कोड, डेटा और स्टैक सेगमेंट का सेट होगा। OS विभिन्न डिस्क्रिप्टर टेबल्स के बीच मेमोरी का प्रबंधन करेगा।
डिस्क्रिप्टर टेबल
प्रत्येक डिस्क्रिप्टिव टेबल (ग्लोबल या लोकल) 8,192 डिस्क्रिप्टर्स का 64K सरणी था: प्रत्येक 8-बाइट रिकॉर्ड जिसमें सेगमेंट के कई पहलुओं को परिभाषित किया गया था जिसका वह वर्णन कर रहा था। सेगमेंट रजिस्टरों के डिस्क्रिप्टिव इंडेक्स फ़ील्ड्स ने 8,192 डिस्क्रिप्टर के लिए अनुमति दी: कोई संयोग नहीं!
डिस्क्रिप्टर
एक डिस्क्रिप्टर ने निम्नलिखित जानकारी रखी - ध्यान दें कि डिस्क्रिप्टर का प्रारूप नए प्रोसेसर के रूप में बदल गया है, लेकिन उसी तरह की जानकारी प्रत्येक में रखी गई थी:
- आधार
यह स्मृति खंड के प्रारंभ पते को परिभाषित करता है। - सीमा
यह मेमोरी सेगमेंट के आकार को परिभाषित करता है - जैसे। उन्हें एक निर्णय लेना था: क्या0x0000आकार0आकार का होगा, इसलिए पहुंच योग्य नहीं? या अधिकतम आकार?
इसके बजाय उन्होंने एक तीसरा विकल्प चुना: सीमा क्षेत्र सेगमेंट के भीतर अंतिम पता योग्य स्थान था। इसका मतलब है कि एक-बाय सेगमेंट को परिभाषित किया जा सकता है; या पता आकार के लिए अधिकतम आकार का। - प्रकार
कई प्रकार के सेगमेंट थे: पारंपरिक कोड, डेटा और स्टैक (नीचे देखें), लेकिन अन्य सिस्टम सेगमेंट को भी परिभाषित किया गया था:- स्थानीय डिस्क्रिप्टर तालिका सेगमेंट ने परिभाषित किया कि कितने स्थानीय डिस्क्रिप्टर्स तक पहुँचा जा सकता है;
- टास्क स्टेट सेगमेंट का उपयोग हार्डवेयर-प्रबंधित संदर्भ स्विचिंग के लिए किया जा सकता है;
- नियंत्रित "कॉल गेट्स" जो कार्यक्रमों को ऑपरेटिंग सिस्टम में कॉल करने की अनुमति दे सकता है - लेकिन केवल सावधानीपूर्वक प्रबंधित प्रवेश बिंदुओं के माध्यम से।
- गुण
सेगमेंट की कुछ विशेषताओं को भी बनाए रखा गया था, जहां प्रासंगिक:- पढ़ें-केवल बनाम पढ़ें-लिखें;
- खंड वर्तमान में मौजूद था या नहीं - ऑन-डिमांड मेमोरी प्रबंधन के लिए अनुमति;
- किस स्तर का कोड (OS बनाम ड्राइवर बनाम प्रोग्राम) इस सेगमेंट तक पहुंच सकता है।
अंत में सच्ची सुरक्षा!
यदि OS ने डेस्क्रिप्टर टेबल्स को सेगमेंट्स में रखा है जिसे केवल कार्यक्रमों द्वारा एक्सेस नहीं किया जा सकता है, तो यह कसकर प्रबंधित कर सकता है कि कौन से सेगमेंट परिभाषित किए गए थे, और प्रत्येक को कौन सी मेमोरी असाइन और एक्सेस की गई थी। एक प्रोग्राम जो कुछ भी सेगमेंट रजिस्टर मूल्य को पसंद करता है, उसका निर्माण कर सकता है - लेकिन अगर उसमें वास्तव में एक सेगमेंट रजिस्टर में लोड करने की दुस्साहस था! ... सीपीयू हार्डवेयर यह पहचान लेगा कि प्रस्तावित डिस्क्रिप्टर मूल्य ने नियमों की एक बड़ी संख्या को तोड़ दिया है, और अनुरोध को पूरा करने के बजाय, यह ऑपरेटिंग सिस्टम के लिए एक अपवाद को बढ़ाएगा ताकि इसे गलत प्रोग्राम को संभालने की अनुमति मिल सके।
यह अपवाद आमतौर पर # 13 था, सामान्य सुरक्षा अपवाद - Microsoft Windows द्वारा विश्व प्रसिद्ध किया गया ... (किसी को लगता है कि एक इंटेल इंजीनियर अस्थिर था?)
त्रुटियाँ
त्रुटियों के प्रकार जो शामिल हो सकते हैं:
यदि प्रस्तावित डिस्क्रिप्टर इंडेक्स तालिका के आकार से बड़ा था;
यदि प्रस्तावित डिस्क्रिप्टर कोड, डेटा या स्टैक के बजाय सिस्टम डिस्क्रिप्टर था;
यदि प्रस्तावित डिस्क्रिप्टर अनुरोध कार्यक्रम से अधिक विशेषाधिकार प्राप्त था;
यदि प्रस्तावित डिस्क्रिप्टर को पठनीय नहीं (जैसे कि एक कोड सेगमेंट) के रूप में चिह्नित किया गया था, लेकिन इसे निष्पादित के बजाय रीड होने का प्रयास किया गया था;
यदि प्रस्तावित डिस्क्रिप्टर को उपस्थित नहीं किया गया था।
ध्यान दें कि अंतिम कार्यक्रम के लिए एक घातक समस्या नहीं हो सकती है: ओएस ध्वज को नोट कर सकता है, सेगमेंट को फिर से स्थापित कर सकता है, इसे अब चिह्नित कर सकता है और फिर वर्तमान में फॉल्टिंग निर्देश को सफलतापूर्वक आगे बढ़ने की अनुमति देता है।
या, शायद डिस्क्रिप्टर को एक सेगमेंट रजिस्टर में सफलतापूर्वक लोड किया गया था, लेकिन फिर इसके साथ एक भविष्य की पहुंच ने कई नियमों में से एक को तोड़ दिया:
- GDT के लिए सेगमेंट रजिस्टर को
0x0000Descriptor Index के साथ लोड किया गया था। यह हार्डवेयर द्वाराNULLरूप में आरक्षित किया गया था; - यदि लोड किए गए डिस्क्रिप्टर को केवल-पढ़ने के लिए चिह्नित किया गया था, लेकिन इसे लिखने का प्रयास किया गया था।
- यदि एक्सेस का कोई भी हिस्सा (1, 2, 4 या अधिक बाइट्स) सेगमेंट की सीमा के बाहर था।
संरक्षित मोड में स्विच करना
संरक्षित मोड में स्विच करना आसान है: आपको बस एक बिट को कंट्रोल रजिस्टर में सेट करना होगा। लेकिन संरक्षित मोड में रहना , सीपीयू के बिना अपने हाथों को फेंकना और खुद को रीसेट करने की वजह से नहीं पता कि आगे क्या करना है, बहुत तैयारी करता है।
संक्षेप में, आवश्यक कदम इस प्रकार हैं:
वैश्विक डिस्क्रिप्टर तालिका के लिए स्मृति का एक क्षेत्र न्यूनतम तीन डिस्क्रिप्टर्स को परिभाषित करने के लिए स्थापित करने की आवश्यकता है:
- द जीरेथ,
NULLडिस्क्रिप्टर; - एक कोड सेगमेंट के लिए एक और डिस्क्रिप्टर;
- एक डेटा सेगमेंट के लिए एक और विवरण।
इसका उपयोग Data और Stack दोनों के लिए किया जा सकता है।
- द जीरेथ,
ग्लोबल डिस्क्रिप्टर टेबल रजिस्टर (
GDTR) को स्मृति के इस परिभाषित क्षेत्र को इंगित करने के लिए प्रारंभिक करने की आवश्यकता है;GDT_Ptr dw SIZE GDT dd OFFSET GDT ... lgdt [GDT_Ptr]CR0मेंPMबिट सेट करने की आवश्यकता है:mov eax, cr0 ; Get CR0 into register or eax, 0x01 ; Set the Protected Mode bit mov cr0, eax ; We're now in Protected Mode!वर्तमान रियल मोड मानों को निकालने के लिए GDT से सेगमेंट रजिस्टरों को लोड करने की आवश्यकता है:
jmp 0x0008:NowInPM ; This is a FAR Jump. 0x0008 is the Code Descriptor NowInPM: mov ax, 0x0010 ; This is the Data Descriptor mov ds, ax mov es, ax mov ss, ax mov sp, 0x0000 ; Top of stack!
ध्यान दें कि यह नंगे न्यूनतम है, बस सीपीयू को संरक्षित मोड में लाने के लिए। वास्तव में पूरी प्रणाली को तैयार करने के लिए कई और चरणों की आवश्यकता हो सकती है। उदाहरण के लिए:
- ऊपरी मेमोरी क्षेत्रों को सक्षम करना पड़ सकता है -
A20गेट को बंद करना; - इंटरप्ट को निश्चित रूप से अक्षम किया जाना चाहिए - लेकिन शायद प्रसंस्करण में त्रुटियों को जल्दी अनुमति देने के लिए, संरक्षित मोड में प्रवेश करने से पहले विभिन्न फॉल्ट हैंडलर स्थापित किए जा सकते हैं।
इस खंड के मूल लेखक ने संरक्षित मोड में प्रवेश करने और इसके साथ काम करने पर एक संपूर्ण ट्यूटोरियल लिखा।
असत्य विधा
असत्य मोड इंटेल और एएमडी प्रोसेसर दोनों को लोड करता है और एक सेगमेंट का वर्णन करने के लिए जानकारी को बचाने के बारे में दो तथ्यों का शोषण करता है।
प्रोसेसर सुरक्षित मोड में चयनकर्ता रजिस्टर में एक चाल के दौरान प्राप्त विवरणकर्ता की जानकारी को कैश करता है।
ये जानकारी चयनकर्ता रजिस्टर के एक वास्तुशिल्प अदृश्य भाग में संग्रहीत हैं।वास्तविक मोड में चयनकर्ता रजिस्टरों को सेगमेंट रजिस्टर कहा जाता है लेकिन, इसके अलावा, वे रजिस्टरों के एक ही सेट को नामित करते हैं और जैसे कि उनका एक अदृश्य भाग भी होता है। ये भाग निश्चित मानों से भरे होते हैं, लेकिन आधार के लिए जो केवल लोड किए गए मूल्य से प्राप्त होता है।
इस तरह के दृश्य में, वास्तविक मोड केवल संरक्षित मोड का एक विशेष मामला है: जहां एक खंड की जानकारी, जैसे आधार और सीमा, बिना जीडीटी / एलडीटी के बिना प्राप्त की जाती है, लेकिन फिर भी खंड से छिपे हुए भाग से पढ़ी जाती है।
संरक्षित मोड में स्विच करने और जीडीटी को क्राफ्ट करने से वांछित विशेषताओं के साथ एक सेगमेंट बनाना संभव है, उदाहरण के लिए 0 का आधार और 4 जीआईबी की सीमा।
चयनकर्ता रजिस्टर के एक क्रमिक लोडिंग के माध्यम से इस तरह की विशेषताओं को कैश किया जाता है, फिर वास्तविक मोड में वापस स्विच करना संभव है और एक सेगमेंट रजिस्टर होता है जिसके माध्यम से पूरे 32 बिट एड्रेस स्पेस का उपयोग होता है।
BITS 16
jmp 7c0h:__START__
__START__:
push cs
pop ds
push ds
pop ss
xor sp, sp
lgdt [GDT] ;Set the GDTR register
cli ;We don't have an IDT set, we can't handle interrupts
;Entering protected mode
mov eax, cr0
or ax, 01h ;Set bit PE (bit 0) of CR0
mov cr0, eax ;Apply
;We are now in Protected mode
mov bx, 08h ;Selector to use, RPL = 0, Table = 0 (GDT), Index = 1
mov fs, bx ;Load FS with descriptor 1 info
mov gs, bx ;Load GS with descriptor 1 info
;Exit protected mode
and ax, 0fffeh ;Clear bit PE (bit0) of CR0
mov cr0, eax ;Apply
sti
;Back to real mode
;Do nothing
cli
hlt
GDT:
;First entry, number 0
;Null descriptor
;Used to store a m16&32 object that tells the GDT start and size
dw 0fh ;Size in byte -1 of the GDT (2 descriptors = 16 bytes)
dd GDT + 7c00h ;Linear address of GDT start (24 bits)
dw 00h ;Pad
dd 0000ffffh ;Base[15:00] = 0, Limit[15:00] = 0ffffh
dd 00cf9200h ;Base[31:24] = 0, G = 1, B = 1, Limit[19:16] = 0fh,
;P = 1, DPL = 0, E = 0, W = 1, A = 0, Base[23:16] = 00h
TIMES 510-($-$$) db 00h
dw 0aa55h
विचार
- जैसे ही एक सेगमेंट रजिस्टर को पुनः लोड किया जाता है, यहां तक कि समान मूल्य के साथ, प्रोसेसर वर्तमान मोड के अनुसार छिपे हुए विशेषताओं को फिर से लोड करता है। यही कारण है कि "विस्तारित" खंडों को रखने के लिए उपर्युक्त कोड
fsऔरgsका उपयोग किया जाता है: इस तरह के रजिस्टरों को विभिन्न 16 बिट सेवाओं द्वारा उपयोग / सहेजे / बहाल किए जाने की संभावना कम है। -
lgdtनिर्देश GDT के लिए एक पॉइंटर को लोड नहीं करता है, इसके बजाय यह एक 24 बिट (32 बिट तक ओवरराइड किया जा सकता है) रैखिक पते को लोड करता है। यह एक निकट पता नहीं है, यह भौतिक पता है (क्योंकि पेजिंग अक्षम होना चाहिए)। यहीGDT+7c00hका कारण है। - उपरोक्त कार्यक्रम एक बूटलोडर है (एमबीआर के लिए, इसमें कोई बीपीबी नहीं है) जो
cs/ds/ssटीपी 7 सी 00 एच सेट करता है और स्थान काउंटर को 0. से शुरू करता है। इसलिए फ़ाइल में ऑफसेट एक्स पर एक बाइट खंड 7c00h में ऑफसेट एक्स पर है और रैखिक पते पर 7c00h + X। - इंटरप्ट को अक्षम किया जाना चाहिए क्योंकि संरक्षित मोड में लघु दौर की यात्रा के लिए आईडीटी निर्धारित नहीं है।
- कोड के 6 बाइट को बचाने के लिए कोड एक हैक का उपयोग करता है।
lgdtद्वारा भरी गई संरचना को ... GDT में, नल डिस्क्रिप्टर (पहला डिस्क्रिप्टर) में सहेजा गया है।
जीडीटी विवरणों के विवरण के लिए इंटेल मैनुअल वॉल्यूम 3 ए के अध्याय 3.4.3 को देखें।