SlideShare a Scribd company logo
Roll your own toy UNIX-clone OSBy Ahmed Essamwww.eramax.org
Table of contents	1. Environment setup2. Genesis3. The Screen4. The GDT and IDT
Setting EnvironmentTo compile and run the sample code we need to have GCC
Ld
NASM
GNU MakeSetting Environment (2)To Run the OS we need a Virtual Machine Emulator such as VirtualBox
Qemu
Bochs (boot loader)1. Environment setupThis tutorial is not a bootloader tutorial. We will be using GRUB to load our kernel. To do this, we need a floppy disk image with GRUB preloaded onto it.What is GRUBGNU GRUB is a Multiboot boot loader.	boot loader is the first software program that runs when a computer starts. It is responsible for loading and transferring control to the operating system kernel software .
(VM setup)1. Environment setupWe will use Bochs (an open-source x86-64 emulator).We need a bochs configuration file (bochsrc.txt).megs: 32floppya: 1_44="myOs2.bin", status=insertedboot: floppylog: bochsout.txtclock: sync=realtimecpu: ips=500000 keyboard_paste_delay: 100000
Useful scriptsMakefile	making (compiling) our project.Link.ld	link files together into one ELF binary (Kernel).update_image.sh	poke your new kernel binary into the floppy image file.run_bochs.sh	mounts the correct loopback device, runs bochs, then unmounts.
2. Genesis2.1 - The boot codeMBOOT_PAGE_ALIGN    equ 1<<0    ; Load kernel and modules on a page boundaryMBOOT_MEM_INFO      equ 1<<1    ; Provide your kernel with memory infoMBOOT_HEADER_MAGIC  equ 0x1BADB002 ; Multiboot Magic value; NOTE: We do not use MBOOT_AOUT_KLUDGE. It means that GRUB does not; pass us a symbol table.MBOOT_HEADER_FLAGS  equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFOMBOOT_CHECKSUM      equ -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS)[BITS 32]                       ; All instructions should be 32-bit.[GLOBAL mboot]                  ; Make 'mboot' accessible from C.[EXTERN code]                   ; Start of the '.text' section.[EXTERN bss]                    ; Start of the .bss section.[EXTERN end]                    ; End of the last loadable section.mboot:  dd  MBOOT_HEADER_MAGIC        ; GRUB will search for this value on each                                ; 4-byte boundary in your kernel file  dd  MBOOT_HEADER_FLAGS        ; How GRUB should load your file / settings  dd  MBOOT_CHECKSUM            ; To ensure that the above values are correct   
2.1 - The boot code (Cont)  dd  mboot                     ; Location of this descriptor  dd  code                      ; Start of kernel '.text' (code) section.  dd  bss                       ; End of kernel '.data' section.  dd  end                       ; End of kernel.  dd  start                     ; Kernel entry point (initial EIP).[GLOBAL start]                  ; Kernel entry point.[EXTERN main]                   ; This is the entry point of our C codestart:  push    ebx                   ; Load multiboot header location  ; Execute the kernel:  cli                         ; Disable interrupts.  call main                   ; call our main() function.  jmp $                       ; Enter an infinite loop, to stop the processor                              ; executing whatever rubbish is in the memory                              ; after our kernel!
2.3. Adding some C code// main.c int main(struct multiboot *mboot_ptr){  // Kernel Code.  return 0xDEADBABA;}
common.c functions for writing to and reading from the I/O bus, and some typedefs :typedef unsigned int   u32int;typedef          int   s32int;typedef unsigned short u16int;typedef          short s16int;typedef unsigned char  u8int;typedef          char  s8int;void outb(u16int port, u8int value){asm volatile ("outb %1, %0" : : "dN" (port), "a" (value)); }u8int inb(u16int port){   u8int ret;   asm volatile("inb %1, %0" : "=a" (ret) : "dN" (port));   return ret;}u16int inw(u16int port){   u16int ret;   asm volatile ("inw %1, %0" : "=a" (ret) : "dN" (port));   return ret;}
3. The Screen3.1. The theoryYour kernel gets booted by GRUB in text mode.That is, it has available to it a framebuffer (area of memory) that controls a screen of characters (not pixels) 80 wide by 25 high, at address 0xB8000.Framebuffer is not actually normal RAM. It is part of the VGA controller's dedicated video memory that has been memory-mapped via hardware into your linear address space.
3.1. The theory (Cont)The framebuffer is just an array of 16-bit words, each 16-bit value representing the display of one character. The offset from the start of the framebuffer of the word that specifies a character at position x, y is:(y * 80 + x) * 2 8 bits are used to represent a character.foreground and background colours (4 bits each).
3.1. The theory (Cont)
3.2.2. The monitor codeMoving the cursor		static void move_cursor()Scrolling the screen		 static void scroll()Writing a character to the screen		 void monitor_put(char c)location = video_memory + (cursor_y*80 + cursor_x);*location = c | attribute; Clearing the screen	u16int blank = 0x20 /* space */ | (attributeByte << 8); 	 for (i = 0; i < 80*25; i++)	   {  	        video_memory[i] = blank;	   }
3.2.2. The monitor code (Cont)Writing a string	void monitor_write(char *c){	   int i = 0;	   while (c[i])	   { 	      monitor_put(c[i++]); 	   }} //-------- Kernel Code :	monitor_clear();monitor_write("Hello, world!");
The monitor
4. The GDT and IDT4.1. The Global Descriptor Table (theory)	are arrays of flags and bit values describing the operation of the segmentation system.Every memory access is evaluated with respect to a segment. That is, the memory address is added to the segment's base address, and checked against the segment's length.
GDT there is one thing that segmentation can do that paging can't, and that's set the ring levels. A ring is a privilege level - zero being the most privileged, and three being the least. Processes in ring zero are said to be running in kernel-mode, or supervisor-mode, because they can use instructions like sti and cli, something which most processes can't.A segment descriptor carries inside it a number representing the ring level it applies to.
The Global Descriptor Table (practical)A GDT entry looks likestruct gdt_entry_struct{   u16int limit_low;           // The lower 16 bits of the limit.   u16int base_low;            // The lower 16 bits of the base.   u8int  base_middle;         // The next 8 bits of the base.   u8int  access;              // Access flags, determine what ring 			        //this segment can be used in.   u8int  granularity;   u8int  base_high;           // The last 8 bits of the base.} __attribute__((packed));
GDTu8int  access;    P    Is segment present? (1 = Yes)DPL    Descriptor privilege level - Ring 0 - 3.DT    Descriptor typeType    Segment type : code segment / data segment.
GDTTo pass the GDT Table ,we pass the Pointer to this table to the CPU and pass its limit(length).So we must use this struct :struct gdt_ptr_struct{   u16int limit;               // The upper 16 bits of all selector limits.   u32int base;                // The address of the first gdt_entry_t }
GDTgdt_entry_t gdt_entries[5];gdt_ptr_t   gdt_ptr;static void init_gdt(){   gdt_ptr.limit = (sizeof(gdt_entry_t) * 5) - 1;   gdt_ptr.base  = (u32int)&gdt_entries;   gdt_set_gate(0, 0, 0, 0, 0);                // Null segment   gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment   gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment   gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User mode code segment   gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User mode data segment   gdt_flush((u32int)&gdt_ptr);}
GDTstatic void gdt_set_gate(s32int num, u32int base, u32int limit, u8int access, u8int gran){   gdt_entries[num].base_low    = (base & 0xFFFF);   gdt_entries[num].base_middle = (base >> 16) & 0xFF;   gdt_entries[num].base_high   = (base >> 24) & 0xFF;   gdt_entries[num].limit_low   = (limit & 0xFFFF);   gdt_entries[num].granularity = (limit >> 16) & 0x0F;   gdt_entries[num].granularity |= gran & 0xF0;   gdt_entries[num].access      = access;}[GLOBAL gdt_flush]    ; Allows the C code to call gdt_flush().gdt_flush:   mov eax, [esp+4]  ; Get the pointer to the GDT, passed as a parameter.   lgdt [eax]        ; Load the new GDT pointer
4.3. The Interrupt Descriptor Table (theory)There are times when you want to interrupt the processor. You want to stop it doing what it is doing, and force it to do something different. An example of this is when an timer or keyboard interrupt request (IRQ) fires.The processor can register 'signal handlers' (interrupt handlers) that deal with the interrupt, then return to the code that was running before it fired. Interrupts can be fired externally, via IRQs, or internally, via the 'int n' instruction. The Interrupt Descriptor Table tells the processor where to find handlers for each interrupt. It is very similar to the GDT. It is just an array of entries, each one corresponding to an interrupt number. There are 256 possible interrupt numbers, so 256 must be defined. If an interrupt occurs and there is no entry for it (even a NULL entry is fine), the processor will panic and reset.
Faults, traps and exceptionsThe special, CPU-dedicated 32 interrupts :0 - Division by zero exception1 - Debug exception2 - Non maskable interrupt3 - Breakpoint exception4 - 'Into detected overflow'5 - Out of bounds exception6 - Invalid opcode exception7 - No coprocessor exception8 - Double fault (pushes an error code)9 - Coprocessor segment overrun10 - Bad TSS (pushes an error code)11 - Segment not present (pushes an error code)12 - Stack fault (pushes an error code)13 - General protection fault (pushes an error code)14 - Page fault (pushes an error code)15 - Unknown interrupt exception16 - Coprocessor fault17 - Alignment check exception18 - Machine check exception19-31 - Reserved
4.4. The Interrupt Descriptor Table (practice)// A struct describing an interrupt gate.struct idt_entry_struct{   u16int base_lo;             // The lower 16 bits of the address to 					//jump to when this interrupt fires.   u16int sel;                 // Kernel segment selector.   u8int  always0;             // This must always be zero.   u8int  flags;               // More flags. See documentation.   u16int base_hi;     // The upper 16 bits of the address to jump to.} __attribute__((packed));struct idt_ptr_struct{   u16int limit;   u32int base; // The address of the first element in our idt_entry_t array.} __attribute__((packed));
IDTThe DPL describes the privilege level we expect to be called from.The P bit signifies the entry is present.	Any descriptor with this bit clear will cause a "Interrupt Not Handled" exception.
IDTextern void isr0 ();...extern void isr31();idt_entry_t idt_entries[256];idt_ptr_t   idt_ptr;static void init_idt(){   idt_ptr.limit = sizeof(idt_entry_t) * 256 -1;   idt_ptr.base  = (u32int)&idt_entries;   memset(&idt_entries, 0, sizeof(idt_entry_t)*256);//set all to null.   idt_set_gate( 0, (u32int)isr0 , 0x08, 0x8E);   idt_set_gate( 1, (u32int)isr1 , 0x08, 0x8E);   ...   idt_set_gate(31, (u32int)isr32, 0x08, 0x8E);   idt_flush((u32int)&idt_ptr);}
IDT[GLOBAL idt_flush]    ; Allows the C code to call idt_flush().idt_flush:   mov eax, [esp+4]  ; Get the pointer to the IDT, passed as a parameter.    lidt [eax]        ; Load the IDT pointer.   ret
IDTGreat! We've got code that will tell the CPU where to find our interrupt handlers - but we haven't written any yet! When the processor receives an interrupt, it saves the contents of the essential registers (instruction pointer, stack pointer, code and data segments, flags register) to the stack. It then finds the interrupt handler location from our IDT and jumps to it. some interrupts also push an error code onto the stack. We can't call a common function without a common stack frame, so for those that don't push an error code, we push a dummy one, so the stack is the same.
interrupt.sISR_NOERRCODE 0ISR_NOERRCODE 1...
interrupt.sASM common handler function to handle interrupts :; In isr.c[EXTERN isr_handler]; This is our common ISR stub. It saves the processor state, sets; up for kernel mode segments, calls the C-level fault handler,; and finally restores the stack frame.isr_common_stub:   pusha                    ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax   mov ax, ds               ; Lower 16-bits of eax = ds.   push eax                 ; save the data segment descriptor   mov ax, 0x10  ; load the kernel data segment descriptor   mov ds, ax   mov es, ax   mov fs, ax   mov gs, ax   call isr_handler   pop eax        ; reload the original data segment descriptor   mov ds, ax   mov es, ax   mov fs, ax   mov gs, ax   popa                     ; Pops edi,esi,ebp...   add esp, 8     ; Cleans up the pushed error code and pushed ISR number   sti   iret           ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
isr.ctypedef struct registers{   u32int ds;                  // Data segment selector   u32int edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha.   u32int int_no, err_code;    // Interrupt number and error code (if applicable)   u32int eip, cs, eflags, useresp, ss; // Pushed by the processor automatically.} registers_t; // This gets called from our ASM interrupt handler stub.void isr_handler(registers_t regs){   monitor_write("recieved interrupt: ");   monitor_write_dec(regs.int_no);   monitor_put('');}
Testing// Kernel Code:asm volatile ("int $0x3");asm volatile ("int $0x4"); Output:

More Related Content

Roll your own toy unix clone os

  • 1. Roll your own toy UNIX-clone OSBy Ahmed Essamwww.eramax.org
  • 2. Table of contents 1. Environment setup2. Genesis3. The Screen4. The GDT and IDT
  • 3. Setting EnvironmentTo compile and run the sample code we need to have GCC
  • 4. Ld
  • 6. GNU MakeSetting Environment (2)To Run the OS we need a Virtual Machine Emulator such as VirtualBox
  • 8. Bochs (boot loader)1. Environment setupThis tutorial is not a bootloader tutorial. We will be using GRUB to load our kernel. To do this, we need a floppy disk image with GRUB preloaded onto it.What is GRUBGNU GRUB is a Multiboot boot loader. boot loader is the first software program that runs when a computer starts. It is responsible for loading and transferring control to the operating system kernel software .
  • 9. (VM setup)1. Environment setupWe will use Bochs (an open-source x86-64 emulator).We need a bochs configuration file (bochsrc.txt).megs: 32floppya: 1_44="myOs2.bin", status=insertedboot: floppylog: bochsout.txtclock: sync=realtimecpu: ips=500000 keyboard_paste_delay: 100000
  • 10. Useful scriptsMakefile making (compiling) our project.Link.ld link files together into one ELF binary (Kernel).update_image.sh poke your new kernel binary into the floppy image file.run_bochs.sh mounts the correct loopback device, runs bochs, then unmounts.
  • 11. 2. Genesis2.1 - The boot codeMBOOT_PAGE_ALIGN    equ 1<<0    ; Load kernel and modules on a page boundaryMBOOT_MEM_INFO      equ 1<<1    ; Provide your kernel with memory infoMBOOT_HEADER_MAGIC  equ 0x1BADB002 ; Multiboot Magic value; NOTE: We do not use MBOOT_AOUT_KLUDGE. It means that GRUB does not; pass us a symbol table.MBOOT_HEADER_FLAGS  equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFOMBOOT_CHECKSUM      equ -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS)[BITS 32]                       ; All instructions should be 32-bit.[GLOBAL mboot]                  ; Make 'mboot' accessible from C.[EXTERN code]                   ; Start of the '.text' section.[EXTERN bss]                    ; Start of the .bss section.[EXTERN end]                    ; End of the last loadable section.mboot:  dd  MBOOT_HEADER_MAGIC        ; GRUB will search for this value on each                                ; 4-byte boundary in your kernel file  dd  MBOOT_HEADER_FLAGS        ; How GRUB should load your file / settings  dd  MBOOT_CHECKSUM            ; To ensure that the above values are correct   
  • 12. 2.1 - The boot code (Cont)  dd  mboot                     ; Location of this descriptor  dd  code                      ; Start of kernel '.text' (code) section.  dd  bss                       ; End of kernel '.data' section.  dd  end                       ; End of kernel.  dd  start                     ; Kernel entry point (initial EIP).[GLOBAL start]                  ; Kernel entry point.[EXTERN main]                   ; This is the entry point of our C codestart:  push    ebx                   ; Load multiboot header location  ; Execute the kernel:  cli                         ; Disable interrupts.  call main                   ; call our main() function.  jmp $                       ; Enter an infinite loop, to stop the processor                              ; executing whatever rubbish is in the memory                              ; after our kernel!
  • 13. 2.3. Adding some C code// main.c int main(struct multiboot *mboot_ptr){  // Kernel Code.  return 0xDEADBABA;}
  • 14. common.c functions for writing to and reading from the I/O bus, and some typedefs :typedef unsigned int   u32int;typedef          int   s32int;typedef unsigned short u16int;typedef          short s16int;typedef unsigned char  u8int;typedef          char  s8int;void outb(u16int port, u8int value){asm volatile ("outb %1, %0" : : "dN" (port), "a" (value)); }u8int inb(u16int port){   u8int ret;   asm volatile("inb %1, %0" : "=a" (ret) : "dN" (port));   return ret;}u16int inw(u16int port){   u16int ret;   asm volatile ("inw %1, %0" : "=a" (ret) : "dN" (port));   return ret;}
  • 15. 3. The Screen3.1. The theoryYour kernel gets booted by GRUB in text mode.That is, it has available to it a framebuffer (area of memory) that controls a screen of characters (not pixels) 80 wide by 25 high, at address 0xB8000.Framebuffer is not actually normal RAM. It is part of the VGA controller's dedicated video memory that has been memory-mapped via hardware into your linear address space.
  • 16. 3.1. The theory (Cont)The framebuffer is just an array of 16-bit words, each 16-bit value representing the display of one character. The offset from the start of the framebuffer of the word that specifies a character at position x, y is:(y * 80 + x) * 2 8 bits are used to represent a character.foreground and background colours (4 bits each).
  • 18. 3.2.2. The monitor codeMoving the cursor static void move_cursor()Scrolling the screen static void scroll()Writing a character to the screen void monitor_put(char c)location = video_memory + (cursor_y*80 + cursor_x);*location = c | attribute; Clearing the screen u16int blank = 0x20 /* space */ | (attributeByte << 8);   for (i = 0; i < 80*25; i++)    {         video_memory[i] = blank;    }
  • 19. 3.2.2. The monitor code (Cont)Writing a string void monitor_write(char *c){    int i = 0;    while (c[i])    {        monitor_put(c[i++]);    }} //-------- Kernel Code : monitor_clear();monitor_write("Hello, world!");
  • 21. 4. The GDT and IDT4.1. The Global Descriptor Table (theory) are arrays of flags and bit values describing the operation of the segmentation system.Every memory access is evaluated with respect to a segment. That is, the memory address is added to the segment's base address, and checked against the segment's length.
  • 22. GDT there is one thing that segmentation can do that paging can't, and that's set the ring levels. A ring is a privilege level - zero being the most privileged, and three being the least. Processes in ring zero are said to be running in kernel-mode, or supervisor-mode, because they can use instructions like sti and cli, something which most processes can't.A segment descriptor carries inside it a number representing the ring level it applies to.
  • 23. The Global Descriptor Table (practical)A GDT entry looks likestruct gdt_entry_struct{   u16int limit_low;           // The lower 16 bits of the limit.   u16int base_low;            // The lower 16 bits of the base.   u8int  base_middle;         // The next 8 bits of the base.   u8int  access;              // Access flags, determine what ring //this segment can be used in.   u8int  granularity;   u8int  base_high;           // The last 8 bits of the base.} __attribute__((packed));
  • 24. GDTu8int  access;    P    Is segment present? (1 = Yes)DPL    Descriptor privilege level - Ring 0 - 3.DT    Descriptor typeType    Segment type : code segment / data segment.
  • 25. GDTTo pass the GDT Table ,we pass the Pointer to this table to the CPU and pass its limit(length).So we must use this struct :struct gdt_ptr_struct{   u16int limit;               // The upper 16 bits of all selector limits.   u32int base;                // The address of the first gdt_entry_t }
  • 26. GDTgdt_entry_t gdt_entries[5];gdt_ptr_t   gdt_ptr;static void init_gdt(){   gdt_ptr.limit = (sizeof(gdt_entry_t) * 5) - 1;   gdt_ptr.base  = (u32int)&gdt_entries;   gdt_set_gate(0, 0, 0, 0, 0);                // Null segment   gdt_set_gate(1, 0, 0xFFFFFFFF, 0x9A, 0xCF); // Code segment   gdt_set_gate(2, 0, 0xFFFFFFFF, 0x92, 0xCF); // Data segment   gdt_set_gate(3, 0, 0xFFFFFFFF, 0xFA, 0xCF); // User mode code segment   gdt_set_gate(4, 0, 0xFFFFFFFF, 0xF2, 0xCF); // User mode data segment   gdt_flush((u32int)&gdt_ptr);}
  • 28. 4.3. The Interrupt Descriptor Table (theory)There are times when you want to interrupt the processor. You want to stop it doing what it is doing, and force it to do something different. An example of this is when an timer or keyboard interrupt request (IRQ) fires.The processor can register 'signal handlers' (interrupt handlers) that deal with the interrupt, then return to the code that was running before it fired. Interrupts can be fired externally, via IRQs, or internally, via the 'int n' instruction. The Interrupt Descriptor Table tells the processor where to find handlers for each interrupt. It is very similar to the GDT. It is just an array of entries, each one corresponding to an interrupt number. There are 256 possible interrupt numbers, so 256 must be defined. If an interrupt occurs and there is no entry for it (even a NULL entry is fine), the processor will panic and reset.
  • 29. Faults, traps and exceptionsThe special, CPU-dedicated 32 interrupts :0 - Division by zero exception1 - Debug exception2 - Non maskable interrupt3 - Breakpoint exception4 - 'Into detected overflow'5 - Out of bounds exception6 - Invalid opcode exception7 - No coprocessor exception8 - Double fault (pushes an error code)9 - Coprocessor segment overrun10 - Bad TSS (pushes an error code)11 - Segment not present (pushes an error code)12 - Stack fault (pushes an error code)13 - General protection fault (pushes an error code)14 - Page fault (pushes an error code)15 - Unknown interrupt exception16 - Coprocessor fault17 - Alignment check exception18 - Machine check exception19-31 - Reserved
  • 30. 4.4. The Interrupt Descriptor Table (practice)// A struct describing an interrupt gate.struct idt_entry_struct{   u16int base_lo;             // The lower 16 bits of the address to //jump to when this interrupt fires.   u16int sel;                 // Kernel segment selector.   u8int  always0;             // This must always be zero.   u8int  flags;               // More flags. See documentation.   u16int base_hi;     // The upper 16 bits of the address to jump to.} __attribute__((packed));struct idt_ptr_struct{   u16int limit;   u32int base; // The address of the first element in our idt_entry_t array.} __attribute__((packed));
  • 31. IDTThe DPL describes the privilege level we expect to be called from.The P bit signifies the entry is present. Any descriptor with this bit clear will cause a "Interrupt Not Handled" exception.
  • 32. IDTextern void isr0 ();...extern void isr31();idt_entry_t idt_entries[256];idt_ptr_t   idt_ptr;static void init_idt(){   idt_ptr.limit = sizeof(idt_entry_t) * 256 -1;   idt_ptr.base  = (u32int)&idt_entries;   memset(&idt_entries, 0, sizeof(idt_entry_t)*256);//set all to null.   idt_set_gate( 0, (u32int)isr0 , 0x08, 0x8E);   idt_set_gate( 1, (u32int)isr1 , 0x08, 0x8E);   ...   idt_set_gate(31, (u32int)isr32, 0x08, 0x8E);   idt_flush((u32int)&idt_ptr);}
  • 33. IDT[GLOBAL idt_flush]    ; Allows the C code to call idt_flush().idt_flush:   mov eax, [esp+4]  ; Get the pointer to the IDT, passed as a parameter.    lidt [eax]        ; Load the IDT pointer.   ret
  • 34. IDTGreat! We've got code that will tell the CPU where to find our interrupt handlers - but we haven't written any yet! When the processor receives an interrupt, it saves the contents of the essential registers (instruction pointer, stack pointer, code and data segments, flags register) to the stack. It then finds the interrupt handler location from our IDT and jumps to it. some interrupts also push an error code onto the stack. We can't call a common function without a common stack frame, so for those that don't push an error code, we push a dummy one, so the stack is the same.
  • 36. interrupt.sASM common handler function to handle interrupts :; In isr.c[EXTERN isr_handler]; This is our common ISR stub. It saves the processor state, sets; up for kernel mode segments, calls the C-level fault handler,; and finally restores the stack frame.isr_common_stub:   pusha                    ; Pushes edi,esi,ebp,esp,ebx,edx,ecx,eax   mov ax, ds               ; Lower 16-bits of eax = ds.   push eax                 ; save the data segment descriptor   mov ax, 0x10  ; load the kernel data segment descriptor   mov ds, ax   mov es, ax   mov fs, ax   mov gs, ax   call isr_handler   pop eax        ; reload the original data segment descriptor   mov ds, ax   mov es, ax   mov fs, ax   mov gs, ax   popa                     ; Pops edi,esi,ebp...   add esp, 8     ; Cleans up the pushed error code and pushed ISR number   sti   iret           ; pops 5 things at once: CS, EIP, EFLAGS, SS, and ESP
  • 37. isr.ctypedef struct registers{   u32int ds;                  // Data segment selector   u32int edi, esi, ebp, esp, ebx, edx, ecx, eax; // Pushed by pusha.   u32int int_no, err_code;    // Interrupt number and error code (if applicable)   u32int eip, cs, eflags, useresp, ss; // Pushed by the processor automatically.} registers_t; // This gets called from our ASM interrupt handler stub.void isr_handler(registers_t regs){   monitor_write("recieved interrupt: ");   monitor_write_dec(regs.int_no);   monitor_put('');}
  • 42. Intel® 64 and IA-32 Architectures Software Developer's Manuals http://www.intel.com/products/processor/manuals/
  • 43. Memory Modes : Real Mode and Protected Mode http://3asfh.net/vb/showthread.php?t=37922
  • 44. References(2)Making a Simple C kernel with Basic printf and clearscreen Functionshttp://www.osdever.net/tutorials/basickernel.php