طراحی سیستم عامل – قدم به قدم ( محـمد علی ژیـانی )
نوشتن یک سیستم عامل – برای نوشتن یک سیستم عامل به چه چیزی نیاز است ؟
خیلی وقت بود تصمیم گرفته بودم که شروع کنم به یاد گیری ساخت یک سیستم عامل شاید به قدمت 15الی 20 سال ، همیشه فکر میکردم کار من نیست ، نمیشه ، گاو نر میخواد و الی آخر….
همیشه هم همین رابه خودم گفتم که اینقدر طول کشید
این چند وقت کتابهای زیادی در مورد سخت افزار و درک مطلب کارکرد سخت افزاری کامپیوتر خوندم
که سه تا از اصلیها که یه کم فکرم را اصلاح کرد
کتاب کرنل بی درنگ نوشته جین لبروس ( ترجمه فرزاد شکاری زاده ، انتشارات نص بود که مبحث راه اندازی یک سیستم عامل بر روی میکروکنترلرها بود
و دومی کتاب اصول میکروکامپیوترها نوشته علیرضا رضائی انتشارات آیلار)
سومی هم مرجع کامل الکترونیک بود که هی فکر نکنم چرا حالا این کلیده روشن شد (زیادی رفتم از پایه )
الانم دارم اسمبلی برادر جعفر نژاد قمی را میخونم ( اگه وقت کنم )
بعد از نوشتن اولین بوت لودر ، دیدم نه ، انگار میشه ، منتها باید قدم اول را برداشت…
از امروز شروع کردم به خوندن مطالب مربوطه روی اینترنت ، و همزمان( به فاصله زمانی کم) هم شروع میکنم به آموزش در اینجا ، میدونم به من مجال نمیده که یه غلط درست حسابی بکنم ولی میدونم جونترهایی هستند که یه روزی این تاپیک براشون بشه سکوئی برای ساخت اون چیزی که من سالهاست به فکرشم و میکروسافت سالهاست ساخته …
خب این حرفها را که نوشتم مقدمه ای بود بر مقدمه ( این جمله یعنی شروع از قبل از شروع )
اول
زبانهایی که برای نوشتن یک سیستم عامل نیاز هست اسمبلی هست و بعد سی
اسمبلی که پایه ترین زبان هست و برای استارت کار حتما نیاز بهش داریم ، چرا ؟
چون سیستم عامل برای لود شدن نیاز به یک بوت لودر داره ، بوت لودر چیه ؟
بوت لود کدیست که در محلی از هارد دیسک(یا هرچی تو این مایه Cd-rom-Flopy-FlashMemory ) هست که در اولین سکتور این دیوایس نوشته میشه و چه بخواهین چه نخواهین حداکثر حجمی که قابل برنامه نویسی هست فقط 512 بایت هست
در پرانتز : بوت لودر یک امضاء دارد که در فرمت هگز به شکل 55AA نوشته میشه ، یعنی عملا شما بوت لودرتون باید درکمتر از 512 بایت نوشته بشه ( خیلی کم هست ولی خب علت داره ، علتش چیه ؟
وقتی کامپیوتر بوت میشه ، ROM Bios که در واقع اولین کدی هست که اجرا میشه میاد روی دیوایس ها میگرده ببینه که آیا بوت ایبل هست یا نه( همان تنظیمی که توی ستاپ میکنید برای معرفی First Boot) ، و این اتفاق فقط روی 512 بایت اول که در واقع برابر حجم هر سکتور یک فلاپی دیسک هست انجام میشه
پس تا اینجا منظور این بوده که وقتی کامپیوتر ریست میشه ، بایوس میاد و اولین سکتور از هارد را میخونه و میخواد که اجراش کنه ! همین
(عکس ضمیمه )
پس نیاز داریم که با کامپایلری کار کنیم که اولا 16 بیتی را ساپورت کنه دوما مینیمم حجم را اشغال کنه ، پس از C نمیتونیم استفاده کنیم چون حداقل فایلی که تحویل میده حجمش بیشتر از این حرفاست
ویکی دیگه از دلایل اینه که فایلهای خروجی سی به علت ساختاری که دارند (PE) از همون بایت اول اجرائی نیستند ، و ما در بوت سکتور از همان بایت اول باید دست و آستین بالا بزنیم چون چیزی برای شناختن فرمت فایلها و غیره اصلا وجود نداره.
*** ادیت شد : با سی هم میشه
خب ممکنه سوال پیش بیاد که چرا همه سیستم عامل را با اسمبلی نمینویسیم ؟ سی یک زبان سطح بالا هست و برنامه نویسی باهاش راحت تره و به علاوه بسیاری از دستورات را به صورت کتابخانه های آماده در اختیارمون میذاره ، پس وقت ، که همون طلاست را بیخودی نباید با نوشتن کدهای اسمبلی هدر بدیم ( البته در نهایت برای طراحی بیس یک سیستم عامل نیاز فراوانی به اسمبلی داریم )
پس اول یک برنامه اسمبلی مینویسم که سیستم اصطلاحا بوت بشود و بعد درونش در واقع یک JUMP میکنیم به محلی که قراره Kernel اصلی سیستم عامل قرار داره برای اجرا به قول معروف Entry Point ، از اینجا به بعد دیگه محدودیتی به نام 512 بایت نداریم دیگه…
( مثلا برای ویندوز ما NTLDR را داریم)
مورد توجه نویسندگان بوت کیت ها
( خلاصه با هرچیزی میشه نوشت با سی یا بورلند سی یا فری بیسیک یا ….. که میشه برنامه های 32بیتی مستقل نوشت )
(با برنامه هایی که تحت ویندوز الان دارند کارمیکنند و از API ها استفاده میکنند و…اشتباه نشه )
پس در واقع ما برای اینکه یک سیستم عامل 32 بیتی داشته باشیم ، نیاز به مخلوطی از 16 بیتی و 32 بیتی داریم .
خب ما نیاز داریم به دونستن یکسری مطالب پایه مثل اشاره گرها ، آدرس های فضای فیزیکی ،تخصیص حافظه ،و….
در همین حین شما برای اینکه بیکار نمونید ابزارهائی که در آینده نیاز دارید را دانلود کنید
NASM
Pcopy
VFD
Bochs
Visual Studio 2005
C++
HxD Hex Editor
لینک ندادم توضیح هم ندادم که بیکار نمونید ، برگشتم دیدم دانلود نکردین ، خب خودم با ذکر توضیحات آپلود میکنم
بخش دوم :
این قسمت هم یکسری تعاریف اولیه را کامل میکنم و بعد دیگه…… ( حتی ممکنه جائیش را اشتباه بگم ، چاره ای نیست خودمم تازه شروع کردم ،ادعایی نیست ، شما ها هم خودتون کمک کنید تصحیح بشه خطاهای نوشتاری و درک مطلبی و… )
Pointer : یا اشاره گرها ، در واقع کارشون اینه که قسمتی از حافظه را به شما معرفی میکنند و در اختیار شما میگذارند ، چون در حالت معمولی خدا میدونه اطلاعات کجای حافظه سخت افزار PAS قراره ریخته بشه ! پس به واسطه متغییر از جنس اشاره گرها کلی کارمون راه می افته ، البته همونقدر که خوبه ، اگر هم درست آدرس دهی ها تعریف نشه همونقدر این اشاره گرها ممکن هستند گیج بزنند.
;char* pointer
_asm cli ; disable interrupts
یا اگر بیشتر از یک دستور داشته باشیم از بلوک ها استفاده میکنیم
_asm { cli hlt }
خب و مورد بعدی که باید یه توضیحی در مورد بدم RTL هست
Standard Library and the Run Time Library
استفاده از کتابخانه های برنامه نویسی که قبلا آماده شده ، مثلا دیگه نیاز نیست دستور printf() را بشینیم با اسمبلی بازنویسی کنیم ، یک کلمه مینویسیم اسم تابع را میریم پی کارمون
اینطوری میشه گفت حدود 90% از سیستم عامل کذائ که قراره نوشته بشه را از توابع آماده موجود از قبل استفاده میکنیم.
خب و اما یه بحث که یه کم میایم توی عمل ( با اون عمل پای منقل فرق میکنه ، معتادای بی جنبه )
Debug و خطایابی
خب اگه قرار باشه هربار که کدی برای سیستم عامل مینویسیم ، یکبار سیستم را ریست کنیم که نه تنها سیستم را مورد عنایت قرار میدیم بلکه کلی از وقتمون را هم هدر میدیم
اینجا برای تستها از یه ابزاری به نام Bochs استفاده میکنیم ، یه چیزی شبیه به VMWare هست ولی خب راحت تر و سریعتره…
( از هر نرم افزار دیگه ای هم میتونید استفاده کنید مثلا vpc میکروسافت یا …. هرکی هرچی دوست داره )
ببینم اصلا دانلود کردین اون لیستی که در قسمت بالا نوشتم ؟
لینک دانلود Bochs : http://Www.ALT.ir/os/Bochs-2.4.5.exe
نرم افزار جالبیه برای تستهای نوشتن یک سیستم عامل ( دلم به حال بیل گیتس اون زمانهایی که این شبیه سازها نبوده میسوزه )
ِ
میکروسافت ویندوز – ورژن 1 به سنه 1985
بخش سوم :
خب بریم برای مباحث شیرین تر
اصلا سیستم عامل چیه ؟ اینا را چرا مینویسیم؟ هدفمون چیه از داشتن یک به اصطلاح سیستم عامل؟
همه این کارها برای اینه که بتونیم این کامپیوتر دیجیتاله زبون نفهم را یه جوری رامش کنیم که کاربر بتونه باهاش ارتباط برقرار کنه و ازش کار بکشه ( نمونه هم که دیگه نیاز نیست مثال بزنم؟ ویندوز ، لینوکس ، مکینتاش و…. ) این که چرا خودمون میخوایم شروع کنیم به یادگیری هم که تکلیفش معلومه…
سیستم عامل یک نرم افزار نیست ، در واقع مجموعه ای از نرم افزارها هستند در کنار هم
زبانهای رایج برای توسعه سیستم عامل هم معمولا c++ و اسمبلی 80×86 هستند ( معمولا)
البته با زبانهای دیگه هم میشه ولی خب کار سخت تر میشه احتمالا
منابع خارجی برای آموزش سی :
cprogramming.com
Thinking in C++
و…. منبع آموزشی به تعداد موهای سر خودتون توی اینترنت هست ، فت و فراوون.
زبان اسمبلی هم که مناسب ترین زبان هست که میتونه با سخت افزار در پائین ترین سطح ارتباط برقرار کنه هم آموزش زیاد هست ( البته فهمش برای من یکی که همیشه سخت بوده )
مثلا خارجکی ها :
Assembly Language: Step by Step
Art of Assembly
ولی کلا این لینک دومیه جالبه ، یه جورائی گرافیکش را قبلا توی آموزشهای اسمبلی ندیده بودم، حتما ببینید
خب از ابزاری که لازم داریم مهمترینش NASM هست ، یک کامپایلر اسمبلی ،
http://www.nasm.us
هم پیج های دولوپرهاش را هم ببینید ، هرکدومشون ماشالا پروژه های جالبی را اجرا کردند.
نکته اینه که این کامپایلر برای ما ترجیح داره به توربو اسمبلی و میکروسافت اسمبلی _ TASM – MASM به علت اینکه هم 16 بیتی فلت را ساپورت میکنه هم 32 بیتی و هم 64 بیتی !!
به قول خودش :
!!!bit-64 (x86-64/x64/AMD64/Intel 64) Support Is Here
C:\>debug boot_loader.bin -w 100 0 0 1 -q
ابزار بعدی که بهش نیاز داریم ( برای راحتی کار )
VFD – Virtual Floppy Drive
از اینجا دانلود کنید ( بعدا آموزشش را موقع کار مینویسیم ، البته یادگیریش سخت نیست !)
http://www.ALT.ir/os/VFD.zip
برنامه خیلی خوبیه برای ساخت یک فلاپی دیسک مجازی که بوت لودری که بعدا مینویسیم را بتونیم روش رایت کنیم و تستهامون را خیلی سریع بگیریم… همه مدل فرمتی را هم قبول میکنه
البته یهو دلتون بخواد CD بوت ایبل بسازید ، اونم غمی نداره با MAgicISO میتونید.
به طور خلاصه مراحل استفاده از ابزارهای معرفی شده در بالا این میشه که
1- تنظیمات اولیه لازم برای شروع کار
شامل ساخت فلاپی مجازیVFD و داشتن یک ماشین مجازی Bochs
2- بوت لودر
شامل برنامه نویسی به زبان اسمبلی و کامپایلش با NASM و کپی کردن برنامه کامپایل شده روی بوت سکتور مثلا با PartCopy یا Debug یا HxD Hex Editor یا…
3-نوشتن کرنل
با استفاده از زبانهای سطح بالائی که معرفی شد مثلا c++ یا هرچی دیگه
و کپی فایل نهائی روی فلاپی دیسکت
4- تست کارکرد
Bochs Emulator را هم که پست قبلی لینک دادم حتما دانلود کنید
بخش چهارم :
برای داشتن یک سیستم عامل در حد قابل قبول نیاز داریم به :
Memory Management – مدیریت حافظه
Program Management – مدیریت برنامه ها
Multitasking – چند وظیفه ای یا اجرای همزمان چند پروسه
Memory Protection – حفاظت از خرابی حافظه
Multiuser – پشتیبانی از یک یا چند اپراتور سیستم
Kernel – هسته اصلی سیستم عامل برای مدیریت بقیه گزینه ها
File System – یک فایل سیستم برای کار کردن با فایلها
Command Shell – رابط خط فرمان
Graphical User Interface – یک محیط گرافیکی جایگزین خط فرمان
Bootloader – و بوت لودر که به زودی میریم سراغش
حتما تاریخچه سیستم عامل ها را میدونید ، اول فقط تک وظیفه ای و خط فرمانی مثل داس بودند ، بعد به صورت مولتی تسک در اومدن و حالا به صورت گرافیکی و و چند هسته ای و و و و …. پس انتظار زیادی از خودمون فعلا نداشته باشیم
خب امیدوارم تمام مطالب 3 تا پست قبلی را هم خوب خونده باشین( خط به خط ) چون “الف” کار طراحی سیستم عامل و نیازهای ابتدائی بودن …
خب…..
ساده ترین کد که قاطی این بوت لودرهای آموزشی کف اینترنت دیدم اینه و امشب اولین بوت لودر را مینویسم و کامپایل میکنیم و تست و اجرا میکنم.
org 0x7c00 ; We are loaded by BIOS at 0x7C00 bits 16 ; We are still in 16 bit Real Mode Start: cli ; Clear all Interrupts hlt ; halt the system times 510 - ($-$$) db 0 ; We have to be 512 bytes. Clear the rest of the bytes with 0 dw 0xAA55 ; Boot Signiture
خب خطوط نوشته شده هرکدوم چه معنی میدن ؟
org 0x7c00 ; We are loaded by BIOS at 0x7C00
[ORG 0] jmp 07C0h:start ; Goto segment 07C0 start: ; Update the segment registers
bits 16 ; We are still in 16 bit Real Mode
CLI hlt
times 510 - ($-$$) db 0
dw 0xAA55
nasm -f bin Boot1.asm -o Boot1.bin
اگه همه چیز درست باشه یک فایل با پسوند .bin برای شما ساخته میشه
خب حالا نرم افزار VFD را اجرا کنید و یک فلاپی مجازی بسازید
خب برنامه PartCopy را هم که لینکش را دادم با این خط دستور اجرا کنید
partcopy Boot1.bin 0 200 -f1
معنیش چیه ؟
اولین پارامترش اسم فایل هست را در
دومین پارامتر میگه از بایت x شروع کن
تا سومین پارامتر بایت Y ( که اینجا 200 هست و در مبنای دسیمال همون 512 میشه ) در درایور f0 که برابر هست با A: یا f1 که برابر هست با B: کپی کن
خب بعد از این کار میتونید در برنامه VFD فلاپی مجازی که در ram ساختین و تبدیل به بوت هم شده را SAVE کنید و از این بوت ، یک ایمیج بوت ایبل همیشه داشته باشید
خب پس سیوش میکنیم مثلا به اسم test.img
دیگه میتونید VFD را هم ببندید.
خب برنامه Bochs را اجرا کنید ،
طبق عکس جلو برید
و بعد هم کلید start
یه چیزی به این شکل میبینید
خب حالا شما اولین بوت لودر خودتون را نوشتید و کامپایل کردید و اجرا هم کردید ( البته الان هیچ غلطی نمیکنه )
در ادامه روش نوشتن و استفاده از kernel ایشالا…
پیوست : یه بنده خدائی هست که یه سیستم عامل نوشته به اسم آراکس ، مطالب خوبی روی سایت برنامه نویس در این مورد بوت و سیستم عامل منتشر کرده که جا داره تشکر کنم ازش ( هرچند که فکر نمیکنم اینجا آیدی داشته باشه http://osdever.blogfa.com )
پیوست 2: یک لینک برای مطالعه در زمانهای بیکاری : http://www.nondot.org/sabre/os/articles/ProtectedMode/
بخش پنجم :
خب امشب میرسیم به سوال قبلی من در اینجا
اینکه یک دیسکت بوت ایبل داریم ولی توی ویندوز که میخوایم بازش کنیم میگه فرمت نشده!
برای این منظور یه چیزی داریم به اسم بلوک OEM که یکسری مشخصات درایو توی اون نوشته شده
bpbBytesPerSector: DW 512 bpbSectorsPerCluster: DB 1 bpbReservedSectors: DW 1 bpbNumberOfFATs: DB 2 bpbRootEntries: DW 224 bpbTotalSectors: DW 2880 bpbMedia: DB 0xF0 bpbSectorsPerFAT: DW 9 bpbSectorsPerTrack: DW 18 bpbHeadsPerCylinder: DW 2 bpbHiddenSectors: DD 0 bpbTotalSectorsBig: DD 0 bsDriveNumber: DB 0 bsUnused: DB 0 b***tBootSignature: DB 0x29 bsSerialNumber: DD 0xa0a1a2a3 bsVolumeLabel: DB "MOS FLOPPY " bsFileSystem: DB "FAT12 "
;********************************************* ; Boot1.asm ; - A Simple Bootloader ; ; Operating Systems Development Tutorial ;********************************************* bits 16 ; We are still in 16 bit Real Mode org 0x7c00 ; We are loaded by BIOS at 0x7C00 start: jmp loader ; jump over OEM block ;*************************************************; ; OEM Parameter block ;*************************************************; TIMES 0Bh-$+start DB 0 bpbBytesPerSector: DW 512 bpbSectorsPerCluster: DB 1 bpbReservedSectors: DW 1 bpbNumberOfFATs: DB 2 bpbRootEntries: DW 224 bpbTotalSectors: DW 2880 bpbMedia: DB 0xF0 bpbSectorsPerFAT: DW 9 bpbSectorsPerTrack: DW 18 bpbHeadsPerCylinder: DW 2 bpbHiddenSectors: DD 0 bpbTotalSectorsBig: DD 0 bsDriveNumber: DB 0 bsUnused: DB 0 b***tBootSignature: DB 0x29 bsSerialNumber: DD 0xa0a1a2a3 bsVolumeLabel: DB "MOS FLOPPY " bsFileSystem: DB "FAT12 " ;*************************************************; ; Bootloader Entry Point ;*************************************************; loader: cli ; Clear all Interrupts hlt ; halt the system times 510 - ($-$$) db 0 ; We have to be 512 bytes. Clear the rest of the bytes with 0 dw 0xAA55 ; Boot Signiture
start: jmp loader ; jump over OEM block
TIMES 0Bh-$+start DB 0
اینو شما معنیشو بگین که چرا 11 بایت را برابر با 0 قرار میده؟!!!
خب برای چاپ یک کاراکتر نیاز به اینتراپت 0x10 داریم
پارامترهای این اینتراپت به این صورت هستند
AH = 0x0E AL = Character to write BH - Page Number (Should be 0) BL = Foreground color (Graphics Modes Only)
xor bx, bx ; A faster method of clearing BX to 0 mov ah, 0x0e mov al, 'A' int 0x10
;********************************************* ; Boot1.asm ; - A Simple Bootloader ; ; Operating Systems Development Tutorial ;********************************************* bits 16 ; We are still in 16 bit Real Mode org 0x7c00 ; We are loaded by BIOS at 0x7C00 start: jmp loader ; jump over OEM block ;*************************************************; ; OEM Parameter block ;*************************************************; TIMES 0Bh-$+start DB 0 bpbBytesPerSector: DW 512 bpbSectorsPerCluster: DB 1 bpbReservedSectors: DW 1 bpbNumberOfFATs: DB 2 bpbRootEntries: DW 224 bpbTotalSectors: DW 2880 bpbMedia: DB 0xF0 bpbSectorsPerFAT: DW 9 bpbSectorsPerTrack: DW 18 bpbHeadsPerCylinder: DW 2 bpbHiddenSectors: DD 0 bpbTotalSectorsBig: DD 0 bsDriveNumber: DB 0 bsUnused: DB 0 b***tBootSignature: DB 0x29 bsSerialNumber: DD 0xa0a1a2a3 bsVolumeLabel: DB "MOS FLOPPY " bsFileSystem: DB "FAT12 " ;*************************************************; ; Bootloader Entry Point ;*************************************************; loader: cli ; Clear all Interrupts xor bx, bx ; A faster method of clearing BX to 0 mov ah, 0x0e mov al, 'J' int 0x10 hlt ; halt the system times 510 - ($-$$) db 0 ; We have to be 512 bytes. Clear the rest of the bytes with 0 dw 0xAA55 ; Boot Signiture
خب کامپایلش کنید و با دستور partcopy اون را روی فلاپی مجازی بریزید و ازش یک سیو بگیرید و با یک شبیه ساز که اینجا Bochs هست اجراش کنید
از vmware میتونید استفاده کنید
از فلاپی واقعی میتونید استفاده کنید
حتی میتونید فلش مموری خودتون را هم به این صورت ویرایش کنید و اون را بوت ایبل کنید و با تنظیم دیسک بوت در Setup کامپیوترتون به صورت واقعی یک تست بگرید
بخش ششم :
خب فقط الان یه توضیح کوتاه بدم در مورد چاپ رشته ها ( قاطی این آموزشها بود گفتم منم بنویسمشون)
توی زبان استمبلی !( اسمبلی سابق) رشته ها را به این صورت تعریف میکنند :
msg db "Welcome to My Operating System!", 0
;*************************************** ; Prints a string ; DS=>SI: 0 terminated string ;*************************************** Print: lodsb or al, al ; al=current character jz PrintDone ; null terminator found mov ah, 0eh ; get next character int 10h jmp Print PrintDone: ret
LODSB Load byte at address DS:(E)SI into AL
mov si, msg call Print
;********************************************* ; Boot1.asm ; - A Simple Bootloader ; ; Operating Systems Development Tutorial ;********************************************* bits 16 ; We are still in 16 bit Real Mode org 0x7c00 ; We are loaded by BIOS at 0x7C00 start: jmp loader ; jump over OEM block ;*************************************************; ; OEM Parameter block ;*************************************************; ; Error Fix 2 - Removing the ugly TIMES directive ------------------------------------- ;; TIMES 0Bh-$+start DB 0 ; The OEM Parameter Block is exactally 3 bytes ; from where we are loaded at. This fills in those ; 3 bytes, along with 8 more. Why? bpbOEM db "My OS " ; This member must be exactally 8 bytes. It is just ; the name of your OS :) Everything else remains the same. bpbBytesPerSector: DW 512 bpbSectorsPerCluster: DB 1 bpbReservedSectors: DW 1 bpbNumberOfFATs: DB 2 bpbRootEntries: DW 224
یه کم توضیحات.
حلقه ها یا Ring
سیستم های نرم افزاری از چند حلقه تشکیل شده اند Ring0 که پائینترین سطح را داره ( سیستم عامل اینجاست ) و حلقه 2 و 3 که حلقه 3 یا rin3 سطحیه که کاربر توی اون قرار داره
حلقه صفر بیشترین دسترسی به سیستم و همینطور دسترسی کم میشه تا برسه به سطح کاربر
سیستم عامل قاعدتا باید بیشترین سطح دسترسی را به سیستم داشته باشه ، پس قاعدتا در حلقه صفر باید باشه
تنها مرحله ای که ما نهایت محدودیت را داریم مرحله اول یا هموان بوت لودر هست که فضائی در حد 512 بایت داریم ( موجود در اولین سکتور دیسک ( سکتورصفر)) ، نه بیشتر و اسمبلی و زبانی که توش ساپورت میشه اسمبلی 16 بیتی هست ، پس برای رسیدن به یک سیستم عامل 32 بیتی باید یه نرم افزار واسط بنویسم (همان کرنل) یا مرحله دوم ( stage 2 )
بعضی جاها خودشون را راحت کردند ، بهش گفتن بوت لودر دو مرحله ای
پس ما برای اینکه به بقیه فضای دیسک بتونیم دسترسی داشته باشیم باید بتونیم بقیه سکتورها را بخونیم.
خب پس ما نیاز داریم به وقفه ( اینتراپت ) ای که بتونه بره و دیسکت را بخونه و اطلاعاتشو به ما برگردونه این وقفه شماره 13 هست ( INT13 )
پس اول باید دیسک را ریست کنیم ، چون نمیدونیم که موقع شروع ، سخت افزار دقیقاکجا بوده
قبلا
.Reset: mov ah, 0 ; reset floppy disk function mov dl, 0 ; drive 0 is floppy drive int 0x13 ; call BIOS jc .Reset ; If Carry Flag (CF) is set, there was an error. Try resetting again
INT 0x13/AH=0x0 – DISK : RESET DISK SYSTEM
AH = 0x0
DL = Drive to Reset
Returns:
AH = Status Code
CF (Carry Flag) is clear if success, it is set if failure
BIOS Interrupt (INT) 0x13 Function 0x02 – Reading Sectors
INT 0x13/AH=0x02 – DISK : READ SECTOR(S) INTO MEMORY
AH = 0x02
AL = Number of sectors to read
CH = Low eight bits of cylinder number
CL = Sector Number (Bits 0-5). Bits 6-7 are for hard disks only
DH = Head Number
DL = Drive Number (Bit 7 set for hard disks)
ES:BX = Buffer to read sectors to
Returns:
AH = Status Code
AL = Number of sectors read
CF = set if failure, cleared is successfull
خب این
سیلندر چیه ؟ کلا هد چیه ؟ سکتور چیه ؟
شکل زیر را برای درک بهتر مسئله ببینید:
هر Track معمولا به 512 قسمت تقسیم شده
نکته : یک فلاپی دیسکت دارای 2 هد بیشتر نیست .
اگه مقادیری زیادتر از حد بهش بدیم ، سیستم نبفهمه و قاط میزنه
این از قسمتی مربوط به CH = Low eight bits of cylinder number
در موردDH = Head Number هم با اینکه فلاپی دوتا هد داره ولی همیشه باید از هد شماره 0 استفاده بکنیم.
در مورد DL = Drive Number (Bit 7 set for hard disks
شماره درایوی هست که میخواهیم باهاش کار کنیم ، شماره 0 همیشه فلاپی یا همون A: هست
شماره 1 معمولا به 5-1/4″ Floppy drives. گفته میشه که الان دیگه جزو عتیقه جاته ولی خب هنوز توسط کامپیوترها به صورت یک سنت قدیمی ساپورت میشه
یک کد مثال :
Reset: mov ah, 0 ; reset floppy disk function mov dl, 0 ; drive 0 is floppy drive int 0x13 ; call BIOS jc .Reset ; If Carry Flag (CF) is set, there was an error. Try resetting again mov ax, 0x1000 ; we are going to read sector to into address 0x1000:0 mov es, ax xor bx, bx .Read: mov ah, 0x02 ; function 2 mov al, 1 ; read 1 sector mov ch, 1 ; we are reading the second sector past us, so its still on track 1 mov cl, 2 ; sector to read (The second sector) mov dh, 0 ; head number mov dl, 0 ; drive number. Remember Drive 0 is floppy drive. int 0x13 ; call BIOS - Read the sector jc .Read ; Error, so try again jmp 0x1000:0x0 ; jump to execute the sector!
bpbBytesPerSector: DW 512 bpbSectorsPerCluster: DB 1 bpbReservedSectors: DW 1 bpbNumberOfFATs: DB 2 bpbRootEntries: DW 224 bpbTotalSectors: DW 2880 bpbMedia: DB 0xF0 bpbSectorsPerFAT: DW 9 bpbSectorsPerTrack: DW 18 bpbHeadsPerCylinder: DW 2 bpbHiddenSectors: DD 0 bpbTotalSectorsBig: DD 0 bsDriveNumber: DB 0 bsUnused: DB 0 b***tBootSignature: DB 0x29 bsSerialNumber: DD 0xa0a1a2a3 bsVolumeLabel: DB "MOS FLOPPY " bsFileSystem: DB "FAT12 "
bpbBytesPerSector: DW 512 bpbSectorsPerCluster: DB 1
bpbReservedSectors: DW 1 bpbNumberOfFATs: DB 2
A Reserved Sector is the number of sectors not included in FAT12. ie, not part of the Root Directory. In our case, The Bootsector, which contains our bootloader, will not be part of this directory. Because of this, bpbReservedSectors should be 1.
bpbRootEntries: DW 224 bpbTotalSectors: DW 2880
bpbMedia: DB 0xF0 bpbSectorsPerFAT: DW 9
The Media De******or Byte (bpbMedia) is a byte code that contains information about the disk. This byte is a Bit Pattern: * Bits 0: Sides/Heads = 0 if it is single sided, 1 if its double sided * Bits 1: Size = 0 if it has 9 sectors per FAT, 1 if it has 8. * Bits 2: Density = 0 if it has 80 tracks, 1 if it is 40 tracks. * Bits 3: Type = 0 if its a fixed disk (Such as hard drive), 1 if removable (Such as floppy drive) * Bits 4 to 7 are unused, and always 1.
bpbSectorsPerTrack: DW 18 bpbHeadsPerCylinder: DW 2
bpbHiddenSectors: DD 0
bpbTotalSectorsBig: DD 0 bsDriveNumber: DB 0
bsUnused: DB 0 b***tBootSignature: DB 0x29
bsSerialNumber: DD 0xa0a1a2a3 bsVolumeLabel: DB "MOS FLOPPY " bsFileSystem: DB "FAT12 "
;********************************************* ; Boot1.asm ; - A Simple Bootloader ; ; Operating Systems Development Tutorial ;********************************************* bits 16 ; We are still in 16 bit Real Mode org 0x7c00 ; We are loaded by BIOS at 0x7C00 start: jmp loader ; jump over OEM block ;*************************************************; ; OEM Parameter block / BIOS Parameter Block ;*************************************************; TIMES 0Bh-$+start DB 0 bpbBytesPerSector: DW 512 bpbSectorsPerCluster: DB 1 bpbReservedSectors: DW 1 bpbNumberOfFATs: DB 2 bpbRootEntries: DW 224 bpbTotalSectors: DW 2880 bpbMedia: DB 0xF0 bpbSectorsPerFAT: DW 9 bpbSectorsPerTrack: DW 18 bpbHeadsPerCylinder: DW 2 bpbHiddenSectors: DD 0 bpbTotalSectorsBig: DD 0
نکته:
با وجود سازگاری
NASM تو فایل manual
…
Its sole function is to specify one offset which is added to all internal address references within the section
Double ORG , New Segment Define , 32 bit و … جواب نداد
راه حل منطقی پیدا نشد
این لودر رو پیدا کردم که به روش خودش ( تعریف فایل کرنل ) مشکل رو حل کرده و لودر خوبی هم هست
; ; Copyright (c) Eike Dehling 2004, all rights reserved ; ; Compile with nasm, write to first sector of a floppy, have fun ; [BITS 16] [ORG 0x7c00] ; ------------------------------------- ; disk de******ion ; ------------------------------------- jmp short start ; 3 bytes to e.g. jump nop db "EikeBoot" ; 8 byte label / OemId dw 512 ; bytes per sector db 1 ; sectors per cluster dw 1 ; size of the bootloader, in sectors db 2 ; number of copies of the FAT dw 224 ; number of entries in Root-Dir dw 2880 ; 16-bit number of sectors db 0xf0 ; media de******or dw 9 ; number of sectors per FAT dw 18 ; sectors per track dw 2 ; number of heads dd 0 ; number of hidden sectors dd 0 ; 32-bit number of sectors db 0 ; bios drive number db 0 ; reserved db 0x29 ; extended boot signature dd 0 ; volume ID db "NO NAME "; volume label db "FAT12 " ; filesystem type ; ------------------------------------- ; bootloader code ; ------------------------------------- print_io_error_trampoline: jmp print_io_error ; out of range crap :( start: ; set segments, bios may set 0x07c0:0x0000 or 0x0000:0x7c00 jmp 0:start2 start2: mov ax, 0 mov ds, ax mov es, ax mov ss, ax mov sp, 0x7000 ; save boot device mov byte [bootdev], dl ; reset disks mov ah, 0 mov dl, byte [bootdev] int 0x13 ; load FAT mov ah, 0x02 ; read from disk mov al, 9 ; number of sectors to read mov bx, fatbuf ; es: bx = buffer mov ch, 0 ; track ((1 / 18) / 2) mov cl, 2 ; sector (1 % 18 + 1) mov dh, 0 ; head ((1 / 18) % 2) mov dl, byte [bootdev] ; dl = device, but that's still correct int 0x13 jc print_io_error_trampoline ; load root directory mov ah, 0x02 ; read from disk mov al, 14 ; number of sectors to read mov bx, rootdir ; es: bx = buffer mov ch, 0 ; track ((19 / 18) / 2) mov cl, 2 ; sector (19 % 18 + 1) mov dh, 1 ; head ((19 / 18) % 2) mov dl, byte [bootdev] ; dl = device, but that's still correct int 0x13 jc print_io_error_trampoline ; find file mov ax, rootdir ; ax = pointer to current dir-entry cld find_start: mov si, ax ; point si to current filename mov di, filename ; point di to the filename we need mov cx, 11 ; compare max 11 bytes repe cmpsb je found_file add ax, 32 ; done yet? cmp ax, rootdir_end jne find_start ; print error and halt mov si, file_not_found call print jmp $ found_file: ; save cluster mov si, ax mov ax, word [si + 26] ; load cluster of file from the directory entry mov word [cluster], ax ; decode FAT mov cx, 512*(9/3) ; number of entries in the fat. mov si, fatbuf mov di, fatdecoded ; ; XXX - WATCH OUT, THIS REALLY NEEDS TO USE EAX, EBX & CO!!! ; fat_decode_loop: ; load dword, split into two pieces of 12 bits, and discard last byte lodsd ; load dword dec si ; need only 3 bytes mov ebx, eax and eax, 0xFFF ; mask stosw mov eax, ebx shr eax, 12 and eax, 0xFFF ; shift & mask stosw loop fat_decode_loop ; load file: ; es:bx = buffer ; ds:si = decoded fat buffer ; rest is for temporary usage only ; prepare buffer mov ax, 0x1000 mov es, ax mov bx, 0 mov ax, word [cluster] load_loop: ; calculate next cluster mov si, ax shl si, 1 mov cx, word [fatdecoded + si] mov word [cluster], cx ; calculate track, head and sector add ax, 31 ; ax = logical sector mov dx, 0 mov di, 18 div di mov cl, dl add cl, 1 ; cl = sector = (logical % 18 + 1) mov dx, 0 mov di, 2 div di mov ch, al ; ch = track = ((logical / 18) / 2) mov dh, dl ; dh = head = ((logical / 18) % 2) ; read data mov ah, 0x02 mov al, 1 mov dl, byte [bootdev] int 0x13 jc print_io_error add bx, 512 ; done? mov ax, word [cluster] cmp ax, 0xFF8 jb load_loop ; DEBUG: print loaded file && halt ;mov si, 0 ;mov di, 0 ;mov ax, 0x1000 ;mov ds, ax ;mov ax, 0xb800 ;mov es, ax ;mov cx, 2000 ;mov al, ' ' ;print_file_loop: ;movsb ;stosb ;loop print_file_loop ;jmp $ ; execute file? ; jmp 0x1000:0x0000 ; jmp $ ; disable interrupts cli ; load our GDT lgdt [gdt] ; enable protected mode ... mov eax, cr0 or eax, 1 mov cr0, eax jmp 0x08 : dword cs_flush ; jump into 32-bit [BITS 32] cs_flush: ; setup segements mov eax, 0x10 mov ds, eax mov es, eax mov ss, eax mov fs, eax mov gs, eax ; make stack-register p-mode compliant shl esp, 4 ; clear flags push dword 0 popf ; Execute loaded file ... jmp 0x10000 [BITS 16] ; ------------------------------------- ; procedures ; ------------------------------------- print_io_error: mov si, io_error call print jmp $ ; print zero-terminated string (without formatting) ; ds:si = string ; es:di = position on screen print: mov ax, 0xb886 mov es, ax mov di, 0 print_loop: lodsw cmp al, 0 jz print_done stosw jmp print_loop print_done: ret ; ------------------------------------- ; data & variables (not in a separate section, cause that makes padding to the ; 512 byte border easier) ; ------------------------------------- ; filename filename: db "KERNEL BIN" ; messages io_error: db "I / O e r r o r ", 0 file_not_found: db "[ O S K e r n e l n o t f o u n d ] ", 0 executing: db "E x e c u t i n g . . . ", 0 gdt: dw (gdtend - gdt) dd gdt dw 0 ; first entry is our gdt-pointer dd 0x0000ffff, 0x00cf9a00 ; second entry: code dd 0x0000ffff, 0x00cf9200 ; third entry: data gdtend: ; end ; boot signature ; resb 510 - ($ - $$) ; align at 512 bytes times 510 - ($ - $$) db 0 ; align at 512 bytes dw 0xAA55 ; ------------------------------------- ; BSS data (starts at 0x8000, that leaves 32k space in the first 64k segment) ; ------------------------------------- [ABSOLUTE 0x8000] ; boot device bootdev: resb 1 ; first cluster of the file we need cluster: resw 1 ; FAT buffer fatbuf: resb 9 * 512 ; root directory rootdir: resb 224 * 32 rootdir_end: ; buffer for decoding the FAT fatdecoded: resb (9 * 512 * 3) / 2
تست شده و کاملا کار میکنه
این هم برای پیگیری بیشتر تصحیح مورد بالا
http://forum.osdev.org/viewtopic.php?f=1&t=15497
بخش هفتم :
هرچی میخوام این مطالب که چطوری محاسبه میشه و بوت لودر میاد هارد را سکتور به سکتور میخونه را بنویسم میبینم هم خودم کم فهمیدم هم مطالبش خیلی گنگه برام اینه که کلا بیخیال و یه پله را جا میندازیم میریم یه پله بعد و مستقیم یک لودر را تشریح میکنیم که عملا پله قبلی را توی دل خودش داره
;********************************************* ; Boot1.asm ; - A Simple Bootloader ; ; Operating Systems Development Series ;********************************************* bits 16 ; we are in 16 bit real mode org 0 ; we will set regisers later start: jmp main ; jump to start of bootloader ;********************************************* ; BIOS Parameter Block ;********************************************* ; BPB Begins 3 bytes from start. We do a far jump, which is 3 bytes in size. ; If you use a short jump, add a "nop" after it to offset the 3rd byte. bpbOEM db "My OS " ; OEM identifier (Cannot exceed 8 bytes!) bpbBytesPerSector: DW 512 bpbSectorsPerCluster: DB 1 bpbReservedSectors: DW 1 bpbNumberOfFATs: DB 2 bpbRootEntries: DW 224 bpbTotalSectors: DW 2880 bpbMedia: DB 0xf8 ;; 0xF1 bpbSectorsPerFAT: DW 9 bpbSectorsPerTrack: DW 18 bpbHeadsPerCylinder: DW 2 bpbHiddenSectors: DD 0 bpbTotalSectorsBig: DD 0 bsDriveNumber: DB 0 bsUnused: DB 0 b***tBootSignature: DB 0x29 bsSerialNumber: DD 0xa0a1a2a3 bsVolumeLabel: DB "MOS FLOPPY " bsFileSystem: DB "FAT12 " ;************************************************; ; Prints a string ; DS=>SI: 0 terminated string ;************************************************; Print: lodsb ; load next byte from string from SI to AL or al, al ; Does AL=0? jz PrintDone ; Yep, null terminator found-bail out mov ah, 0eh ; Nope-Print the character int 10h jmp Print ; Repeat until null terminator found PrintDone: ret ; we are done, so return ;************************************************; ; Reads a series of sectors ; CX=>Number of sectors to read ; AX=>Starting sector ; ES:BX=>Buffer to read to ;************************************************; ReadSectors: .MAIN mov di, 0x0005 ; five retries for error .SECTORLOOP push ax push bx push cx call LBACHS ; convert starting sector to CHS mov ah, 0x02 ; BIOS read sector mov al, 0x01 ; read one sector mov ch, BYTE [absoluteTrack] ; track mov cl, BYTE [absoluteSector] ; sector mov dh, BYTE [absoluteHead] ; head mov dl, BYTE [bsDriveNumber] ; drive int 0x13 ; invoke BIOS jnc .SUCCESS ; test for read error xor ax, ax ; BIOS reset disk int 0x13 ; invoke BIOS dec di ; decrement error counter pop cx pop bx pop ax jnz .SECTORLOOP ; attempt to read again int 0x18 .SUCCESS mov si, msgProgress call Print pop cx pop bx pop ax add bx, WORD [bpbBytesPerSector] ; queue next buffer inc ax ; queue next sector loop .MAIN ; read next sector ret ;************************************************; ; Convert CHS to LBA ; LBA = (cluster - 2) * sectors per cluster ;************************************************; ClusterLBA: sub ax, 0x0002 ; zero base cluster number xor cx, cx mov cl, BYTE [bpbSectorsPerCluster] ; convert byte to word mul cx add ax, WORD [datasector] ; base data sector ret ;************************************************; ; Convert LBA to CHS ; AX=>LBA Address to convert ; ; absolute sector = (logical sector / sectors per track) + 1 ; absolute head = (logical sector / sectors per track) MOD number of heads ; absolute track = logical sector / (sectors per track * number of heads) ; ;************************************************; LBACHS: xor dx, dx ; prepare dx:ax for operation div WORD [bpbSectorsPerTrack] ; calculate inc dl ; adjust for sector 0 mov BYTE [absoluteSector], dl xor dx, dx ; prepare dx:ax for operation div WORD [bpbHeadsPerCylinder] ; calculate mov BYTE [absoluteHead], dl mov BYTE [absoluteTrack], al ret ;********************************************* ; Bootloader Entry Point ;********************************************* main: ;---------------------------------------------------- ; code located at 0000:7C00, adjust segment registers ;---------------------------------------------------- cli ; disable interrupts mov ax, 0x07C0 ; setup registers to point to our segment mov ds, ax mov es, ax mov fs, ax mov gs, ax ;---------------------------------------------------- ; create stack ;---------------------------------------------------- mov ax, 0x0000 ; set the stack mov ss, ax mov sp, 0xFFFF sti ; restore interrupts ;---------------------------------------------------- ; Display loading message ;---------------------------------------------------- mov si, msgLoading call Print ;---------------------------------------------------- ; Load root directory table ;---------------------------------------------------- LOAD_ROOT: ; compute size of root directory and store in "cx" xor cx, cx xor dx, dx mov ax, 0x0020 ; 32 byte directory entry mul WORD [bpbRootEntries] ; total size of directory div WORD [bpbBytesPerSector] ; sectors used by directory xchg ax, cx ; compute location of root directory and store in "ax" mov al, BYTE [bpbNumberOfFATs] ; number of FATs mul WORD [bpbSectorsPerFAT] ; sectors used by FATs add ax, WORD [bpbReservedSectors] ; adjust for bootsector mov WORD [datasector], ax ; base of root directory add WORD [datasector], cx ; read root directory into memory (7C00:0200) mov bx, 0x0200 ; copy root dir above bootcode call ReadSectors ;---------------------------------------------------- ; Find stage 2 ;---------------------------------------------------- ; browse root directory for binary image mov cx, WORD [bpbRootEntries] ; load loop counter mov di, 0x0200 ; locate first root entry .LOOP: push cx mov cx, 0x000B ; eleven character name mov si, ImageName ; image name to find push di rep cmpsb ; test for entry match pop di je LOAD_FAT pop cx add di, 0x0020 ; queue next directory entry loop .LOOP jmp FAILURE ;---------------------------------------------------- ; Load FAT ;---------------------------------------------------- LOAD_FAT: ; save starting cluster of boot image mov si, msgCRLF call Print mov dx, WORD [di + 0x001A] mov WORD [cluster], dx ; file's first cluster ; compute size of FAT and store in "cx" xor ax, ax mov al, BYTE [bpbNumberOfFATs] ; number of FATs mul WORD [bpbSectorsPerFAT] ; sectors used by FATs mov cx, ax ; compute location of FAT and store in "ax" mov ax, WORD [bpbReservedSectors] ; adjust for bootsector ; read FAT into memory (7C00:0200) mov bx, 0x0200 ; copy FAT above bootcode call ReadSectors ; read image file into memory (0050:0000) mov si, msgCRLF call Print mov ax, 0x0050 mov es, ax ; destination for image mov bx, 0x0000 ; destination for image push bx ;---------------------------------------------------- ; Load Stage 2 ;---------------------------------------------------- LOAD_IMAGE: mov ax, WORD [cluster] ; cluster to read pop bx ; buffer to read into call ClusterLBA ; convert cluster to LBA xor cx, cx mov cl, BYTE [bpbSectorsPerCluster] ; sectors to read call ReadSectors push bx ; compute next cluster mov ax, WORD [cluster] ; identify current cluster mov cx, ax ; copy current cluster mov dx, ax ; copy current cluster shr dx, 0x0001 ; divide by two add cx, dx ; sum for (3/2) mov bx, 0x0200 ; location of FAT in memory add bx, cx ; index into FAT mov dx, WORD [bx] ; read two bytes from FAT test ax, 0x0001 jnz .ODD_CLUSTER .EVEN_CLUSTER: and dx, 0000111111111111b ; take low twelve bits jmp .DONE .ODD_CLUSTER: shr dx, 0x0004 ; take high twelve bits .DONE: mov WORD [cluster], dx ; store new cluster cmp dx, 0x0FF0 ; test for end of file jb LOAD_IMAGE DONE: mov si, msgCRLF call Print push WORD 0x0050 push WORD 0x0000 retf FAILURE: mov si, msgFailure call Print mov ah, 0x00 int 0x16 ; await keypress int 0x19 ; warm boot computer absoluteSector db 0x00 absoluteHead db 0x00 absoluteTrack db 0x00 datasector dw 0x0000 cluster dw 0x0000 ImageName db "KRNLDR SYS" msgLoading db 0x0D, 0x0A, "Loading Boot Image ", 0x0D, 0x0A, 0x00 msgCRLF db 0x0D, 0x0A, 0x00 msgProgress db ".", 0x00 msgFailure db 0x0D, 0x0A, "ERROR : Press Any Key to Reboot", 0x0A, 0x00 TIMES 510-($-$$) DB 0 DW 0xAA55
add bx, WORD [bpbBytesPerSector] ; queue next buffer inc ax ; queue next sector loop .MAIN ; read next sector
همچنین معرفی ImageName در اواخر برنامه که نام فایل کرنل را مشخص میکنه ،
و خلاصه سورس جالبه ، فقط یک نکته داره ، اونم اینه که وقتی ما فایل کرنل را برای تست روی فلاپی کپی میکنیم ، هنوز این سیستم عامل ویندوز هست که فایل را کپی میکنه و فایل مپ را در واقع میسازه
خیالتون را راحت کنم فعلا منم هنوز چیزی در این مورد نخوندم ولی در ادامه تا وقتی که
stage3 یا کرنل 32بیتی را میخوایم اجرا کنیم به مرور این مطالب را بهش میرسیم
فقط جهت دونستن کلیات این مورد فعلا :
خب تا اینجا فرض میگیرم این لودر را دقیقا میدونیم چیکار میکنه ! ، اصلا بیخیال ، صبر و حوصله شو نداریم و همین که کار میکنه بسه بهتره بریم سر اصـــــــــــــــــــــــ ــــــــــــل مطلب ، یعنی لود کردن یک فایل کرنل ساده
یک فایل ساده مینویسم که فقط یک متن نشون بده
;********************************************* ; Stage2.asm ; - Second Stage Bootloader ; ; Operating Systems Development Series ;********************************************* org 0x0 ; offset to 0, we will set segments later bits 16 ; we are still in real mode ; we are loaded at linear address 0x10000 jmp main ; jump to main ;*************************************************; ; Prints a string ; DS=>SI: 0 terminated string ;************************************************; Print: lodsb ; load next byte from string from SI to AL or al, al ; Does AL=0? jz PrintDone ; Yep, null terminator found-bail out mov ah, 0eh ; Nope-Print the character int 10h jmp Print ; Repeat until null terminator found PrintDone: ret ; we are done, so return ;*************************************************; ; Second Stage Loader Entry Point ;************************************************; main: cli ; clear interrupts push cs ; Insure DS=CS pop ds mov si, Msg call Print cli ; clear interrupts to prevent triple faults hlt ; hault the syst ;*************************************************; ; Data Section ;************************************************; Msg db "Preparing to load operating system...",13,10,0
کامپایلش میکنیم
و فایل ساخته شده را به اسم KRNLDR.SYS تغییر نام میدیم
تا اینجا پس 2تا فایل داریم ، اولین که همون بوت لودر هست و با دستور
partcopy Boot1.bin 0 200 -f0
اون را روی فلاپی ریختیم
و این دومی که یک فایله و به صورت معمولی اون را Copy&Paste میکنیم روی فلاپی ( این دیگه partcopy نمیخواد )
حالا چی داریم ؟ یک دیسکت بوت که بعد از بوت شدن میگرده دنبال فایل کرنل و اون را اجرا میکنه
از حالا دیگه فقط باید فکرمون را بزاریم روی تکمیل این کرنل و بحث بوت لودر را تا وقتی که برسیم به قسمتهایی که باید بهش رجوع مجدد کنیم و تغییراتی برای سیستم عامل 32 بیتی توش بدیم همینجا بسته نگهش میداریم..
ادامه دارد…
بخش هشتم :
اگه سیستم شما فلاپی درایو واقعی داشته باشه ، مقدارش برابر با f0 هست
اگه درایو جدید با vfd میسازید و b: میشه باید به جای اون مقدار f1 را قرار بدید
این پیغام وقتی داده میشه که نتونه روی درایو write انجام بشود.
خب امروز یه کم تئوری داریم. مبحث شیرین مد محافظت شده Protect Mode
( هنوز سر بحث قبلی که نشد کامل توضیح بدم یعنی “ساختار فایل سیستم FAT ” وژدانم ناراحته )
خب تا اینجا دیدین که سیستم بوت شد و همزمان یک کار( یک وظیفه ) قابل انجام بود و هیچی دیگه !
در حالت واقعی Real Mode ما میتونیم یک بایت را یکجائی بنویسیم ولی همزمان نمیشه هم پورتهای سیستم را خوند ، هم نوشت، هم پشتک و وارو زد
خلاصه کامپیوتر در این مد ، عملا فرقی با یک مرکز کنترل دیجیتال 0 . 1 بی خاصیت نداشت.
PMode این حالت در میکروپروسسورهای 80286 به بعد به وجود اومد برای استیبل تر شدن سیستمها و جلوگیری از خطاهایی که در ادامه میگم چیا بودن * ( این ستاره را اون آخر میگم)
در حالت ReadMode شما حافظتی از حافظه نداشتید (هرجائیش هرموقع ، بی حساب کتاب میتونستید دیتابنویسید و بخونید)
در حالت RM شما دسترسی به 16بیت رجیستر داشید که نهایتا میتونستید تا 1 مگابایت حافظه را آدرس دهی کنید
در RM شما هیچ پشتیبانی از حافظت حافظه سخت افزاری و یا مولتی تسکینگ نداشتید.
و مهمترین مشکل نبود Ring های مختلف در حالت RM بود ، یعنی چی ؟ یعنی همه کدهایی که نوشته میشن در واقع در Ring0 نوشته میشوند.! و این توی دنیای سیستم عاملهای امروز اصلا جالب نیست. مثلا با اجرای یک دستور CLI یا HLT تمام سیستم فاتحه اش خونده میشه. یعنی یک خرابی ( فقط یکی رخ بده ) عملا سیستم عامل و ادامه کار کامپیوتر به کشک تبدیل میشد.
اما خواص Protect Mode که همه جا به PMode معروفه
Protected Mode:
* Has Memory Protection
* Has hardware support for Virtual Memory and Task State Switching (TSS)
* Hardware support for interruting programs and executing another
* 4 Operating Modes: Ring 0, Ring 1, Ring 2, Ring 3
* Access to 32 bit registers
* Access to up to 4 GB of memory
دارای حفاظت از حافظه (memory )
پشتیبانی سخت افزاری از حافظه مجازی و انجام وظیفه سوئیچینگ خارجی (منظورشو نفهمیدم)
پشتیبانی اینتراپت ها و قابل استفاده کردنشون در برنامه های دیگر.
داشتن رینگ های کاری مختلف ( از 0 تا 3)
دسترسی به رجیسترهای 32 بیتی ( پس فکر کردین میگن سیستم عامل 32بیتی منظورشون چیه
به دلیل فوق ، امکان سازماندهی حافظه تا 4گیگابایت.
شاید دیده باشین مادربوردهای قدیمی ( نه چندان قدیمی ) بیشتر از 4 گیگ رم ساپورت نمیکنن! ( احتمالا از همینه )
خب دیدیم که برنامه های اولیه ما در رینگ 0 اجرا میشدند در حالی که میدونیم برنامه های کاربر باید در رینگ 3 اجرا بشوند ، بنابراین ما در اینجا باید از یکی از ثابتها و دستورالعملهای پردازنده استفاده کنیم به نام LGDT
آشنایی با معماری سیستم و چگونگی کارکرد پردازنده به ما کمک خواهد کرد برای درک خیلی بهتر این مسئله.
یه نگاه به صورت کلی به خانوده x86 یا همین کامپیوترهامون بندازیم میبینم سرتاپاشون 3تا چیز بیشتر نیست
CPU
MEMORY
I/O _Input/Output
به قول یکی از دوستان ، سرخپوستی کامل ، خودش و شرتش و اسبش .
شکل فوق یک سرخپوست x86 را نشون میده
این طرز کار بالا در عین سادگی ، در واقع شیرینی کامپیوتر را نشون میده… با یک روش کار ساده و اصولی ، همه ما ها داریم حال میکنیم دیگه.
تمام ورودی ها و خروجیهای کامپیوتر ( از کیبورد و موس و مانیتور گرفته تا کارتهای PCI – ISA و پورتهای usb و cd-rom و واحد Memory یا RAM همه از طریق رابطی به نام System BUS با قلب پردازش یا CPU در ارتباط هستند و یک فروند cpu همه اینها را فرماندهی میکنه.
هر اتفاقی بی افته از فشار دادن یک کلید تا نوشتن و خواندن روی حافظه قابل دسترسی میشه برامون.
ما به عنوان یک برنامه نویس سیستمی در اول کار باید همه اینائی که اون بالا گفتیم را بتونیم در پایئن ترین سطح (رینگ0) بتونیم پوشش بدیم.
در system bus ما یک Data bus داریم و یک address Bus
همه اینها در مجموع عملا چیزی نیست جز سطح ولتاژ منطقی 0 و 1
خب اول DATA bus را یه توضیحی بدم
موجی از این صفر و یک هاست که عملا بعد از جمع شدن پشت سر هم معنی پیدا میکنند
الکترونیکی ها با عبارت TTL آشنا هستند ، این سیستم باس دقیقا از همین استفاده میکنند
این منطق رد و بدل شدن اطلاعات به صورت همزمان در سه سطح تعریف شده
16 – 32 – 64 ( یعنی مثلا 32 بیت همزمان به cpu ارسال میشه یعنی 32 تا نقطه درنظربگریدی در یک لحظه ممکنه هرکدوم روشن یا خاموش باشند ) و عملا معنی cpu ی مثلا 64 بیتی ازهمینجا نشات میگیره ( در ادامه رجیسترها را در هرکدوم حالات یه توضیحی میدم )
خب وقتی یک cpu بتونه به جای 16 تا داده مثلا 32 حالت را تشخیص بده ، عملا سرعت اون cpu خیلی خیلی بیشتر از 16تائی میشه ، و به همین نسبت 64 بیتی و این اواخر هم 256 بیتی شنیدم بروبچ اونطرف آبی ، از ما بهترون ، در صنایع نظامی استفاده میکنند.
پس ما عملا در هر سیکل کاری cpu بسته به نوعش بسته های 16 الی 64 تائی از این 0ویک ها رد و بدل میکنیم.
و منبعد منظور ما از 16 بیتی یا 32 بیتی ، عملا مقدار اطلاعات قابل عبور ازاین گذرگاه باس هست. مثلا میگیم :
32 bit processor
اما Address Bus جیست ، اگه به شکل نگاه کنید همه دیوایس ها و حافظه و قطعات از طریق یک مجرا به هم وصل هستند، اگه ما فرمانی مثلا به ram بدیم قاعدتا باید i/o قاطی کنه
ااینجا آدرس باس به کمک میاد و برای هر چیزی یک آدرس منحصر به فرد در نظر میگیره
اگه به این BUS به شکل معنی اتوبوس واحد نگاه کنیم بد نیست ،
یک سیستم قدیمی 80186 کلا 20 خط را ساپورت میکرد
یک سیستم 80286 یا 80386 تا 24 خط را روی باسش ساپورت میکنه
یک سیستم از 80386 به بالا تا 32 بیت ( bit/line بیت بر خط )
………….
اومدم یک آمپول متوکلروپرامید را بشکنم انگشتم را همین الان بریدم … تایپ باشه برای یه شب دیگه
* :
( زکی پس ما تو بخش الکترونیک زدیم روی avr ی 8 بیتی سیستم عامل مولتی پروسس پیاده کردیم و شد )
خب یه کم دیگه در مورد حافظه توضیح بدم
حتما حافظه های DDR , DDR2 و اینا به گوشتون خورده
حافظه های DDR یا Double Data Rate (DDR) Controller برای هر بار خواندن یا نوشتن از یک پالس سیستم استفاده میکنند
و در حالت Dual Channel Controller در هر پالس سیستم هم میشه از حافظه خواند و هم نوشت
خوشبختانه دردسر سرو کله زدن با دیوایس حافظه به عهده یک قطعه فیزیکی روی مادربرد هست به اسم 1337 Multiplexer که توضیحات فنی و دیتاشیت اون برای علاقه مندان روی گوگل هست
اما بریم سراغ زیربنای ارتباطات ما که همان subsystem I/O یا به زبون راحت تر PORT ها هستند…
ما دو دسته پورت داریم ، سخت افزاری و نرم افزاری
از پورتهای سخت افزاری میشه پورت کیبورد . موس . سریال – پارالل – یو اس بی . 1394 . وووو نام برد
از نظر الکترونیکی این پورتها حامل سیگنالها هستند که اطلاعات خودشون را روی باس سیستم به cpu تحویل میدهند
شما با نوشتن یا خواندن مستقیم روی آدرس این پورتهاو ارسال دیتا به اونها میتونید عملا اونها را کنترل کنید
اما قسمت مهم جائیه که بهش میگیم پورتهای نرم افزاری که عملا با اختصاص یک شماره ، دسترسی به پورتهای سخت افزاری را ایجاد میکنه. محل نگهداری این اعداد ( بخونید آدرسها) اصطلاحا به Memory Mapped I/O معروف هست.
مثلا آدرس 0xA000:0 هرچی توش بنویسید عملا انگار روی قسمت مربوط به خروجی گرافیک سیستم نوشتید و بلافاصله نتیجه را روی خروجی ( مانیتور) میتونید ببینید
در حالت Real Mode در سیستم های X86 ما یک جدول ثابت داریم
x86 Real Mode Memory Map
با استفاده از جدول بالا ( که ایشالا همیشه ثابته ) ما عملا دسترسی به زیربنای یک کامپیوتر داریم
خب به عنوان مثال شاید خوندین که INT19 سیستم را ریست میکنه ، این دستور عملا میاد مقدار 0x1234 را در آدرس 0x0040:0x0072 مینویسه و بعد جامپ میکنه ( پرش میکنه ) به آدرس 0xFFFF:0, نتیجه این عمل ریست شدن کامپیوتره ( به قول خودشون ریست گرم ) یا همان مشابه عمل Ctrl+Alt+del در داس یا ویندوزهای قدیمی هست.
احتمالا الان متوجه میشید که این Int19 یک پورت نرم افزاری تعریف شده ، پس میتونه توی یک سیستم عامل سیستم را تعریف کنیم که کامپیوتر را ریست کنه و توی یک سیستم دیگه کامپیوتر ریست نشه.
تبدیل آدرس 0x0040 : 0x0072 به آدرس مطلق( واقعی ) میشه : 0x000000472
که مکانی است که در بایوس وظیفه ریست سیستم را دارد.
و به همین منوال… مثلا آدرس 0x000B8000 اگه بنویسیم انگار افکت دادیم به متن نوشته های روی مانیتور
( ( اون قسمت Our Bootloader براتون آدرس آشنا نیست؟ )
خب برگردیم به مبحث Port Mapping
وقتی سیستم بوت میشه ، ROM Bios که یک IC هست روی مادربرد وظیفه آدرس دهی به سخت افزارهای متصل به خودش را داره و با ساخت جدول برداری وقفه هارا اجازه میده که با استفاده از این اعداد ، سخت افزارها کار کنندو تحت کنترل باشند.
خب ما اینجا نیاز به یک استاندارد داریم که هربار یک آدرس رندوم به هر دیوایسی داده نشه ، و الا بدبخت بودیم که و عملا پردازنده میاد یک آدرس همیشگی برای آدرس باسهای دیوایسهای مختلف به عنوان استاندارد برای دسترسی بهشون اعمال میکنه
این نکته خیلی مهمه ، چون تنها راه محافظت شده و تضمین شده به سخت افزار هست
hardware protect Mode
مقداری از این جدول را در زیر مینویسم ( این جدول خیلی طولانیه )
توجه کنید که این جدول کامل کامل نیست …
و شاید در کل این جدول ، آدرس پورت پارالل براتون آشنا باشه (0x378)
اما میتونید به عنوان یک مرجع برای دسترسی به پورتهای سخت افزاری ازش هر موقع استفاده کرد ، ایشالا که نویسنده اصلیش حاجی Mike اش توش عیب و ایرادی نزاشته باشه
در ادامه میرسیم به بحث رجیسترها ( از 16 تا 64 بیتی) و دستورالعملهای ویژه cpu و…
—
خب در ساختار استاندارد پردازنده های x86 ما همیشه یک In و out داریم ، یعنی چی؟ یعنی هم میتونیم دیتابگیریم هم بدیم. و بدین شکل با دیوایس ها ارتباط برقرار کنیم.
برای یک مثال عملی مثلا خواندن اطلاعات از کیبورد!
طبق جدول دسترسی به کیبورد از نوع ps2درآدرس 0x60 هست
خب در این دیواس که اختصاصا رجیستر کنترلیش در آدرس 0x64 هست ( پس این جدوله چی گفته این مکان ماله موس هست ؟)
اگر بیت اول این مکان 1 باشه یعنی دستگاه در حالت INput هست و دیتا اومده توی بافرش
WaitLoop: in al, 64h ; Get status register value and al, 10b ; Test bit 1 of status register jz WaitLoop ; If status register bit not set, no data is in buffer in al, 60h ; Its set--Get the byte from the buffer (Port 0x60), and store it
کد بالا را یک نمونه اولیه از درایورنویسی برای سخت افزار ببینید (خیلی تخمیه نه؟ )
نمونه بالا یک کد خواندن مستقیم از سخت افزار هست ، روش نوشتن هم همینطوره ، یعنی آدرس را میدیم به باس و اطلاعات را میزاریم و مینویسیم روش ( دستور out )
به قول خارجیا این دیگه آخرشه direct hardware access
نکته کنکوری : به بحث اینتراپتها هم میرسیم
ولی در مجموع این Port mapping و Port I/O را خوب به خاطر بسپارید که خیلی کار باهاش در مد محافظت شده سخت افزاری باهاشون داریم.
اما خود Processor
خود میکروپروسسور هم یکسری دستورات خاص داره ، در خانواده 8086 ها جدول زیر داشته باشید، بعدا هرکدوم را به موقع توضیح میدم
اجرای هرکدوم از این دستورات در پایه ترین سطح میکروپروسسور رخ میده و اگه برنامه ای اشتباهی در فراخوانی این دستورات داشته باشه، فاتحه سیستم خونده میشه و کرش میکنه
و 80×86 Registers
هر میکروپروسسوری یکسری ثبات داخلی داره که همه کارها و محاسبات ، فلگ ها و ووو به واسطه این ثباتها قابل دسترسی هست.
RAX (EAX(AX/AH/AL)), RBX (EBX(BX/BH/BL)), RCX (ECX(CX/CH/CL)), RDX (EDX(DX/DH/DL)), CS,SS,ES,DS,FS,GS, RSI (ESI (SI)), RDI (EDI (DI)), RBP (EBP (BP)). RSP (ESP (SP)), RIP (EIP (IP)), RFLAGS (EFLAGS (FLAGS)), DR0, DR1, DR2, DR3, DR4, DR5, DR6, DR7, TR1, TR2, TR3, TR4, TR5, TR6, TR7, CR0, CR1, CR2, CR3, CR4, CR8, ST, mm0, mm1, mm2, mm3, mm4, mm5, mm6, mm7, xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7, GDTR, LDTR, IDTR, MSR, and TR.
* EAX - Accumlator Register. Primary purpose: Math calculations * EBX - Base Address Register. Primary purpose: Indirectly access memory through a base address. * ECX - Counter Register. Primary purpose: Use in counting and looping. * EDX - Data Register. Primary purpose: um... store data. Yep, thats about it :)
+--- AH ----+--- AL ---+ | | | +-------------------------------------------------------------+ | | | | +-------------------------------------------------------------+ | | | | +--------EAX lower 32 bits--------------| | | |------------------ RAX Complete 64 bits----------------------|
خدا رحم کنه سیستم عاملهای 256 بیتی را…
شوخی کردم ، پخی نیستن ، ما در ادامه این آموزش فقط روی سیستم عامل 32 بیتی کار میکنیم ، ولی این 64 هم چیز خفنی نیست. همه شون مثل همن
یه مقدار مجبورم قبل از مراحل عملی کمی جفنگیات بنویسم ، تحمل کنید ، چاره ای نیست
خب بعد از رجیسترهای عمومی ، نوبت رجیسترهای سگمنت میشه
* CS - Segment address of code segment * DS - Segment address of data segment * ES - Segment address of extra segment * SS - Segment address of stack segment * FS - Far Segment address * GS - General Purpose Register
segment:offset
The x86 uses several registers that help when access memory. * SI - Source Index * DI - Destination Index * BP - Base Pointer * SP - Stack Pointer
+---------- EFLAGS (32 Bits) ----+ | | |-- FLAGS (16 bits)- | | | | ==================================================================== < Register Bits | | +------------------------- RFLAGS (64 Bits) -----------------------+ | | Bit 0 Bit 63
کلا کاربرد زیادی دارند این رجیسترهای وضعیت ( Flag Register )
جدل ضمیمه نوع و خاصیت هر کدوم از بیتهای وضعیت را مشخص میکنه
توی جدول بالا یه توضیحی در مورد IOPL بدم IO Privilege Level (IOPL
وقتی این مقدار 0 یا 1 باشه ، فقط به هسته سیستم عامل اجازه داده میشه از دستوراتی مثل CLI – STI -IN – OUT استفاده بشه ، در غیر دیگر برنامه ها دسترسی به این توابع پایه ندارند و cpu خطای General Protection Fault (GPF را صادر میکنه
Test Registers : رجیسترهایی وجود دارند که کارشون فقط تست کردن هست
و اکثرا هیچ داکیومنت و توضیحاتی براشون منتشر نشده ( TR4,TR5,TR6,TR7. )
TR6 برای چک کردن اجرا شدن فرامین هست
TR7 برای ثبت داده هاست ( data register )
این رجیسترها فقط در Ring0 قابل دسترسی هستند( فقط با دستور MOV ) و در خارج از این رینگ منتجر به خطای GPF میشه General Protection Fault ( ترجمه تخمی داره ، گس مخافظت شده عمومی )
Debug Registers رجیستر های اشکال زدائی که شامل رجیستر های زیر است :
DR0 ، DR1 ، DR2 ، DR3 ، DR4 ، DR5 ، DR6 ، DR7
فقط در رینگ 0 و ( فقط با دستور MOV ) قابل دسترسی هستند
Beakpoint Registers :رجیسترهای نقطه توقف
DR0, DR1, DR2, DR3
شروط بیشتر در رجیستر DR7 که یک رجیستر 32 بیتی هست تعریف شده که بهشون میگن
Debug Control Register
حال ترجمه شا ندارم :
DR7 is a 32 bit register that uses a bit pattern to identify the current debugging task. Here it is: * Bit 0...7 - Enable the four debug registers (See below) * Bit 8...14 - ? * Bit 15...23 - When the breakpoints will trigger. Each 2 bits represents a single Debug Register. This can be one of the following: o 00 - Break on execution o 01 - Break on data write o 10 - Break on IO read or write. No hardware currently supports this. o 11 - Break on data read or write * Bit 24...31 - Defines how large of memory to watch. Each 2 bits represents a single Debug Register. This can be one of the following: o 00 - One bytes o 01 - Two bytes o 10 - Eight bytes o 11 - Four bytes
In Bits 0...7 in the above list: * Bit 0: Enable local DR0 register * Bit 1: Enable global DR0 register * Bit 2: Enable local DR1 register * Bit 3: Enable global DR1 register * Bit 4: Enable local DR2 register * Bit 5: Enable global DR2 register * Bit 6: Enable local DR3 register * Bit 7: Enable global DR3 register
* RDMSR - Read from MSR * WRMSR - Write from MSR
البته این دستورات خاص هستند و اینتل ازشون به عنوان یک CPUID در ساختار x86 ها رسما استفاده کرده
البته این لیست همیشه در حال آپدیت است…. و خواهد بود.
اطلاعات تکمیلی از سایت اینتل در این مورد MST برای علاقه مندان با این جفنگیات:
http://developer.intel.com/Assets/PDF/manual/253669.pdf
system programming guid جالبی از اینتل به حجم 7 مگ حدودا.
یکی را میخوایم اینو بخونه و ترجمه کنه ! تا دقیقتر بهفهمین چیکارا میشه کرد.
روش استفاده هم اینطوریه مثلا ( برای خوندن )
; This reads from the IA32_SYSENTER_CS MSR mov cx, 0x174 ; Register 0x174: IA32_SYSENTER_CS rdmsr ; Read in the MSR ; Now EDX:EAX contains the lower and upper 32 bits of the 64 bit register
; This writes to the IA32_SYSENTER_CS MSR mov cx, 0x174 ; Register 0x174: IA32_SYSENTER_CS wrmsr ; Write EDX:EAX into the MSR
پیوست :
( باور کنید نصف این چیزایی که نوشتم را هنوز حتی ندیدم، ایشالا رفتیم جلوتر ، توی عمل شاید بهشون بر بخوریم و عملی تو سرومغز هم دیگه بزنیم )
پیوست2» فکر نکنید فقط من چرت و پرت میگم ؛ امروز خوندم که فرهنگستان لغت گفته به جای فیلتر بگیم : پالایه !!!
خب بریم سراغ Control Registers
این رجیستر برای ما خیلی مهمه ، چرا ؟
چون به ما اجازه میده رفتار CPU را بتونیم تغییر بدیم ، یعنی چی ؟
ترجمه شا بیخیال ولی اونائی که قرمز کردم را توجه کنید
CR0 Control Register
CR0 is the primary control register. It is 32 bits, which are defined as follows:
* Bit 0 (PE) : Puts the system into protected mode * Bit 1 (MP) : Monitor Coprocessor Flag This controls the operation of the WAIT instruction. * Bit 2 (EM) : Emulate Flag. When set, coprocessor instructions will generate an exception * Bit 3 (TS) : Task Switched Flag This will be set when the processor switches to another task. * Bit 4 (ET) : ExtensionType Flag. This tells us what type of coprocesor is installed. o 0 - 80287 is installed o 1 - 80387 is installed. * Bit 5 (NE): Numeric Error o 0 - Enable standard error reporting o 1 - Enable internal x87 FPU error reporting * Bits 6-15 : Unused * Bit 16 (WP): Write Protect * Bit 17: Unused * Bit 18 (AM): Alignment Mask o 0 - Alignment Check Disable o 1 - Alignment Check Enabled (Also requires AC flag set in EFLAGS and ring 3) * Bits 19-28: Unused * Bit 29 (NW): Not Write-Through * Bit 30 (CD): Cache Disable * Bit 31 (PG) : Enables Memory Paging. o 0 - Disable o 1 - Enabled and use CR3 register
mov ax, cr0 ; get value in CR0 or ax, 1 ; set bit 0--enter protected mode mov cr0, ax ; Bit 0 now set, we are in 32 bit mode!
CR1 Control Register Reserved by Intel, do not use. CR2 Control Register Page Fault Linear Address. If a Page Fault Exception accures, CR2 contains the address that was attempted accessed CR3 Control Register Used when the PG bit in CR0 is set. Last 20 bits Contains the Page Directory Base Register (PDBR) CR4 Control Register Used in protected mode to control operations, such as v8086 mode, enabling I/O breakpoints, Page size extension and machine check exceptions.
* Bit 0 (VME) : Enables Virtual 8086 Mode Extensions * Bit 1 (PVI) : Enables Protected Mode Virtual Interrupts * Bit 2 (TSD) : Time Stamp Enable o 0 - RDTSC instruction can be used in any privilege level o 1 - RDTSC instruction can only be used in ring 0 * Bit 3 (DE) : Enable Debugging Extensions * Bit 4 (PSE) : Page Size Extension o 0 - Page size is 4KB o 1 - Page size is 4MB. With PAE, it is 2MB. * Bit 5 (PAE) : Physical Address Extension * Bits 6 (MCE) : Machine Check Exception * Bits 7 (PGE) : Page Global Enable * Bits 8 (PCE) : Performance Monitoring Counter Enable o 0 - RDPMC instruction can be used in any privilege level o 1 - RDPMC instruction can only be used in ring 0 * Bits 9 (OSFXSR) : OS Support for FXSAVE and FXSTOR instructions (SSE) * Bits 10 (OSXMMEXCPT) : OS Support for unmasked SIMD FPU exceptions * Bits 11-12 : Unused * Bits 13 (VMXE) : VMX Enable
و
CR8 Control Register Provides Read and Write access to the Task Prority Register (TPR)
* GDTR - Global De******or Table Register * IDTR - Interrupt De******or Table Register * GDTR - Local De******or Table Register * TR - Task Register
در آینده ای نزدیک در محیط PMODE که قرار خبرمون سیستم عامل را توش پیاده سازی کنیم ، کلی به کار میاد…
ولی خدائیش اینcpu خیلی خودش تخمیه! قیافه شو نگاه کنید ، خواه نا خواه کار کردن باهاش نیاز به دونستن خیلی خیلی چرت و پرت داره خب
ریخت و قیافه و ذات کثیف یک پنتیوم 3 به صورت لخت شده و کاملا سِکسی :
من یک بلاک متن هستم، روی دکمه ویرایش کلیک کنید تا این متن را تغییر دهید. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Ut elit tellus, luctus nec ullamcorper mattis, pulvinar dapibus leo.
* L2: Level 2 Cache * CLK: Clock * PIC: Programmable Interrupt Controller * EBL: Front Bus Logic * BBL: Back Bus Logic * IEU: Integer Execution Unit * FEU: Floating Point Execution Unit * MOB: Memory Order Buffer * MIU / MMU: Memory Interface Unit / Memory Management Unit * DCU: Data Cach Unit * IFU: Instruction Fetch Unit * ID: Instruction Decoder * ROB: Re-Order Buffer * MS: Microinstruction Sequencer * BTB: Branch Target Buffer * BAC: Branch Allocater Buffer * RAT: Register Alias Table * SIMD: Packed floating point * DTLB: Data TLB * RS: Reservation Station * PMH: Page Miss Handler * PFU: Pre-Fetch Unit * TAP: Test Access Port
بخش نهم :
یه توضیح کوچولو و دوباره و بعد ورود به دنیای برنامه نویسی هسته سیستم عامل 32 بیتی
Protect Mode در این مد شما به عنوان برنامه نویس به هیچ چیز آماده ای از قبل دسترسی ندارید
نه اینتراپت ، نه سیستم call ها ، نه کتابخانه های استاندارد Lib برنامه نویسی ، نه هیچی ، و یک اشتباه کافیه که سیستم کرش کنه. پس حواس باید جمع باشه
برای ادامه GDT را هم باید وسطشو باز کنیم نیگا کنیم
خب پس شروع میکنیم:
1 – با حالت تئوری مد محافظت شده که اینهمه توضیح تاحالا دادم که حتما آشنائین PM
2- آدرس دهی در حالت PM
3- ورود به محیط PM
4- جدول عمومی GDT- Global De******or Table – نمیدونم به چه اسمی ترجمش کنم … در ادامه یه اسمی بلاخره براش میسازیم
خب براش شروع خودمون یک کتابخانه میسازیم STDIO را حتما برنامه نویسهای سی باهاش آشنائی کامل دارند ، پس از یک اسم این مدلی برای شروع استفاده میکنیم ( عشقی)
اسم فایل را میزاریم stdio.inc
خلاصه اینکه این فایل را با فایل stdio.lib خود سی قاطی نکنید
( فعلا فقط نگاهش کنید ، کاربردش را در آینده ای نزدیک به صورت کد اجرائی توضیح میدم )
;************************************************* ; stdio.inc ; -Input/Output routines ; ; OS Development Series ;************************************************* %ifndef __STDIO_INC_67343546FDCC56AAB872_INCLUDED__ %define __STDIO_INC_67343546FDCC56AAB872_INCLUDED__ ;************************************************; ; Puts16 () ; -Prints a null terminated string ; DS=>SI: 0 terminated string ;************************************************; bits 16 Puts16: pusha ; save registers .Loop1: lodsb ; load next byte from string from SI to AL or al, al ; Does AL=0? jz Puts16Done ; Yep, null terminator found-bail out mov ah, 0eh ; Nope-Print the character int 10h ; invoke BIOS jmp .Loop1 ; Repeat until null terminator found Puts16Done: popa ; restore registers ret ; we are done, so return %endif ;__STDIO_INC_67343546FDCC56AAB872_INCLUDED__
.inc هم که حتما میدونید مخفف include هست ، یک استاندارد تعریف پسوند هست که برنامه نویس بدونه این فایل قراره اینکلود بشه توی برنامه اصلی.
اون %ifndef های اول هم برای اینه که دوباردوبار برنامه نیاد توی دل برنامه اصلی قرار بگیره
نکته کنکوری که توی این سورس میبینید pusha و popA هست . میبینید که هیچ تابعی نیست که چیزی را برگردونه
در اول کار کلیه رجیسترها ذخیره میشن و در انتها هم دوباره بازیابی میشن.
نه خانی اومده ، نه خانی رفته.
خب اگه یادتون بیاد ما محدودیت شدید 512 بایت را برای بوت لودر داشتیم ، پس بهتره تمام موارد غیر ضروری را ازش حذف کنیم و همه را منتقل کنیم به برنامه دومی ( اسمشو میزاریم KRNLDR که مخفف عنوان Kernel loader هست )
این کرنل لودر یا مرحله 2 کارش چیه؟:
1-فعال کردن و رفتن به حالت محافظت شده
2-بازیابی اطلاعات موجود در بایوس برای ادامه فعالیت و داشتن آدرس سخت افزارها و …
3-لود کردن و اجرای کرنل اصلی.
( میتونه اینجا لود کردن حالتی مثل safe mode ویندوز هم باشه ، نوعی هسته خالص تر شده)
( یا میتونه اینجا از حالت مولتی بوت استفاده بشه و چندتا سیستم عامل را بوت کنه ( مثل بوت کامندرها)
4-فعال کردن 20th برای دسترسی به 4گیگ حافظه ( یادم نیست توضیحش را دادم قبلا یا نه ،-فعلا دارم تایپ میکنم حال رفتن و دیدن ندارم ؛ به حد کافی حواسم پرت هست )
5- فراهم کردن هندل برای وقفه های پایه.
6- و کارهای دیگه…
و خلاصه یک محیطی آماده میکنه که بتونیم از اینجا به بعد برنامه های نوشته شده سطح بالاتر را بتونیم اجرا کنیم. ( نیت ندارید که کل هسته سیستم عامل را با اسمبلی بنویسید )
خب از حالا به بعد این Protect Mode را بهش میگن حالت حافظت از حافظه. یعنی چی؟
یعنی تمام آدرس دهی ها را فرض میگیرم در حافظه وقتی برای اولین بار تهیه میشن تا آخر کسی بهشون دست نمیزاره و محافظت شده هستند در برابر تغییرات.
البته مکانهایی خاص از حافظه مد نظر هست ، نه همه حافظه. ( توضیح میدم )
در پروسسور های 80×86 ما میایم یک نقشه از حافظه تهیه میکنیم Memory Map که بیسش همون جدول GDT میشه و پروسسور یک General Protection Fault (GPF) میسازه برای پردازش خطاهای رخ داده در این جدول تا بتونه به وقفه های رخ داده شده رسیدگی کنه ( و الا پروسسور گوگیجه بگیره انگار که همون معنی کرش کردن براش اجرا شده باشه )
De******or Tables
ما یکسری جداول توصیفی داریم ، که هرکدوم توش مقادیری قرار میگیرند ، این مقادیر پایه و بنیان دسترسی low level ارتباط و تعامل با حافظه سیستم جهت پیاده سازی و اجرای سیستم عامل هست .
Global De******or Table (GDT),
Local De******or Table (LDT)
Interrupt De******or Table (IDT)
که آدرسهای پایه ای رجیسترهای ذخیره شده تحت عنوان
GDTR, LDTR, and IDTR
شناسائی میشن
توجه کنید که در صورت هرگونه درخواست دسترسی ring3 به این جداول که در ring0 ساخته میشوند ، خطای سیستمی برمیگرده ( cpu فحشتون میده )
اصلی ترین جدولی که باهاش کار داریم GDT هست که حالا جزئیاتش را میگم این جدول هم در بوت لودر باهاش کار داریم هم در کرنل.
کارش چیه ؟ یک نقشه کلی از مموری در اختیار طراح سیستم عامل قرار میده … دقیقا فکرکنید نقشه راههای یک کشور چقدر کار میکنه ؟ در این حد
این جدول شامل سه بخش هست که اول Null descreptor بهش میگن و همه مقادیرش 00 هست ، دو بخش بعدی Code De******or و Data De******or هستند.
این جدول یک جدول 8 بایتی هست که هر بیت اون یه خاکی تو سرش میکنه ( ماشالا )
به ترتیب ارزش بیتها از آخر به اول مینویسم ( ترجمه مطالب این ریختی را ببرین دارلترجمه )
* Bits 56-63: Bits 24-32 of the base address
* Bit 55: Granularity
o 0: None
o 1: Limit gets multiplied by 4K
* Bit 54: Segment type
o 0: 16 bit
o 1: 32 bit
* Bit 53: Reserved-Should be zero
* Bits 52: Reserved for OS use
* Bits 48-51: Bits 16-19 of the segment limit
* Bit 47 Segment is in memory (Used with Virtual Memory)
* Bits 45-46: De******or Privilege Level
o 0: (Ring 0) Highest
o 3: (Ring 3) Lowest
* Bit 44: De******or Bit
o 0: System De******or
o 1: Code or Data De******or
* Bits 41-43: De******or Type
o Bit 43: Executable segment
+ 0: Data Segment
+ 1: Code Segment
o Bit 42: Expansion direction (Data segments), conforming (Code Segments)
o Bit 41: Readable and Writable
+ 0: Read only (Data Segments); Execute only (Code Segments)
+ 1: Read and write (Data Segments); Read and Execute (Code Segments)
* Bit 40: Access bit (Used with Virtual Memory)
* Bits 16-39: Bits 0-23 of the Base Address
* Bits 0-15: Bits 0-15 of the Segment Limit
; This is the beginning of the GDT. Because of this, its offset is 0.
; null de******or
dd 0 ; null de******or–just fill 8 bytes with zero
dd 0
; Notice that each de******or is exactally 8 bytes in size. THIS IS IMPORTANT.
; Because of this, the code de******or has offset 0x8.
; code de******or: ; code de******or. Right after null de******or
dw 0FFFFh ; limit low
dw 0 ; base low
db 0 ; base middle
db 10011010b ; access
db 11001111b ; granularity
db 0 ; base high
; Because each de******or is 8 bytes in size, the Data descritpor is at offset 0x10 from
; the beginning of the GDT, or 16 (decimal) bytes from start.
; data de******or: ; data de******or
dw 0FFFFh ; limit low (Same as code)
dw 0 ; base low
db 0 ; base middle
db 10010010b ; access
db 11001111b ; granularity
db 0 ; base high
; code de******or: ; code de******or. Right after null de******or
dw 0FFFFh ; limit low
dw 0 ; base low
db 0 ; base middle
db 10011010b ; access
db 11001111b ; granularity
db 0 ; base high
11111111 11111111 00000000 00000000 00000000 10011010 11001111 00000000
db 10011010b ; access
# Bit 0 (Bit 40 in GDT): Access bit (Used with Virtual Memory). Because we don't use virtual memory (Yet, anyway), we will ignore it. Hence, it is 0
# Bit 1 (Bit 41 in GDT): is the readable/writable bit. Its set (for code selector), so we can read and execute data in the segment (From 0x0 through 0xFFFF) as code
# Bit 2 (Bit 42 in GDT): is the "expansion direction" bit. We will look more at this later. For now, ignore it.
# Bit 3 (Bit 43 in GDT): tells the processor this is a code or data de******or. (It is set, so we have a code de******or)
# Bit 4 (Bit 44 in GDT): Represents this as a "system" or "code/data" de******or. This is a code selector, so the bit is set to 1.
# Bits 5-6 (Bits 45-46 in GDT): is the privilege level (i.e., Ring 0 or Ring 3). We are in ring 0, so both bits are 0.
# Bit 7 (Bit 47 in GDT): Used to indicate the segment is in memory (Used with virtual memory). Set to zero for now, since we are not using virtual memory yet
بیتهای دسترسی شامل موارد زیر هستند:
بیت 0 این موضوع که همان بیت 40 جدول GDT هست مشخص میکنه که از حافظه مجازی Virtual memory استفاده بکنیم یا نه ( ما میگیم نه و مقدارش را 0 میزاریم )
بیت 1 یا همان بیت 41 جدول GDT : این بیت به ما اجازه خواندن و نوشتن از این مکان حافظه ( یعنی 0 تا ffff را میدهد)
بیت 2 یا همان بیت 42 جدول GDT : این بیت به expansion direction معروف هست و ما کاری فعلا باهاش نداریم ( یه جونمرد پیدا نشد بره این دیتاشیت اینتل را ترجمه کنه؟)
بیت 3 یا همان بیت 43 جدول GDT : به سی پی یو میگه این تکه کد اجرائی یا دیتا هست. که البته در مورد ما Code هست نه Data
بیت 4 یا همان بیت44 جدول GDT :این یک کد سلکتور هست که میزاریمش همیشه 1 باشه
بیت 5 و 6 : رینگ کاربردی را مشخص میکنه که ما چون رینگ 0 هستیم همیشه این مقدار هر دو بیت 0 هست.
بیت 7 : برای نشان دادن قطعه ای در حافظه مجازی هست که چون ما فعلا دنبال این بحث حافظه مجازی نیستیم براش یابو اب میدیم و مقدراش را میزاریم 0
این بایت دسترسی access byte مهم هستند چون وقتی نزدیک میشیم به رینگ 3 یا برنامه های کاربردی باید حواسمون بهش باشه.
خب از اینجا به بعدش یه کم توضیحاتش پیچیده میشه و مخ من امشب گیجه ، یه چند روز دیگه ایشالا ریست شدم ادامه این بحث را مینویسم.
میدونم همه دوست دارین فرتی بریم سراغ اصل سیستم عامل ، ولی باور کنید اگه اصول را حتی همینقدر دست و پا شکسته ندونید ؛ هیچ فرقی بین نوشتن و استفاده یک سیستم عامل مستقل با استفاده از یک سیستم عامل ویندوز معمولی در آینده نخواهید داشت. پس تحمل کنید
در ادامه داستان ، بایت granularity را توضیح میدم ….)
دانلود کتاب سیستم عامل تانن باوم با فرمت PDF
به نقل از اینجا
اینم کتاب معروف سیستم عامل برای کسایی که می خوان بدونن یه سیستم عامل چه جوری کار می کنه و چه جوری پیاده سازی می شه
دانلود مستقیم
http://www.ALT.ir/os/Operating.Syste…rd.Edition.zip
حجم 5.5 مگ
بریم سراغ بایت بعدی یعنی granularity
db 11001111b ; granularity db 0 ; base high
# Bit 0-3 (Bits 48-51 in GDT): Represents bits 16-19 of the segment limit. So, lessee... 1111b is equal to 0xf. Remember that, in the first two bytes if this de******or, we set 0xffff as the first 15 bites. Grouping the low and high bits, it means we can access up to 0xFFFFF. Cool? It gets better... By enabling the 20th address line, we can access up to 4 GB of memory using this de******or. We will look closer at this later...
# Bit 4 (Bit 52 in GDT): Reserved for our OS's use--we could do whatever we want here. Its set to 0.
# Bit 5 (Bit 53 in GDT): Reserved for something. Future options, maybe? Who knows. Set to 0.
# Bit 6 (Bit 54 in GDT): is the segment type (16 or 32 bit). Lessee.... we want 32 bits, don't we? After all-we are building a 32 bit OS! So, yeah--Set to 1.
# Bit 7 (Bit 55 in GDT): Granularity. By setting to 1, each segment will be bounded by 4KB.
The last byte is bits 24-32 of the base (Starting) address–which, of course is 0.بیت 0 تا 3 کارش اینه که وقتی 1111 باشه ، به همراه استفاده از ادرس لاین 20 به ما این اجازه داده میشه که تا 4 گیگ حافظه را آدرس دهی کنیم. ( علت و عوامل رجوع شود به رفرنس cpu اینتل یا amd )
بیت 4 و 5 رزرو هستند و با 0 پر میشن
بیت 6 برای اینکه به محیط 32 بیتی دسترسی داشته باشیم باید 1 باشه
بیت 7 وقتی یک باشه ، قطعات حافظه( سگمنت ) را حداکثر 4 کیلوبایت تعیین میکنه
8 بیت بعدی که در واقع بیس آدرس ( آدرس شروع ) هستند هم برابر با 0 قرار داده میشن
( همش همینقدر تو این مدت نوشتم ، شرمنده ، ایشالا فسفر مغز بزاره زودتر تکمیلش میکنم)
این نوشته ها هم از دوست عزیز xman_1365_x در سایت برنامه نویس دیدم
گفتم اینجا بزارمش شاید به درد یکی که افتاده توی این خط اعتیاد سیستم عاملی خورد…
::
نقل قول: مقدمه ایی بر اسمبلی 64 بیتی
مرجع پست اول اینجاست
که ایبوک در ارتباط با abi هم داره که مفیده!
مقدمه ای در ارتباط با IA-64
x86 Instruction Set Architecture Comprehensive 32-64-bit Coverage
اینم هست که اگر عضو نیستین از گوگل پیدا کنید شاید بعدا خودم آپلودش کنم
http://book.pdfchm.net/32-64-bit-80x…9781598220025/
و
http://www.cs.cmu.edu/~fp/courses/15213-s07/misc/asm64-handout.pdf
ایبوک های مفید زیادی هم از سایت اینتل هست که میتونید بگیرید
اینم تولز اینتل
شاید ادامه داشته باشد ، شاید هم ادامه اش را شما بنویسید……….