Linux Aygıt Sürücüleri için Güvenli Port Ayırtılması

ArticleCategory:

KernelCorner

AuthorImage:

[Yazarın Fotografı]

TranslationInfo:

original in en Dr. B. Thangaraju

en to tr Özden Akıncı

en to tr Onan Görel

AboutTheAuthor:

Dr. B. Thangaraju, Bharathidasan Üniversitesi'nden fizik üzerine Ph.D aldı ve Hindistan'da, Hindistan Bilim Enstitüsü'nde beş yıl araştırma asistanı olarak çalıştı. Araştırmalarını Saydam ve İletken Oksit (TCO) ince filmler, Spray Pyrolysis, Işık Akustik Teknikleri ve Chalcogenide Camların p- den n- doğru değişimi üzerine yaptı. Uluslar arası üne sahip dergilerde 10 tane araştırma yazısı bulunmaktadır. Ayrıca yapmış olduğu araştırmaların sonuçlarını yediden fazla ulusal ve uluslar arası konferanslarda sunmuştur. Halen Hindistan'da Wipro Technologies'te Talent Transformation'da müdür olarak çalışmaktadır.

Halen Hindistan'da Wipro Technologies'te Talent Transformation'da müdür olarak çalışmaktadır. Araştırma alanı ise, Linux Çekirdeği, Aygıt Sürücüleri, Gerçek Zamanlı Linux'tur.

Abstract:

Bir aygıt sürücüsü yazmak zorlu ve maceralı bir iştir. Bir defa aygıt, sürücünün init_module()'ü içinde yer almalıdır. Yani aygıtla ilgili kaynaklar burada bulunmalıdır. Bir aygıt için en önemli kaynaklardan biri I/O portudur. Dinamik olarak bağlanan sürücülerde, sürücüyü geliştiren kişi aygıt üzerinde kullanılmayacak port aralıklarını yerleştirirken dikkatli olmalıdır. İlk olarak, sürücü hangi port aralıklarını kullanacak ve hangileri boş olacak tespit edilmelidir. Daha sonra bu portlar aygıt için talep edilmelidir. Modül kernelden çıkarıldığı zaman, portların boş olmaları gerekir. Bu makalede linux aygıtları üzerinde güvenli portların yerleştirilmesinde yaşanan karışıklıklar tartışılacaktır.

ArticleIllustration

[Illustration]

ArticleBody:

Giriş

Kaynaklar; I/O portları, hafıza ve IRQ hatlarıdır. Bu makalede I/O alt sistemlerinin temelleri ve I/O portları ile bağlantılı kaynakların paylaşımının önemi anlatılacaktır. Ayrıca, makalede aygıttan nasıl port adresi alınacağı ve port adresinin nasıl boş bırakılacağı da açıklanacaktır.

Portlar, buslar ve aygıt kontrolörleri gibi basit donanım elemanları bir çeşit I/O aygıtlarıdır. Aygıt sürücüleri, sisteme işletim sistemi ve uygulamalar arasında standart bir arayüzü sağlamakla birlikte; bir aygıtın I/O alt sistemin arayüzüne erişimini de gösterir. Bilgisayara eklenen pek çok çeşitte aygıt mevcuttur. Örneğin; depolama aygıtları (diskler, teypleri CD-ROM ve disketler), insan arayüz cihazları (klavye, fare ve ekran), ver iletim cihazları (modemler ve ağ kartları). Çok fazla çeşitte I/O aygıtları olmasına rağmen, bizim sadece aygıtların nasıl bağlandıklarını ve yazılımın, donanımı nasıl kontrol edebildiğini anlamamız yeterli olacaktır.

Temel Kavram

Bir aygıt elektronik bölüm ve mekanik bölüm olmak üzere iki kısımdan oluşmaktadır. Elektronik bölüme aygıt kontrolörü denir. Kontrolör, sisteme sistem hattı (bus) aracılığı ile bağlanır. Tipik olarak, çakışmayan port adreslerinin ayarlanmaları her bir kontrolöre bağlanarak yapılır. I/O portları durum, kontrol, veri girişi ve veri çıkışı diye adlandırılan dört parça kayıttan oluşmaktadır. Durum kaydı host tarafından okunabilen bitlerden oluşur. Kontrol kaydı ise, host tarafından bir komuta başlamak için veya aygıtın modunu değiştirmek için yazılır. Ayrıca, veri girişi kaydı veri almak için ve veri çıkışı kaydı da sistem dışına veri yollamak için kullanılır.

İşlemci ve bir aygıt arasındaki temel arayüz kontrol ve durum kayıtlarıyla belirlenir. İşlemci bir program çalıştırdığı ve aygıtla ilgili bir hesaplamaya geçtiği zaman, aygıta uygun komutları çalıştırır. Kontrolör istenilen davranışların kontrolünü yapar ve sonra durum kaydı içindeki uygun bitleri ayarlar ve bekler. Operasyonun bitimine kadar aygıtın durumunun belli aralıklarla kontrol edilmesi işlemcinin sorumluluğundadır. Mesela, yazıcı tarafından kullanılan paralel port sürücüsü, normalde yazıcının çıktı kabul edip edemeyeceğine bakar. Eğer yazıcı hazır değilse sürücü bir süreliğine uyur ( bu süre içersinde işlemci bazı işe yarar işlemler yapabilir). Daha sonra sürücü, yazıcı hazır olana kadar tekrar dener. Bu işlemler sistemin performansını arttıracaktır. Aksi taktirde sistem, hiçbir şey yapmadan bekleyecektir.

Kayıtlar I/O uzayında belirli adreslere sahiptirler. Bu adresler genellikle sistemin açınım zamanında (boot time) belirlenirler. Açınım zamanında konfigürasyon dosyası içerisinde bulunan parametreler kullanılarak sistem inşa edilir. Herhangi bir aygıt eklenmesi ihtimaline karşı, her bir aygıt için belli adres aralıkları ayrılmış olmalıdır. Yani, çekirdek var olan aygıtlar için sürücüler içerir. Aygıt için ayrılmış olan I/O aralıkları proc dizini altında saklanır. Sisteminizde mevcut olan aygıtların port adres aralıklarını $cat /proc/interrupts. Çıktının ilk sütunu port aralığını, ikinci sütunu ise aygıtın sahip olduğu portu gösterir. İşletim sistemlerinin bazıları, sistem çalıştığında aygıt sürücü modüllerini dinamik olarak yükleme özelliğine sahiptirler. Yani sistem çalışırken, sisteme herhangi bir yeni aygıt eklenebilir ve dinamik olarak eklenen bu aygıt, aygıt sürücü modülü tarafından kontrol edilip sisteme kabul edilebilir.

Aygıt sürücüsü kavramı oldukça karmaşık ve soyut bir kavramdır. Bu bilgisayarın yazılımı üzerinde en alt seviyede çalışır. Çünkü aygıt sürücü kavramı tamamen aygıtın donanım özelliklerine bağlıdır. Tüm aygıt sürücüleri basit aygıt türlerine göre düzenlenmiştir. Bu türler karakter, blok veya ağ (network) olabilirler. Eğer herhangi bir uygulama bir aygıta ihtiyaç duyuyorsa; çekirdek uygun aygıt sürücüsüyle iletişime geçer. Sonra, sürücü uygun komutları aygıt üzerinde uygular. Aslında aygıt sürücüsü bir çeşit fonksiyonlar serisidir. Bu fonksiyonlar aç, kapat, oku, yaz, ioctl ve llseek gibi kavramları içerirler. Modülünüzü yerleştirdiğiniz zaman, init_module( ) fonksiyonu çağrılır ve modül çıkarıldığı zaman cleanup_module( ) fonksiyonu çağrılır. Aygıt kaydı, init_module( ) içerisindeki aygıt sürücüne yapılır.

Aygıt init_module( ) içerisine kaydedildikten sonra I/O portları, hafıza ve IRQ satırları gibi aygıt kaynakları da kendiliğinden init_module( ) 'e yerleşirler. Bunlar sürücünün aygıtı düzgün çalıştırması için gereklidir. Eğer aygıt için yanlış bir hafıza adresi atarsanız; çekirdek segmentation fault şeklinde hata mesajı verecektir. I/O portlarında yapılan herhangi bir hatada ise, çekirdek tarafından wrong I/O port şeklinde bir mesaj verilmeyecektir ve bu yanlış port atanması sonucunda sistemde var olan aygıtlar arasında bir çakışmaya neden olacaktır. Modulü çıkardığınız zaman, ana numaralar serbest kalır ve aygıt kaydı silinir. Böylece cleanup_module( ) fonksiyonu ile kaynaklar boş kalır.

Aygıt sürücülerinin en sık yaptıkları iş ise I/O portlarını okumak ve yazmaktır. Bundan dolayı, sürücünüz port adreslerinin uygun aygıt tarafından kullanıldığından emin olması gerekir. Sürücü bundan emin olmak için önce hangi adreslerin kullanılıp kullanılmadığını belirler: sürücü kullanımda olmayan adresleri bulur ve çekirdekten aygıtı için uygun adres aralığını belirlemesini ister.

Güvenli Port Tahsisindeki Açıklar

Şimdi,kernel fonksiyonlarıyla kaynak tahsisatının nasıl yapıldığını ve daha sonra kaynakların nasıl serbest hale getirildiğini göreceğiz. Bu örnek yaklaşım Linux 2.4 kernel ile uygulanmıştır.Bundan dolayı,bütün yürütme sadece Linux işletim sistemi ve bazı Unix işletim sistemi çeşitlerinde uygulanabilir.

İlk önce,aygıt için uygun port aralığının olup olmadığı aşağıdaki kod ile araştırılır.

int check_region (unsigned long start, unsigned long len);

Bu fonksiyonun geri dönüş değeri istenen port adres aralığı boş ise sıfırdır. Portlar kullanımda ise geri dönüş değeri sıfırdan küçük bir değer veya negatif hata kodudur.( -EBUSY or -EINVAL). Fonksiyon iki argüman alır: start ardışıl bölgenin başlangıcı (veya I/O port aralığının başlangıcı) ve len aralıktaki port sayısını gösterir.

Port kullanılabilir ise bir defada request_region fonksiyonu hem kontrol hem tahsisat aynı anda yapılabilir.

struct resource *request_region (unsigned long start, unsigned long len, char *name);

İlk iki argüman önceki fonksiyonda gördüğümüz argümanların aynısı, character türünden gösterici olan name ise belirtilen port adresinin tahsis edileceği aygıtın ismidir. Bu fonksiyonun geri döniş değeri struct resource türünden göstericidir. Resource structure <linux/ioport.h> içinde tanımlanmıştır ve kaynak aralığını tanımlamak için kullanılır. Bu yapı aşağıdaki formatı içerir:
 
struct resource { 
        const char *name;  
        unsigned long start, end;  
        unsigned long flags; 
        struct resource *parent, *sibiling, *child;   
};  
Modül kernelden kaldırıldığı zaman,önceden kullanılan port diğer aygıtları kullanımı için boşaltılmalıdır.Bunun içinde cleanup_module ( ) modülü içindeki release_region ( ) fonksiyonu kullanılır. Bu fonksiyonun yazılımı:

void release_region ( unsigned long start, unsigned long len);

İki argümanın açıklaması önceki fonksiyonlardaki gibidir. Yukarıda açıklanan üç fonksiyon içinde tanımlanmış makrolardır.

Aygıt Port Tahsisi için Sürücü Program Örneği

Aşağıdaki örnek program dinamik olarak yüklenen aygıt için gereken port tahsisatının yapılmasını açıklamaktadır.
#include <linux/fs.h.>
#include <linux/ioport.h.>

struct file_operations fops;
unsigned long start, len;

int init_module (void) 
{
 int status;
 start = 0xff90; 
 len   = 0x90;

 register_chrdev(254,"your_device",&fops);

 status =  check_region (start, len);
 if (status == 0) { 
     printk ("The ports are availave in that range\n");   
     request_region(start,len,"your_device");
 } else {
     printk ("The ports are already in use. Try other range.\n");
     return (status);
 }
 return 0;
}

void cleanup_module (void) 
{
 release_region(start, len);
 printk ("ports are freed successfully\n");
 unregister_chrdev(254,"your_device");}
 printk (" your device is unregistered\n"); 
}

Bu örnekte,karışılık olmaması için,hata kontrolu ve dinamik tahsisat sayısı kontrolünden sakınılmıştır.Sadece gereken port başarılı bir şekilde tahsis edilmiştir, bu işlemde proc dizininden kontrol edilebilir:
$cat /proc/ioports

Sürücü için Kernel I/O Port Fonksiyon Seçenekleri

Linux port boyutuna bağlı olan ve I/O portlarından okuma ve I/O portlarına yazmayı sağlayan birçok fonksiyonu destekler. Port genişliği 8,16 veya 32 bit genişliğinde olabilir.Linux kernel <asm/io.h> başlığı I/O portlarına girişleri sağlayan inline fonksiyonları belirler. Okuma(inx) ve yazma(outx) için 8 bit,16 bit ve 32 bit portlar ile aşağıdaki fonksiyonlar kullanılır:



__u8 inb (unsigned int port);
void outb (__u8 data, unsigned int port);

__u16 inw (unsigned int port);
void outw(__u16 data, unsigned int port);

__u32 inl (unsigned int prot);
void outl (__u32 data, unsigned int port);


string versiyonu için birden fazla veriyi herhengi bir anda etkin bir şekilde transfer etmek için aşağıdaki fonksiyonlar kullanılabilir.


void insb(unsigned int port, void *addr, unsigned long count);
void outsb(unsigned int port, void *addr, unsigned long count);


addr transfer yapılacak veya transferin yapılacağı alanın hafızadaki yerini gösterir.count ise transfer edilecek birimin sayısını gösterir.Veri tek porttan tek porta yazılır veya okunur.


void insb(unsigned int port, void *addr, unsigned long count);
void outsb(unsigned int port, void *addr, unsigned long count);

16 bitlik değerleri 16 bitlik tek porta yazma veya porttan okuma.


void insb(unsigned int port, void *addr, unsigned long count);
void outsb(unsigned int port, void *addr, unsigned long count);

32 bitlik değerleri 32 bitlik tek porta yazma veya porttan okuma.

Bilgilendirme

Yazar, yazının gözden geçirmesinden dolayı Talent Transformation, Wipro Technologies, India Mr. Jayasurya V teşekkür eder.

Referanslar