Search     or:     and:
 LINUX 
 Language 
 Kernel 
 Package 
 Book 
 Test 
 OS 
 Forum 
iakovlev.org

JeffOS

Автор - jwthomp@cu-online.com

Для начала вспомним формат дескриптора gdt:


 63 - 56    4-й байт базового адреса 
 55         Granularity Bit
 54         Default Bit
 53         0
 52         Available for Use (free bit)
 51 - 48    старшие биты ограничения
 47         Segment Present Bit
 46 - 45    Descriptor Privilege Level
 44         System Bit
 43         Data/Code Bit
 42         Conforming Bit
 41         Readable bit
 40         Accessed bit
 39 - 32    3-й байт базового адреса
 31 - 24    2-й байт базового адреса
 23 - 16    1-й байт базового адреса
 15 - 8     2-й байт ограничения
 7 - 0      1-й байт ограничения
 
Здесь : базовый адрес - это адрес самой таблицы. Ограничение - число , измеряемое либо в байтах , либо в килобайтных страницах. Если Granularity Bit=0 , то размер сегмента может быть произвольным в килобайтах. Если Granularity Bit=1 , то размер сегмента кратен 4 килобайтам.

default bit - определяет тип кодового сегмента - 32-битный или 16-битный. Если он равен 0 - то 16-битный.

present bit - устанавливается в единицу , если сегмент загружен.

system bit - устанавливается в 0 , если сегмент принадлежит самой ОС.

data/code bit - указывает на кодовый сегмент либо сегмент данных.

readable bit - сегмент на чтение либо выполнение

Первая стадия загрузки ядра : смотрим каталог /boot. mainboot.c -> функция "_start" - в ее начале идет вызов kinit_MemMgmt (memboot.c). Здесь происходит попытка определения количества памяти в системе - val = kmemcountboot();.. Далее происходит инициализация page tables - kinit_pageboot. Пэйджинг - механизм захвата процессом памяти. Сейчас будет создана т.н. "виртуальная память". Каждый раз , когда происходит выделение памяти , процессор смотрит в эту таблицу.

page tables организованы по следующему принципу : вначале идет т.н. page directory , которая разбита на 1024 частей.Каждая такая часть устроена следующим образом :


 31 - 12    базовый адрес
 11 - 9     не используется
 8          0
 7          Page Size Bit
 6          0
 5          Accessed Bit
 4          Page Cache Disable Bit
 3          Page Write Through Bit
 2          User/Supervisor Bit
 1          Read/Write Bit
 0          Page Present Bit
базовый адрес состоит из 2-х частей - индекса и физического адреса самой таблицы. Page Size Bit - указывает на размер страницы (Bit = 0 для 4kb или Bit = 1 для 4MB). accessed bit - устанавливается при доступе к ней. Page Cache Disable Bit и Page Write Bit не используются. User/Supervisor Bit - устанавливает права доступа. Если бит=0 , к странице имеют доступ привилегированные уровни 0, 1, 2 Если бит=1 , к странице имеют доступ привилегированные уровни 0, 1, 2, 3. Read/Write - устанавливает право на запись для пользовательского процесса. Present Bit - представлена ли страница в памяти.

Т.о. page directory ссылается на тысячу таблиц page tables , каждая из которых в свою очередь имеет следующую структуру :


 31 - 12    Page Base Address
 11 - 9     Unused (Free bits)
 8 - 7      0
 6          Dirty Bit
 5          Accessed Bit
 4          Page Cache Disable Bit
 3          Page Write Through Bit
 2          User/Supervisor Bit
 1          Read/Write Bit
 0          Page Present Bit
 
 
Все Kernel pages установлены с правами Supervisor, Read/Write, Page Present .

Функция kinit_pageboot(memboot.c) инициализирует три page tables. Первая - размером 1 метр - это сама page directory. Вторая имеет размер 4 метра и будет использоваться пользовательскими процессами. Третья является указателем на верхнюю память. Первый адрес в page directory будет указывать на нижний адрес в пространстве этих 4 метров. Адрес page directory помещается в регистр cr3. В регистре cr0 устанавливается paging bit. Первая стадия загрузки ядра завершена.

Вторая стадия начинается с фуункции _start_kernel(main.c). Выводим на экран "Main kernel loaded.". Инициализируем память - kinit_page()(mem.c). Вначале проинициализируем битовое поле PMAT размером 2048 байт. Каждый бит этого поля представляет одну страницу физической памяти. Если бит=1 , соответствующая страница памяти выделена.

Функция kinit_tmpuser инициализирует память для пользовательских процессов, эта память начинается с адреса 20000h.

Функция kinit_task()(task.c) инициализирует стартовый процесс ядра. Она вызывает 2 функции : первая - kinit_gdt() - инициализирует GDT. Инициализируются 5 сегментов : код ядра , данные ядра , данные ядра 2 , код пользователя , данные пользователя.

Вторая функция - kinit_ktask() - инициализирует задачу , управляемую ядром. Обнуляется TaskList - сюда включается список всех задач в системе. Выделяем 4 килобайта для kernel task segment. Этот сегмент добавляем в GDT. Этот Task segment имеет следующую структуру :


 struct TSS {
     ushort link;            // set to 0
     ushort unused0;
     ulong esp0;             // set to the end of the task segment page
     ushort ss0;             // set to SEL_KDATA (Kernel Data segment)
     ushort unused1;
     ulong esp1;             // set to 0
     ushort ss1;             // set to 0
     ushort unused2;
     ulong esp2;             // set to 0
     ushort ss2;             // set to 0
     ushort unused3;
     ulong cr3;              // set to the physical address of this tasks page 
                             // tables
     ulong eip;              // set to the entry point to this tasks code
     ulong eflags;           // set to 0x4202
     ulong eax, ecx, edx, ebx, esp, ebp, esi, edi; // set to garbage values
     ushort es;              // set to SEL_KDATA (Kernel data segment)
     ushort unused4;
     ushort cs;              // set to SEL_KCODE (Kernel code segment)
     ushort unused5;
     ushort ss;              // set to SEL_KDATA
     ushort unused6;
     ushort ds;              // set to SEL_KDATA
     ushort unused7;
     ushort fs;              // set to SEL_KDATA
     ushort unused8;
     ushort gs;              // set to SEL_KDATA
     ushort unused9;
     ushort ldt;             // set to 0
     ushort unused10;
     ushort debugtrap;       // set to 0
     ushort iomapbase;       // set to 0
 };
 
Параметры espx и ssx используются для хранения указателя на стек . Параметр cr3 используется для хранения указателя на физический адрес страницы памяти. Понятно , что каждая задача имеет уникальный набор страниц. Параметры eax, ecx, edx, ebx, esp, ebp, esi, edi; используют свои наборы. В данной версии LDT не используется. Поля из этой структуры будут загружаться каждый раз в регистры , когда процессор будет переключаться на данную задачу.Когда процесс становится неактивным , соответственно происходит обратное сохранение.

При создании task segment в GDT нужно проинициализировать указатель на него - 8-байтный дескриптор. Его формат :


 63 - 56    Fourth Byte of Base Address
 55         Granularity Bit
 54 - 53    0
 52         Available for use (free bit)
 51 - 48    Upper Nibble of Size
 47         Present in Memory Bit
 46 - 45    Descriptor Privilege Level
 44         System Built
 43         16/32 Bit
 42         0
 41         Busy Bit
 40         1
 39 - 32    Third Byte of Base Address
 31 - 24    Second Byte of Base Address
 23 - 16    First Byte of Base Address
 15 - 8     Second Byte of Segment Size
 7 - 0      First Byte of Segment Size
Его структура похожа на кодовый сегментный дескриптор , за исключение 16/32 bit, и Busy Bit.

Создаем пользовательский процесс - task_create. Он размещается по адресу 0x20000. Его стек размещается в 0x2107c. Этот процесс имеет привилегии ядра и мало похож на обычный пользовательский процесс. Процесс добавляется в список задач.

После создания 2-х процессов проинициализируем таблицу прерываний - kinit_idt(int.c). Большинство прерываний имеет указатель на заглушку. Таблица загружаетя в idt-регистр и разрешаются прерывания. Врапперы прерываний лежат в jump.S.

Следующий шаг - переключение задач - swtch()(task.c). При этом извлекается селекторный адрес и процессор переключает задачи.

Исходники лежат тут

Оставьте свой комментарий !

Ваше имя:
Комментарий:
Оба поля являются обязательными

 Автор  Комментарий к данной статье