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

Sample Inline Functions

Memory access

FAR_PEEKx
читает байт (или слово) из памяти из сегмента данных.
static __inline__ dword farpeekl(word sel,void *off)
 {
   dword ret;
   asm("push %%fs;mov %1,%%fs;"
       "mov %%fs:(%2),%0;"
       "pop %%fs":"=r"(ret):"g"(sel),"r"(off));
   return ret;
 }
FAR_POKEx
пишет байт или слово в память
static __inline__ void farpokeb(word sel, void *off, byte v)
 {
   asm("push %%fs; mov %0,%%fs;"
       "movb %2,%%fs:(%1);"
       "pop %%fs": :"g"(sel),"r"(off),"r"(v));
 }

I/O access

OUTx
посылает байт в порт. В зависимости от размера - outb, outw и outl . Модификатор "a" указывает на то , что си-шная переменная value будет размещена в регистре eax
static __inline__ void outb(unsigned short port, unsigned char val)
 {
    asm volatile("outb %0,%1"::"a"(val), "Nd" (port));
 }
INx
получение данных из порта .
static __inline__ unsigned char inb(unsigned short port)
 {
    unsigned char ret;
    asm volatile ("inb %1,%0":"=a"(ret):"Nd"(port));
    return ret;
 }
IO_WAIT
перевод процессора в состояние ожидания .
static __inline__ void io_wait(void)
 {
    asm volatile("jmp 1f;1:jmp 1f;1:");
 }
static __inline__ void io_wait(void)
 {
    asm volatile("outb %%al, $0x80" : : "a"(0));
    // port 0x80 is used for 'checkpoints' during POST.
    // linux kernel seems to think it's free for use :-/
 }

Interrupt-related functions

Enabled?
вертает 'true' если irq = enabled
static __inline__
 int irqEnabled()
 {
         int f;
         asm volatile ("pushf;popl %0":"=g" (f));
         return f & (1<<9);
 }
acknowledge
sends the PIC chip (8259a) that we're ready for more interrupts. as pics are cascaded for int 8-15, if no>=8, we will have to send a 'clearance code' for both master and slave PICs.
static __inline__
 void irqUnlock(int no)
 {
   /*             Val, Port */
   if (no>7) outb(0x20,0xa0);
   outb(0x20,0x20);
 }
LIDT
определение новой таблицы прерываний
void lidt(void *base, unsigned int size) {
    unsigned int i[2];
 
    i[0] = size << 16;
    i[1] = (unsigned int) base;
    asm ("lidt (%0)": :"p" (((char *) i)+2));
 }

Другой вариант

void lidt(void *base, unsigned short size) {
 struct{unsigned short length;unsigned long base;}__attribute__((__packed__)) IDTR;
 
    IDTR.length = size;
    IDTR.base = (unsigned long) base;
    asm ("lidt (%0)": :"p" (&IDTR));
 }

Cpu-related functions

CPUID
Идентификация процессора
/** issue a single request to CPUID. Fits 'intel features', for instance
  */
 static inline void cpuid(int code, dword *a, dword *d) {
   asm volatile("cpuid":"=a"(*a),"=d"(*d):"0"(code));
 }
READ_CRx
чтение значения регистра cr0
static inline unsigned read_cr0() {
    unsigned val;
    asm volatile("mov %%cr0, %0":"=r"(val));
    return val;
 }

Interrupt Service Routines

Что такое Interrupt Service Routine (ISR)?

Когда срабатывает внешнее прерывание - нормальное течение команд прерывается и вызывается подпрограмма обслуживания прерываний (ISR).

Прерывание может быть вызвано как железом , так и софтом . Например , каждый раз при нажатии на клавишу срабатывает прерывание IRQ1 (Interrupt Request 1)

Софт может вызвать прерывания с помощью ключевого слова int .

Нужно различать , что в защищенном режиме текущие прерывания хранятся в в interrupt descriptor table (IDT) , а в реальном режиме - в interrupt vector table (IVT) .

Problem

Некоторые пытаются писать inline-код в следующем стиле :

/* How NOT to write an interrupt handler           */
 void interrupt_handler(void)
 {
     __asm__("pushad"); /* Save registers.          */
     /* do something */
     __asm__("popad");  /* Restore registers.       */
     __asm__("iret");   /* This will triple-fault! */
 }

Это не будет работать . Правильно вот так :

push   %ebp
 mov    %esp,%ebp
 sub    $<size of local variables>,%esp
 pushad
 # C code comes here
 popad
 iret
 # 'leave' if you use local variables, 'pop %ebp' otherwise.
 leave
 ret

Solutions

Two-Stage Assembler Wrapping

Напишем враппер на ассемблере , который вызывает си-шную функцию :.

/* filename : isr_wrapper.asm */
 .globl   _isr_wrapper
 .align   4
 
 _isr_wrapper:
 
     pushad
     call    _interrupt_handler
     popad
     iret
 
 
 /* filename : interrupt_handler.c */
 void interrupt_handler(void)
 {
     /* do something */
 }

Загрузчик на GAS - первые шаги

Начнем со следующего кода - step1.s - создаем простой бинарник размером 512 байт , в его конец не забудем прописать 2 магических байта:

 .code16   
 .text
 
 .global _start
 start:
 hang:
    jmp hang
 .org 510
 boot_flag:
    .word 0xAA55
    
 Компиляция :
 	as step1.s -o step1.o
 	ld step1.o -o step1.bin --oformat binary
 
Немного усложним задачу - step2.s - распечатаем сообщение на экране . Для этого используем 10-е прерывание. Команда lodsb последовательно прогрузит байтики текстового сообщения. В примере используем числовые метки. Если при обращении метка стоит выше , к ней добавляется префикс b, если ниже - префикс f:

 .code16
 .text
 
 # .global _start
 # start:
 
    mov $0x07C0, %ax
    mov %ax, %ds
 
    mov $msg, %si
 1: lodsb
    or %al,%al # zero=end of str
    jz 2f   # get out
    mov $0x0E, %ah
    int $0x10
    jmp 1b
 
 2:
    jmp 2b
 
 msg: .asciz "Welcome to www.iakovlev.org\n" 
    .org 510
    .word 0xAA55   
 
 Копиляция :   
 as step2.s -o step2.o
 ld --oformat binary --Ttext 0x0 -o step2.bin step2.o
     
 
Далее - step3.s - попробуем вызвать подпрограмму внутри загрузчика :

 .code16
 .text
 
 mov $0x07C0, %ax
 mov %ax, %ds
 
 mov $msg, %si
 call Print
 
 1:
 jmp 1b
 
 # Prints a NULL-terminated string
 # INPUT: SI=String
 # OUTPUT: None
 # CLOBBERS: SI, AX, BX, FLAGS
 Print:
         mov $0x0E, %ah
         xor %bx, %bx
 1:
         lodsb
         or %al, %al
         jz 2f
 
         # INT 0x10 VIDEO TELETYPE
         # AH=0xE AL=Character [BH=VideoPage BL=Attribute]
         int $0x10
         jmp 1b
 2:
         ret
 
 msg: .asciz "Welcome to www.iakovlev.org\n"
 .org 510
 .word 0xAA55
 
 Компиляция :
 	as step3.s -o step3.o
 	ld --oformat binary --Ttext 0x0 -o step3.bin step3.o
 
 
Следующим вариантом будет использование макроса . 2 исходника - step4.s

 .code16
 .text
 
 .global _start
 _start:
 
 .include "our_macros.s"   
 
 1:
    jmp 1b
 
 
и our_macros.s :

 
 # Syntax is .macro MacroName Param1, Param2, Param3, ...
 # Alternative: .macro MacroName Param1 Param2 Param3 ...
 # You can also set defaults like .macro DoSomething Int1=3 Int2=6
 .macro BIOSPrint StringPointer
    mov $\StringPointer, %si
 1: lodsb
    or %al,%al
    jz 2f
    mov $0x0E, %ah
    int $0x10
    jmp 1b
 2:
 .endm
 
    mov $0x07C0, %ax
    mov %ax, %ds
 
    BIOSPrint msg
 
 2:
    jmp 2b
 
 msg: .asciz "Welcome to www.iakovlev.org\n"
    .org 510
    .word 0xAA55   
 
 компиляция - 
 	as step4.s -o step4.o
 	ld --oformat binary --Ttext 0x0 -o step4.bin step4.o
 
 
В следующем примере переключаемся в защищенный режим . Глобальная таблица дескрипторов - gdt - имеет размер 1 MB и расположена в памяти начиная с адреса 0x0 . После возвращения из защищенного режима в real mode ограничение на размер сираницы в 64 КВ уже недействительно.

 .code16
 .text
 
 ljmp $0, $Start
 Start:
 xor %ax, %ax
 mov %ax, %ds
 mov %ax, %ss
 mov $0x8200, %sp         # Differs from the original since we hardly need 7KBs of stack
 
 cli                      # Interrupts off, save current DS
 push %ds
 
 lgdt (gdtinfo)           # The brackets are just decoration, it works without them 
 			 # but you may find it more legible this way
 
 mov %cr0, %eax           # Enable protected mode
 or $1, %al
 mov %eax, %cr0
 
 mov $0x8, %ax            # Load selector 1, 32bit flat data
 mov %ax, %ds
 
 mov $0x0f01, %ax         # Use the segment to write to the screen
 mov %ax, %ds:(0xB8000)
 
 mov %cr0, %eax           # Shut off protected mode
 xor $1, %al
 mov %eax, %cr0
 
 pop %ds                  # Get the realmode segment back and enable interrupts
 sti
 
    mov $msg, %si
 10: lodsb
    or %al,%al # zero=end of str
    jz 1f   # get out
    mov $0x0E, %ah
    int $0x10
    jmp 10b
 
 1:
 jmp 1b
 
 # Description for the CPU to find the GDT
 gdtinfo: .word gdtend - gdt - 1
          .long gdt
 
 gdt:     .long 0         # Descriptor 0 is the NULL descriptor
          .long 0
 gdtkerneldata:           # 32bit Flat Data, 0-4GB Writable
         .word 0xFFFF
         .word 0
         .byte 0
         .byte 0x92
         .byte 0xCF
         .byte 0
 gdtend:
 
 .org 510
 .word 0xAA55
 
Несколько примеров вспомогательных функций:

 .code32
 # ------------------
 dochar:
     call cprint              # print one character
 sprint:
     lodsb                    # string char to AL
     or %al, %al
     jnz dochar               # else, we're done
     addb $1, (ypos)          # down one row
     movb $0, (xpos)          # back to left
     ret
 
 cprint:
     mov $0x0F, %ah             # attrib = white on black
     mov %eax, %ecx             # save char/attribute
     movzxb (ypos), %eax
     mov $160, %edx             # 2 bytes (char/attrib)
     mul %edx                   # for 80 columns
     movzxb (xpos), %ebx
     shl $1, %ebx               # times 2 to skip attrib
 
     mov $0xb8000, %edi         # start of video memory
     add %eax, %edi             # add y offset
     add %ebx, %edi             # add x offset
 
     mov %ecx, %eax             # restore char/attribute
     movw %ax, %ds:(%edi)
     addb $1, (xpos)            # advance to right
 
     ret
 
 # ------------------------------------
 
 printreg32:
     mov $outstr32, %edi
     mov (reg32), %eax
     mov $hexstr, %esi
     mov $8, %ecx               # eight nibbles
 
 hexloop:
     rol $4, %eax               # leftmost will
     mov %eax, %ebx             # become rightmost
     and $0x0f, %ebx
     mov (%esi, %ebx), %bl       # index into hexstr
     mov %bl, (%edi)
     inc %edi
     dec %ecx
     jnz hexloop
 
     mov $outstr32, %esi
     call sprint
 
     ret
 
 # ------------------------------------
 
 xpos: .byte 0
 ypos: .byte 0
 hexstr: .ascii "0123456789ABCDEF"
 outstr32: .asciz "00000000"    # register value
 reg32: .long 0                 # pass values to printreg32
 

 

 

 

 

 

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

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

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