ОС на ассемблере. Полуношный проэкт. часть 2.
Запись от JIuXOMAHT размещена 18.02.2016 в 13:50
Показов 2688
Комментарии 2
|
Прелюдия... Здравствуй, друг. Спустя время ты все еще веришь в меня... Благодарю тебя. В прошлый раз мы остановились на той мысли, что множество камней будут мешать нашему безумному сплаву по реке информации в тихий омут незаметного триумфа. Наследственная многозадачность теперь уже кажется недосягаемой мечтой, но мы дойдем, дойдем, я обещаю. Несмотря на все препоны со стороны этого неоптимизированного мира... И первый шаг мы сделаем прямо в следующий раз... В следующий раз... Постоянно он идет следующим, все время отдаляя тот долгожданный миг, когда вымысел становится явью. Эти мирские делишки постоянно всовывают свои немаскируемые прерывания в наш прекрасный дзэн. Прости, что я только трачу свое время. Но такова моя судьба, выбор которой я сделал несколько миллиардов тактов назад... А пока довольствуйся тем, что написано немножко ниже... Давай погрустим немножко вдвоем. Пусть это будет немножко меланхоличная ОС... Итак, наследственная многозадачность. Каждая следующая программа принимает себе в обязанность исполнять основные функции предыдущей, при этом управление, расчет и распределение ресурсов между задачами находится всецело на ее плечах. Таким образом, сразу возникают первое, второе и третье правила написания кода программ для нашей ОС: 1)Код должен содержать минимизированный листинг всех функций и констант, которые будут впоследствии наследованы следующей программой; 2) Код должен содержать в себе особую выделенную зону для вставки в нее этих кусков кода, наследуемых у предыдущей программы; 3) Код должен содержать собственный диспетчер ресурсов, который распределяет ресурсы между основной и наследованными задачами. Как ты уже понял, прямого переключения между приложениями не будет. Каждая запускаемая программа будет заново ассемблироваться с наследственными включениями. Соответственно, ОС должна будет иметь собственный компилятор FASM. А программы будут храниться в виде текстовых файликов. Для защиты их содержимого можно изменить их тип на новый, системный, допустим, *_sm, и запретить внесение туда изменений. Мало того, еще и каталог с разрешенными *_sm , будет пополняться только с "кивка" нашего компилятора. Это позволит нам впоследствии написать приложение для написания программ и "дописывания" ОС в реальном времени, не прибегая к услугам эмуляторов... Но это все пока мечты... мечты... жалкие, бесформенные мечты... Которые беспомощно бьются о подводные камни. Например, включение линии A20 перед запуском защищенного режима... Допустим, включить три следующие строчки не так сложно: in al, 92h or al, 02h; здесь может понадобится время ожидания out 92h, al Однако, по словам самой Интел, уже поддержка этого рудимента ошибок 286-го относительно спорна на ядре Nehalem, а на ядре Haswell и более поздних так вовсе отсутствует... Ну этот так, к сведению. Или вот еще... Адресная бюрократия. Так называемый "защищенный режим"... Как было прекрасно в реальном... Куда хочешь смотришь, указывай только адрес... Нет, теперь его глобальная таблица дескрипторов, со всеми его дополнительными атрибутами привилегий и прочего флуда, который ничего, кроме постоянного ужирательства процессорного времени на постоянную работу с памятью, пусть и в кэше, но тратит миллионы тактов; ничего и не защищает. Это такая защита от дурака. Типа если разбирается - у него будет работать программа, а ежели нет, то и фиг с ним. Для тех, кто еще не в тарелке: между обращением программы и памятью поставили дополнительный набор простыней - Глобальная таблица дескрипторов GDR, локальная таблица дескрипторов LDR, и для страничной памяти (paging)- страница памяти (адресует 4 кб данных), таблица страниц памяти (адресует 4 мб данных), каталог таблиц страниц памяти (адресует 4 гб данных). Все их системным программистам приходится писать самим. Конечно, контекст многих задач настолько непредсказуем, что без динамического прописывания селекторов (указателей на дескриптор в таблице, который в свою очередь является прописанным указателем на сегмент или страницу в памяти, этакий посредник) не обойтись. Однако внесение изменений в таблицу глобальную создает опасность для системы остаться у разбитого корытца, ибо правило гласит : сначала таблица, потом исполнение, иначе #GP. Поэтому и существуют локальные таблицы дескрипторов, которые можно изменять уже поспокойнее, не забыв прописать в таблице прерываний на передачу обработки прерывания программы обработчиком в составе текущего приложения. Для ассемблерщика это яд, пустая трата времени, но мы должны этот путь пройти, ибо как гласит AMD64: сначала реальный, за ним защищенный, затем длинный и уж только потом 64-й. Вот! Там же лежит небольшой кусок кода экстренного перехода из реального в 64-разрядный режим (оригинальные комментарии оставлены как есть): mydata segment para pGDT32 label fword ; Used by LGDT. dw gdt32_limit ; GDT limit ... dd gdt32_base ; and 32-bit GDT base pIDT32 label fword ; Used by LIDT. dw idt32_limit ; IDT limit ... dd idt32_base ; and 32-bit IDT base pGDT64 label tbyte ; Used by LGDT. dw gdt64_limit ; GDT limit ... dq gdt64_base ; and 64-bit GDT base pIDT64 label tbyte ; Used by LIDT. dw idt64_limit ; IDT limit ... dq idt64_base ; and 64-bit IDT base mydata ends ; end of data segment code16 segment para use16 ; 16-bit code segment ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;; ; 16-bit code, real mode ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;; ; ; Initialize DS to point to the data segment containing ; pGDT32 and PIDT32. Set up a real-mode stack pointer, SS:SP, ; in case of interrupts and exceptions. ; cli; установка флага, запрещающего внешние прерывания mov ax, seg mydata; запись в общий 16-й регистр указателя сегмента метки "mydata" mov ds, ax; запись в 16-й регистр сегмента данных mov ax, seg mystack; запись в общий 16-й регистр указателя сегмента метки "mystack" mov ss, ax; запись в 16-й регистр сегмента стека. mov sp, esp0; запись в 16-й регистр указателя стека нового указателя стека с CPL=0 ; ; Use CPUID to determine if the processor supports long mode. ; mov eax, 80000000h ; Extended-function 8000000h. cpuid ; Is largest extended function cmp eax, 80000000h ; any function > 80000000h? jbe no_long_mode ; If not, no long mode. mov eax, 80000001h ; Extended-function 8000001h. cpuid ; Now EDX = extended-features flags. bt edx, 29 ; Test if long mode is supported. jnc no_long_mode ; Exit if not supported. ; ; Load the 32-bit GDT before entering protected mode. ; This GDT must contain, at a minimum, the following ; descriptors: ; 1) a CPL=0 16-bit code descriptor for this code segment. ; 2) a CPL=0 32/64-bit code descriptor for the 64-bit code. ; 3) a CPL=0 read/write data segment, usable as a stack ; (referenced by SS). ; ; Load the 32-bit IDT, in case any interrupts or exceptions ; occur after entering protected mode, but before enabling ; long mode). ; ; Initialize the GDTR and IDTR to point to the temporary ; 32-bit GDT and IDT, respectively. ; lgdt ds:[pGDT32]; загрузка 32-й временной глобальной таблицы дескрипторов lidt ds:[pIDT32]; загрузка 32-й временной таблицы прерываний ; ; Enable protected mode (CR0.PE=1). mov eax, 000000011h; запись в общий 32-й регистр особого значения mov cr0, eax; запись в 32-й регистр управления значения общего 32-го регистра ; ; Execute a far jump to turn protected mode on. ; code16_sel must point to the previously-established 16-bit ; code descriptor located in the GDT (for the code currently ; being executed). ; db 0eah ;Far jump... Этот дальний прыг ну просто необходим, дабы сбросить конвеер, на котором dw offset now_in_prot;to offset... уже сидят следующие команды, организованные по старым правилам dw code16_sel ;in current code segment. реального режима. А нам нужен защищенный режим. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;; ; At this point we are in 16-bit protected mode, but long ; mode is still disabled. ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;; now_in_prot: ; ; Set up the protected-mode stack pointer, SS:ESP. ; Stack_sel must point to the previously-established stack ; descriptor (read/write data segment), located in the GDT. ; Skip setting DS/ES/FS/GS, because we are jumping right to ; 64-bit code. ; mov ax, stack_sel mov ss, ax; ну вот мы уже загружаем новые, временные, правда, но правила mov esp, esp0 ; ; Enable the 64-bit page-translation-table entries by ; setting CR4.PAE=1 (this is _required_ before activating ; long mode). Paging is not enabled until after long mode ; is enabled. ; mov eax, cr4; загрузка содержимого 32-го регистра управления в общий 32-й регистр bts eax, 5; установка 5 бита в состояние "1" mov cr4, eax; загрузка содержимого общего 32-го регистра в 32-й регистр управления ; ; Create the long-mode page tables, and initialize the ; 64-bit CR3 (page-table base address) to point to the base ; of the PML4 page table. The PML4 page table must be located ; below 4 Gbytes because only 32 bits of CR3 are loaded when ; the processor is not in 64-bit mode. ; mov eax, pml4_base ; Pointer to PML4 table (<4GB). Грузим указатель на нашу долгожданную 64-ю mov cr3, eax ; Initialize CR3 with PML4 base. таблицу. ; ; Enable long mode (set EFER.LME=1). ; mov ecx, 0c0000080h ; EFER MSR number. Это особый дополнительный регистр в AMD64, который rdmsr ; Read EFER. отвечает в том числе за работу длинного режима. Первые две bts eax, 8 ; Set LME=1. строчки его считывают, вторые - переписывают и возвращают. wrmsr ; Write EFER. 0c0000080h - это его особый адрес. ; ; Enable paging to activate long mode (set CR0.PG=1) ; mov eax, cr0 ; Read CR0. Уже второй раз обращаемся в регистр управления, устанавливаем бит "1" и bts eax, 31 ; Set PE=1. возвращаем на место. mov cr0, eax ; Write CR0. ; ; At this point, we are in 16-bit compatibility mode ; ( LMA=1, CS.L=0, CS.D=0 ). ; Now, jump to the 64-bit code segment. The offset must be ; equal to the linear address of the 64-bit entry point, ; because 64-bit code is in an unsegmented address space. ; The selector points to the 32/64-bit code selector in the ; current GDT. ; db 066h; Вот и мы уже изменяем размер операндов с 16-х на 32-е db 0eah; Вот и мы опять прыгаем далеко со сбросом конвеера dd start64_linear; Прыгаем на это смещение. dw code64_sel; В этот чудесный сегмент. code16ends ; End of the 16-bit code segment ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;;; ;; ;;; Start of 64-bit code ;; ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;;;;;;;;;;;;;;;;;;;; code64 para use64 start64: ; At this point, we're using 64-bit code ; ; Point the 64-bit RSP register to the stack’s _linear_ ; address. There is no need to set SS here, because the SS ; register is not used in 64-bit mode. ; mov rsp, stack0_linear ; ; This LGDT is only needed if the long-mode GDT is to be ; located at a linear address above 4 Gbytes. If the long ; mode GDT is located at a 32-bit linear address, putting ; 64-bit descriptors in the GDT pointed to by [pGDT32] is ; just fine. pGDT64_linear is the _linear_ address of the ; 10-byte GDT pseudo-descriptor. ; ; The new GDT should have a valid CPL0 64-bit code segment ; descriptor at the entry-point corresponding to the current ; CS selector. Alternatively, a far transfer to a valid CPL0 ; 64-bit code segment descriptor in the new GDT must be done ; before enabling interrupts. ; lgdt [pGDT64_linear] ; ; Load the 64-bit IDT. This is _required_, because the 64-bit ; IDT uses 64-bit interrupt descriptors, while the 32-bit ; IDT used 32-bit interrupt descriptors. pIDT64_linear is ; the _linear_ address of the 10-byte IDT pseudo-descriptor. ; lidt [pIDT64_linear] ; ; Set the current TSS. tss_sel should point to a 64-bit TSS ; descriptor in the current GDT. The TSS is used for ; inner-level stack pointers and the IO bit-map. ; mov ax, tss_sel ltr ax ; ; Set the current LDT. ldt_sel should point to a 64-bit LDT ; descriptor in the current GDT. ; mov ax, ldt_sel lldt ax ; ; Using fs: and gs: prefixes on memory accesses still uses ; the 32-bit fs.base and gs.base. Reload these 2 registers ; before using the fs: and gs: prefixes. FS and GS can be ; loaded from the GDT using a normal “mov fs,foo” type ; instructions, which loads a 32-bit base into FS or GS. ; Alternatively, use WRMSR to assign 64-bit base values to ; MSR_FS_base or MSR_GS_base. ; mov ecx, MSR_FS_base mov eax, FsbaseLow mov edx, FsbaseHi wrmsr ; ; Reload CR3 if long-mode page tables are to be located above ; 4 Gbytes. Because the original CR3 load was done in 32-bit ; legacy mode, it could only load 32 bits into CR3. Thus, the ; current page tables are located in the lower 4 Gbytes of ; physical memory. This MOV to CR3 is only needed if the ; actual long-mode page tables should be located at a linear ; address above 4 Gbytes. ; mov rax, final_pml4_base ; Point to PML4 mov cr3, rax ; Load 64-bit CR3 ; ; Enable interrupts. ; sti ; Enabled INTR снятие флага, запрещающего внешние прерывания <insert 64-bit code here> Часть вопросов так и осталась просто вопросами, особенно та часть, что относится к 64-ому коду. Разработчики особо не старались создавать готовый код, снабдив некоторые места внятными метками. Пока мы не научимся их писать, дальнейшее продвижение маловероятно. Обратите внимание на оператор db и seg. Оператор в отличие от команд не является указанием для процессора, это указание ассемблеру, поэтому счетчик команд никак не реагирует на него, кроме одного случая: если после оператора db стоит опкод какой-либо команды. Да и вообще, когда читаешь описание той или иной хрени, обязательно нарвешься на какого-то толкиенутого с его "резервированием местом в памяти", вместо "ручная запись в итоговый код следующей байтной или битной последовательности определенной длины". Для чего нужна эта ручная запись? Ну так дело все в мнемониках: на деле одной мнемонике может соответствовать от одного до нескольких опкодов с некоторыми исполнительными различиями. В большинстве случаев, ассемблер верно трактует контекст и выставляет нужный опкод в нужном месте, однако в критичных местах лучше перестраховаться и вместо JMP Segment:Smeschenie записать db 0eah; dd Smeschenie; dw Segment; Оператор seg возвращает значения сегмента памяти, в котором находится указанная величина... Почитай про операторы, очень интересно. Пока все, мой друг. Обещаю не бросать тебя на очень долго. До скорого... [продолжение следует] |
Размещено в Без категории
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Всего комментариев 2
Комментарии
-
Есть же тег ASM.Запись от castaway размещена 10.03.2016 в 21:41
-
Запись от JIuXOMAHT размещена 10.03.2016 в 23:22


