s3dworld, В общем, содержимое памяти по адресу Х — то, что было прочитано согласно соответствующему протоколу. Если чипсету сказано перенаправлять обращения к F0000…FFFFF в оперативку, чип BIOS можно аккуратно достать. Не факт, кстати, что по этим адресам всё ещё живёт распакованный BIOS.
baldr А он там уже и не живет. Он давно уже наверх переехал в адреса 4 Гб-размер биоса s3dworld ОЗУ это ОЗУ и оно занимается только временным хранением данных, а вот процессор обращается к этому ОЗУ используя изрядно нашинкованное адресное пространство. При чем в кривой интелловской архитектуре пространство устройств PCI не может располагаться выше 4 Гб, поэтому все регистры и память устройств PCI проецируются на адресное пространство ближе к концу (4 Гб). Устройства PCI могут занимать огромный кусок адресного пространства и для памяти его может не хватить (только видео память, при достаточном ее количестве вырезает 0,5 Гб). Если для отображения всей памяти на адресное пространство не хватает 4 Гб, то вот оставшаяся память проецируется за 4ый Гб. На счет этого бита можешь не заморачиваться особо. Если все лежит в плоском адресном пространстве, то и стек и данные могут просто где-то находиться в одном и том же сегменте данных и этот бит не понадобится. Но вот если пользоваться механизмом сегментации для распределения памяти, то это понадобится для как раз таки сегмента стека.
Причём дотянуться до неё используя механизм сегментов нельзя - придётся включать страницы и подкачивать память из за границы 4Гб в доступное сегментам 4Гб адресное пространство (АП) и обратно, например, используя часть доступного АП как окно в эту "запредельную" память
А у меня такой вот вопрос, на основании того, что мне сказали что можно сначала переключиться в защищённый режим, а уже потом заполнять структуры: а можно ли переключиться в защищённый режим (то есть получу 32-бита адресации), но не заполнять никаких структур (то есть не использовать защиту)? Я имею в виду, чтобы работать так же как и в реальном режиме, только с большим диапазоном адресов. Понятное дело что была бы однозадачность.
s3dworld я же уже посоветовал тебе унреал в #11 - вторая ссылка с примером это как раз оно и есть - реальный режим с 4Гб диапазоном адресов.
s3dworld После переключения можно менять таблицы дескрипторов сколько душе угодно. Собственно, так и происходит на практике: переключаются с временными таблицами (лишь бы хватило на сегменты кода, данных и стека плюс на TSS). Хотя и этих временных вполне достаточно для доступа к всем 4 Гбайтам адресного пространства (ну а если нужно выше, то, как уже сказали, нужно включать страничный механизм и всё такое). Но переключиться в защищённый вообще без заполнения дескрипторов невозможно: откуда процессор возьмёт информацию для управления памятью-то?
Я тут решил для себя сделать справочник. Вот начал со структуры селектора сегмента. Она загружается в один из шести сегментных регистров (в зависимости что за сегмент нам нужен, хотя для длинного режима какие-то сегментные регистры игнорируются). Так вот, сам по себе селектор сегмента состоит из 16 бит. Так мне и интересно, а для чего вообще нужно поле RPL (Requestor Privilege Level)? То есть я конечно знаю что это, но зачем он нужен у селектора сегмента, если уровень привилегии я устанавливаю у дескриптора?
В общем я хочу добиться следующего: перейти в длинный режим. Перейти в длинный режим без перехода в защищённый режим нельзя. Но в длинном режиме нет страничной организации памяти, следовательно не нужно же мне заполнять глобальную таблицу дескрипторов или нужно? Я просто не знаю, можно ли тупо перейти без ничего в защищённый режим, чтобы от туда сразу же скакануть в длинный (помню только что нужен будет длинный прыжок). В общем что мне нужно будет заполнить? И если в длинном режиме нет сегментов, то как же там всё устроено? Как это через плоскую модель памяти осуществляется. Есть ли странички?
s3dworld минимум для перехода в защищенный режим Code (Text): sub sp, 12 mov bp, sp mov dword [bp+0], gdt_ptr mov word [bp+4], gdt_limit sgdt [bp+6] lgdt [bp+0] mov eax, cr0 test al, 1 jnz already_in_PM or al, 1 mov cr0, eax jmp code_sel:code_ptr ... code_ptr: ;если нужно mov ds, data_sel mov es, data_sel mov fs, data_sel mov gs, data_sel mov ss, stack_sel ...
Вообще-то там есть и GDT и страничная адресация. Причём страничная адресация обязательная, в отличии от простого PM. Для перехода тебе потребуется GDT также на 3 элемента - NULL, код и данные + полноценный каталог страниц. Только не 2 уровневый, а 4-х. Единственное отличие от обычного PM - у сегментов игнорируется база и лимит. То есть все сегменты имеют базу 0 и лимит 2 ^ 64 байт. Однако такие же сегменты тебе никто не запрещает описать и в простом PM, а при условии, что ещё нужна таблица страниц long mode сложнее PM. Без ничего нет и PM. Нужна таблица дескрипторов сегментнов. Как минимум. Можно перейти в long mode минуя 32-битный режим - сделать - просто вместо jmp CODE32_SELECTOR:start32 сделать jmp CODE64_SELECTOR:start64. Если у тебя нет нужды в 64-битной арифметике,только 2 ГБ ОЗУ и не нужна многозадачность, то зачем тебе сейчас вообще страничная адресация? Сделай 4 ГБ сегменты и тебе будет доступна вся память. Вот тебе даже код: Code (Text): lgdt [gdtr] cli mov eax, cr0 or eax, 1 mov cr0, eax jmp 8:start32 use32 start32: mov ax, 16 mov ds, ax mov es, ax mov fs, ax mov gs, ax mov ss, ax mov esp, новый указатель стека, потому что старый теперь ведёт не туда куда надо, если SS в RM был не равен 0 ; Всё. Мы в PM. Нам доступны все 4 ГБ памяти. Достаточно просто написать mov dword[0x12345678], 0x87654321 ... gdt dq 0,\ 0x00CF9A000000FFFF,\ ; kernel 32-bit code 0x00CF92000000FFFF ; kernel 32-bit data gdtr: dw 8 * 3 - 1 dd gdt max7C4 Вы меня опередили UPD: http://flatassembler.net/examples/longmode.zip - Тут минимум для перехода в long mode в одном файле, а в другом помимо перехода ещё и обработка прерываний.
max7C4 и KIV Спасибо, огромное! Страничную адресация мне действительно не нужна. Про то что нет сегментной адресации в длинном режиме - это я прочитал тут http://ru.osdev.wikia.com/wiki/Режим_IA-32e. Значит получается что она есть. Но меня всё же интересует именно длинный режим, так как я хочу производить операции над 64-битными числами. Я читал официальную документацию от AMD и там сказано что переключиться в длинный режим можно только из защищённого. Неужели в официальном мануале ошиблись? Мне вот стало не понятно следующее: Неужели это правда?
Ничего особенно плохого здесь нет. Просто при переключении задач ОС должна сама сохранить регистры ЦПУ. А вообще аппаратная многозадачность была сделана достаточно криво (не то что бы с багами, просто сам механизм кривой), что современные ОС ей не пользуются, потому что свою реализацию сделать проще, да и на переносимость ОС это влияет (на других архитектурах никогда не было аппаратной многозадачности). Нет. Там не ошиблись. В варианте предложенном мною мы делаем так: Code (Text): mov eax, cr0 or eax, 0x80000001 mov cr0, eax jmp CODE64_SELECTOR:start64 use64 start64: mov rax, 0x1234 ... ПРоц переключается в PM ещё по команде mov cr0, eax (а ещё включается страничная адресация тут же, поэтому cr3 должен быть правильно настроен, long mode разрешён, а страничное расширение включено - словом, всё, что требуется для long mode). Просто мы прыгаем на 64-битный код из 16-битного, а не 32. Таким образом экономим на 32-битном сегменте. Кстати, сегмент данных в long mode потребуется только когда мы подумаем о разделении кода ядра и кода приложений. Пока всё выполняется на одном уровне привелегий и нет TSS нам достаточно двух селекторов в GDT - NULL и код. Значение DS, ES, FS и GS игнорируются. Должны быть корректно настроены только CS и SS. Причём SS только если происходит возврат по iretq или retf на другой уровень привелегий, а вам это сейчас не нужно. К слову для длинной арифметики long mode не нужен. Хватит SSE, которым кажется можно пользоваться даже в RM. Ну в обычном PM точно.
KIV Спасибо большое! То есть получается что в длинном режиме сегменты всё таки используются (видимо в статье ошиблись). Ну а на счёт страничной адресации, она обязательно для длинного режима или же можно без неё (мне бы пока лучше без неё)?
Нет. В длинном режиме страничная адресация обязательна. Не совсем. В отличии от других режимов тут сегменты нужны только для определения прав доступа и разрядности кода. Остальные поля игнорируются (исключение - дескриптор TSS). То есть сегментации в привычном понимании нет. Посмотри пример с flatassembler.net. Там маппят первый мегабайт памяти. Всю память маппить не надо! Это займёт слишком много времени и памяти. Тем более в длинном режиме, когда значащая часть адреса - 48 бит. У тебя просто в память таблицы не поместятся. Тебе нужен простейший менеджер памяти, который выполнял бы три функции - получение N физических страниц, освобождение N физических страниц начиная с X, маппинг N физических страниц,начиная с X к виртуальному адресу Y каталога страниц B. Если хочешь - могу поделиться. Или определись, что тебе нужно (IO APIC, видюха, сколько-то МБ ОЗУ) и мапь при переходе в LM.
KIV Интересно! Получается что уровней привилегий больше нет в длинном режиме. То есть я должен сам продумать чтобы у меня программы пользователя не лезли по ненужному адресу? Конечно хочу чтобы поделился, только для меня лучше больше не кодом, а алгоритмом)) Значит суть такова: я заполняю глобальную таблицу дескрипторов где у меня будет первый элемент (с индексом 0) который трогать нельзя (просто он обязательно есть). Первый элемент (второй дескриптор) будет иметь базу 0 и лимит в 4^32 (этот один сегмент и будет представлять из себя плоскую модель памяти: код, данные и стек). Мне нужно будет самому определять диапазоны для каждой из программ (где ей можно данные хранить, где стек и прочее...). Потом я загружаю в регистр GDTR адрес где у меня моя таблица дескрипторов находится (кстати, это получается что в длинном режиме не используется регистр LDTR, то есть нет локальных таблиц дескрипторов, кажись). Я пока не прыгаю никуда (ни в 32-битный, ни в 64-битный код), так как в длинном режиме нужна страничная адресация, то мне нужно настроить регистр CR3. Как я понял, регистр CR3 указывает на начало каталога (таблицу) страниц. Только вот в статье http://www.wasm.ru/article.php?article=pipm07 так всё напутано, что я ничего не понял. Вот после того как я эти таблицы построю и загружу адрес этой таблицы в CR3, тогда мне можно прыгать в 64-битный код (правда есть же какие-то две таблицы страниц). Так как с жёстким я пока работать не умею, то и подкачку мне делать не нужно (у меня все страницы присутствуют в памяти). А если для запуска программ нет места для создания страничек, то просто нельзя запустить программу (так как-то). Вообще, как я понял, лучше с самого начала всю доступную память поделить на странички (для меня лучше бы по 4 МБ), так ведь (чтобы в последствии не менять значения в каталоге страниц)? Интересно же, как же программе пользователя можно запретить в длинном режиме доступ к коду ядра или же к коду или данным другой программы? Для меня важно ещё бы научиться работать с точками останова и регистрами отладки (ведь без них далеко не уедешь начинающему). Вот тут вот http://ru.osdev.wikia.com/wiki/Кодирование_команд я увидел что существует 16 регистров отладки, но в документации AMD я насчитал только 8. Кто же врёт?
Есть! И сегменты остались только для этого. А в обычном PM Они ещё имели базу и лимит. Ещё как уедешь. На Bochs. Он предоставляет огромные отладочные возможности. Если у тебя ОС и программы на разных уровнях привелегий, то тебе надо 5 дескрипторов: NULL, код ядра, данные ядра, код приложения, данные приложения. При этом поля лимита и базы значения не имеют абсолютно - можешь писать туда что хочешь. Вот моя таблица дескрипторов: Code (Text): NULL_SELECTOR = 0 KERNEL_CODE64_SELECTOR = 1 shl 3 KERNEL_DATA64_SELECTOR = 2 shl 3 USER_DATA64_SELECTOR = 3 shl 3 USER_CODE64_SELECTOR = 4 shl 3 USER_DATA32_SELECTOR = 5 shl 3 USER_CODE32_SELECTOR = 6 shl 3 TSS_SELECTOR = 7 shl 3 align 16 gdt dq 0,\ 0x00A09A0000000000,\ ; kernel 64-bit code 0x00A0920000000000,\ ; kernel 64-bit data 0x00A0F20000000000,\ ; user 64-bit data 0x00A0FA0000000000,\ ; user 64-bit code 0x00CFF2000000FFFF,\ ; user 32-bit data 0x00CFFA000000FFFF,\ ; user 32-bit code 0x0000E90000003001 + ((tss and 0xFFFFFF) shl 16) + ((tss and 0xFF000000) shl 32),tss shr 32 ; TSS Пока ты не сделаешь TSS тебе последняя строчка не нужна. Если тебе не нужна поддержка 32-битных приложений, то убери и препоследнюю с предпредпоследней строчкой. LDT и раньше мало использовался, а сейчас в нём вообще необходимости нет, потому что все дескрипторы сегментов различаются лишь DPL (а TSS можно только в GDT делать). После mov cr0, eax таблицы страниц уже должны быть настроены, хотя код и 16-битный. Поскольку тебе для маппинга страниц потребуется доступ к произвольному физ. адресу опреативки, а примапить всю ты не можешь надо выделить некую временную страницу. Таблица страниц, которая на указывает на неё уже примаппена при старте. Вот для примера код для монтирования страниц: Code (Text): ; Временное монтирование страницы RAX temp_mount_page: push rax and rax, not 0xFFF or rax, 111b mov [temp_page_flags], rax invlpg [temp_page] pop rax ret ; Монтирование RCX физических страниц начиная с RAX к виртуальному адресу RDX каталога страниц RBX map_virtual_pages: push rax rcx rdx .map_page: push rbx rcx rdx rax mov rax, rbx mov cl, 48 @@: call temp_mount_page sub cl, 9 mov rdx, [rsp + 8] shr rdx, cl and rdx, 0x1FF lea rdx, [qword temp_page + rdx * 8] cmp cl, 12 jbe @f mov rax, [rdx] test rax, rax jz .create_page_table jmp @b .create_page_table: push rcx [temp_page_flags] mov rcx, 1 call alloc_phis_pages pop [temp_page_flags] invlpg [temp_page] or rax, 111b mov [rdx], rax call temp_mount_page push rax rdi xor rax, rax mov rdi, temp_page mov rcx, 512 rep stosq pop rdi rax rcx jmp @b @@: pop rax mov [rdx], rax pop rdx rcx rbx invlpg [rdx] add rax, 0x1000 add rdx, 0x1000 dec rcx jnz .map_page pop rdx rcx rax ret
KIV Кстати, я не могу понять что такое: Нашёл что shl это сдвиг битов влево на указанное количество. То есть если так: Code (Text): shl EAX,2 То биты в регистре EAX сдвинутся влево (наверное два новых бита справа будут нулями). А вот такая вот конструкция: Мне не понятна. Что она значит?
Я константу объявляю. После этого KERNEL_CODE64_SELECTOR = 8 А mov eax, KERNEL_CODE64_SELECTOR прокомпилируется в mov eax, 8