LinuxBIOS

Co to jest LinuxBIOS?

Zadaniem LinuxBIOSa jest zastąpienie BIOSu dostarczonego przez producenta na płycie głównej. Normalny BIOS oraz program służący do konfiguracji urządzeń na płycie głównej jest zastępowany przez prostą sekwencję inicjującą oraz moduł rozpakowujący jądro Linuxa. Następnie jest uruchamiane jądro Linuxa i od tej pory proces bootowania przebiega już w normalny sposób. Dzięki wprowadzeniu takich zmian możliwe jest przejście do etapu montowania głównego systemu plików w czasie poniżej sekundy od momentu włączenia komputera.

LinuxBIOS w większości składa się z Linuxa, poprawionego, aby jądro było w stanie pracować na niezainicjalizowanym sprzęcie. Po wprowadzeniu dodatkowych poprawek do jądra używanego przez LinuxBIOS możliwe jest zbootowanie innego jądra (np. za pomocą mechanizmu LOBOS, omawianego poniżej), a także możliwe jest wykorzystanie protokołów sieciowych wspieranych przez Linuxa, aby pobrać właściwe jądro do zbootowania.

Dlaczego LinuxBIOS?

LinuxBIOS powstał głównie z myślą o dużych klastrach. Jego twórcy zauważyli mnóstwo ograniczeń związanych z BIOSami instalowanych przez producentów płyt głównych i postanowili usprawnić proces bootowania. Wady obecnie stosowanych programów bootujących to:

Kto z tego korzysta?

LinuxBIOS znalazł zastosowanie w wielu klastrach, oto ich lista (w kolejności powstawania):

Poza zastosowaniem w dużych klastrach, LinuxBIOS jest używany w małych urządzeniach dedykowanych jak routery, firewalle, stacjonarne odtwarzacze DivX, dekodery telewizji satelitarnej, itd. Zawędrował także na wojnę w Afganistanie i pomagał w przeszukiwaniu rumowiska po WTC wewnątrz robota wojskowego o nazwie PackBot.

Jak to działa?

Głównym założeniem twórców LinuxBIOSa jest to, aby ograniczyć do minimum kod inicjalizujący i pozwolić wszystko zrobić Linuxowi. Wszystkie procesory rodziny x86 uruchamiają się w trybie emulacji 8086 operując na 16-bitowych adresach i instrukcjach. Jest to problematyczne, ponieważ inicjalizacja niektórych urządzeń wymaga 32-bitowgo adresowania, np. ustawienie pamięci SDRAM wymaga zapisu do komórek, których adresacja wykracza daleko poza 16 bitów, co bardzo komplikuje sekwencje inicjalizacyjną. Dlatego twórcy LinuxBIOSa zdecydowali się od razu przejść do trybu 32-bitowego. Przejście jest dość proste: procesor musi załadować tablicę deskryptorów segmentów (Global Descriptor Table) i włączyć ochronę pamięci. Aby załadować GDT trzeba wywołać instrukcję LGDT podając wskaźnik do tablicy wewnątrz adresowalnej pamięci. To nie jest problem, ponieważ taką tablicę można umieścić w NVRAMie. Po przejściu do trybu 32-bitowego należy dokonać inicjalizacji układu. Kod w asemblerze jest potrzebny jedynie do uruchomienia DRAM, później można już wykorzystywać C, co pozwala na łatwą przenaszalność kodu.

Istnieje problem związany z tym, że LinuxBIOS działa na niezainicjowanym sprzęcie (nie można robić żadnych założeń związanych ze sprzętem), natomiast Linux zakłada, że sprzęt jest zainicjalizowany. Dlatego należy wprowadzić niewielkie zmiany do jądra. Na przykład Linux zakłada, że jeżeli kontroler dyskowy nie jest włączony, to dlatego że BIOS wyłączył go, natomiast na niezainicjalizowanym sprzęcie kontroler IDE nie jest włączony, bo nie ma BIOSu, który mógłby go włączyć. Dlatego to, że kontroler nie jest włączony ma zupełnie inne znaczenie i należy wprowadzić zmiany.

Pod LinuxBIOSem nie ma programu służącego do konfigurowania urządzeń na płycie głównej, jak to ma miejsce w przypadku zwykłego BIOSu. Ponieważ LinuxBIOS rozwijany jest z zgodnie z zasadą "pozwólmy to zrobić Linuxowi", dokonywana jest jedynie minimalna inicjalizacja sprzętu. Oznacza to, że jeżeli chcemy na przykład wyłączyć kartę sieciową zintegrowaną z płytą główną, wymaga to od nas poznania adresu pamięci (a dokładnie bitu), gdzie ustawia się tą opcję i ręcznego dodania instrukcji, która dokona odpowiedniej zmiany zawartości tej komórki pamięci.

Przejście do trybu chronionego

Ten krok wymaga 17 instrukcji w asemblerze, aby ustawić znany stan dla segmentacji, stronicowania i sprzętu TLB a następnie przejść do trybu 32-bitowego.

  1. Pierwsza instrukcją wykonywaną pod adresem 0xffff0 jest skok do początku procedur startowych BIOSu. Jest to standardowa procedura resetująca na platformie x86.
  2. Następne pięć instrukcji to: wyłączenie przerwań, wyczyszczenie TLB oraz ustawienie rejestrów kodu i danych na znane wartości.
  3. Teraz ładujemy wskaźnik do GDT.
  4. Następne cztery instrukcje odpowiadają za włączenie ochrony pamięci.
  5. Pozostałe instrukcje ustawiają rejestry segmentowe dla trybu chronionego.

Ustawienie DRAM

Ponieważ początkowo nie ma żadnego działającego sprzętu zarządzającego pamięcią, inicjalizacja odbywa się przy pomocy kodu asemblera. Jest to najbardziej złożona część procesu startowego i wymaga nawet kilkuset instrukcji, aby ją obsłużyć. Po włączeniu DRAM i ustawieniu stosu możliwe jest już korzystanie z kodu w języku C.

Ustawianie specyficznych dla płyty głównej parametrów

Przeprowadzenie dodatkowych ustawień jest realizowanie za pomocą kilkuset instrukcji w C.

Rozpakowywanie jądra i skok do niego

Kod rozpakowujący jądro jest zaczerpnięty bezpośrednio ze standardowego jądra Linuxa i wprowadzone są do niego niewielkie zmiany, aby możliwe było uruchomienie jego w środowisku ROMowym. Parametry startowe jądra są kopiowane pod znany adres w danej architekturze, po czym całe jądro jest rozpakowywane (pod adres 0x100000 dla PC). Następnie wywoływana jest funkcja startup_32, co powoduje pominięcie startowych procedur bootowalnych jądra, ponieważ tam dochodzi do rozpakowania dalszej części jądra, a to jest już zrobione przez LinuxBIOSa.

Co dalej?

Po wystartowaniu jądra Linuxa możliwe jest korzystanie ze wszystkich dobrodziejstw dostarczanych przez nie. Twórcy LinuxBIOSa opisują kilka możliwości: bootowanie przez sieć, standardowe, bezdyskowe a także czynności związane ze zdalnym zarządzaniem.

Bootowanie przez sieć przy użyciu Linuxa daje o wiele więcej możliwości niż w przypadku standardowych protokołów takich jak bootp czy PXE, ponieważ można użyć biblioteki SSH i nawiązywać połączenia do serwera DHCP przy jej pomocy. Dzięki temu połączenia są realizowane po protokole TCP, który nie jest tak zawodny, jak UDP (używany przez bootp i PXE). Serwer DHCP może wskazać węzłowi w klastrze, które jądro zbootować a nawet przesłać je.

Możliwe jest także wykorzystanie sieci opartych o pamięć o wysokiej przepustowości (np. SCI Scalable Coherent Interface) w celu przekopiowania jądra a nawet całego obrazu RAMDISK w ciągu kilku sekund.

LOBOS

Termin LOBOS oznacza Linux Os Boots OS. Jest to funkcja systemowa zezwalająca działającemu jądru zbootować nowe jądro bez opuszczania trybu 32-bitowego i bez użycia BIOSu. Dzięki temu można stosować Linuxa jako protokół bootowania sieciowego a nawet jako BIOS (co ma miejsce w przypadku LinuxBIOSa). Realizacja funkcji składa się z kilku kroków:

  1. Wczytanie nowego jądra do pamięci w inne miejsce niż aktualnie działające jądro.
  2. Przeniesienie krytycznych dla jądra struktur danych w bezpieczne miejsce. Są to struktury wspierające pamięć wirtualną, tablice GDT oraz parametry nowo bootowanego jądra.
  3. Wyłączenie przerwań.
  4. Przełączenie sprzętu kontrolującego pamięć wirtualną na nowe tablice.
  5. Skopiowanie kodu odpowiadającego za kopiowanie nowego jądra na stare w bezpieczne miejsce w pamięci, aby nie został zapisany.
  6. Skok do kodu kopiującego, który przenosi nowe jądro pod adres 0x100000, czyli w miejsce starego.
  7. Na koniec skok do funkcji startowej nowego jądra. W wyniku czego uzyskujemy efekt jakby to jądro zostało właśnie rozpakowane i uruchomione przez program ładujący.

Budowa w pełni funkcjonalnego BIOSu

LinuxBIOS w pełni obsługuje Linuxa i to właśnie jemu powierza dalszą inicjalizację sprzętu. Natomiast inne systemy operacyjne takie jak BSD i Windows 2000/XP nie mogą bezpośrednio działać z LinuxBIOSem, ponieważ polegają na kilku usługach dostarczanych przez zwykłe BIOSy. Właśnie z myślą o nich powstała inicjatywa łącząca elementy projektu LinuxBIOS, emulatora Bochs (Bochs jest darmowym, w pełni funkcjonalnym emulatorem komputera PC z procesorem x86, napisanym w C++, a co najważniejsze zawiera darmowy BIOS, działający na emulowanym sprzęcie) oraz dodatkowego oprogramowania w celu stworzenia pierwszego w pełni darmowego BIOSu dla PC.

Z projektu LinuxBIOS została zaczerpnięta część odpowiadająca za start systemu (bez jądra). Następnie została dodana warstwa pośrednia między LinuxBIOSem a Bochs BIOSem nazwana ADLO (ADhesive LOader), przekazująca parametry z LinuxBIOSa do Bochs BIOSa, a także zmieniająca działanie pewnych funkcji w Bochs BIOSie bez konieczności modyfikowania go. Do tego dołączony jest Bochs BIOS czyli BIOS z emulatora Bochs. Zawiera on funkcjonalność potrzebną do uruchomienia systemów takich jak Windows przy pomocy GNU Grub Bootloadera. Połączenie LinuxBIOSa, ADLO i Bochs BIOSa jest szybkie oraz nie zajmuje dużo miejsca.

Windows 2000
Grub Bootloader
Bochs BIOS
ADLO
LinuxBIOS

Wykorzystanie BIOSu z darmowego emulatora Bochs było możliwe dzięki temu, w jaki sposób został napisany. Po pierwsze żadne urządzenie emulowane przez Bochs nie zostało zaimplementowane w warstwie BIOSu. BIOS został napisany tak jakby miał działać na prawdziwym sprzęcie. Po drugie skoro sprzęt w jest emulowany, to nie musi być włączony, a więc nie trzeba go inicjalizować (np. nie inicjalizuje pamięci podręcznej procesora, czy też kontrolera IDE). A tym właśnie zajmuje się LinuxBIOS. Jedyne zmiany wprowadzone do Bochs BIOSa polegały na bardziej prawidłowym zaimplementowaniu niektórych przerwań, co nie złamało jego kompatybilności z emulatorem.

Usługi BIOSu wymagane przez systemy operacyjne do poprawnej pracy są liczne. Większość z nich jest trywialna jak na przykład szukanie klawiatury czy korzystanie z przerwania zegarowego. Główne cztery usługi potrzebne to: funkcje ekranu, dysku twardego, pamięci oraz tabeli PCI. Wykorzystywane są także nowe usługi jednak są one opcjonalne (np. ACPI).

Usługa BIOSu Standardowe
jądro 2.4.x
Zmienione
jądro 2.4.x
Windows
2000/XP
FreeBSD
Przerwanie Int13 tak nie tak tak
Przerwanie Int15 tak nie tak tak
PnPBIOS nie nie nie nie
Tablica PCI tak tak tak tak
Video BIOS nie nie nie nie
ACPI nie nie nie nie
APM nie nie nie nie

Funkcje BIOSu zajmujące się ekranem można pobrać z pamięci, spod adresu 0xC0000 (do 0xC7FFF) w pamięci. Tam właśnie znajduje się BIOS producenta karty graficznej. Po przekopiowaniu go umieszczony jest on razem z LinuxBIOSem i Bochs BIOSem w EEPROMie. Dzięki temu mamy pełne wsparcie dla przerwania Int10. W celu realizacji przerwania Int13, czyli dostępu do dysku używana jest implementacja zawarta w Bochs BIOSie, dostosowana do prawdziwego sprzętu, jednak wszelkie zmiany wprowadzone są za pomocą ADLO. Wsparcie dla zwracania informacji o obszarach pamięci oraz tablicach PCI zawarte jest w Bochs BIOSie.

Proces bootowania odbywa się w następujący sposób:

  1. Uruchomienie LinuxBIOSa, który dokonuje minimalnej koniecznej inicjalizacji sprzętu
  2. LinuxBIOS po zakończeniu inicjalizacji uruchamia następny program znajdujący się w ROMie. Standardowo jest to jądro Linuxa, jednak w tym przypadku będzie top ADLO
  3. ADLO sprawdza czy Bochs BIOS i VGA BIOS jest we właściwym miejscu, a następnie kopiuje Bochs BIOSa do "Shadow RAMu", czyli do pamięci rozszerzonej, dla której przydzielony jest ten sam adres co dla ROMu. Oryginalny ROM jest następnie wyłączany, a nowe miejsce w pamięci zyskuje ochronę przez zapisem.
  4. Ponieważ LinuxBIOS zapisuje mapę pamięci oraz tablicę mapowania przerwań IRQ w postaci w formacie niezgodnym z tym, w jakim one są zapisywane w komercyjnych BIOSach, ADLO konwertuje je do postaci rozumianej przez Bochs BIOS.
  5. Następnie ADLO zapisuje parametry konfiguracyjne w CMOSie.
  6. Kolejnym etapem jest uruchomienie Bochs BIOSa, którego zadaniem jest ustawienie wektora przerwań. Ten wektor zawiera jednak już podmienioną funkcję obsługi przerwania Int13, do czego doszło w trakcie wczytywania BIOSu przez ADLO z ROMu.
  7. Na koniec jest uruchamiany program ładujący tj. Grub czy Lilo.

Moje boje z LinuxBIOSem

Niestety projekt LinuxBIOS jest bardzo słabo udokumentowany i jedynie dostępne są szczątkowne informacje dotyczące jego stanu i sposobu korzystania z niego. Na szczęście w sieci można znaleźć kilka artykułów opisujących w dokładny sposób, krok po kroku, w jaki sposób zainstalować LinuxBIOSa na własnej płycie głównej. Zachęcony tym faktem stwierdziłem, że też spróbuję tego dokonać.

Pierwszą rzeczą, którą należy sprawdzić, jest to czy płyta, na której chce się zainstalować LinuxBIOS, jest wspierana. Do testów przeznaczyłem dwie płyty Asus P2B oraz Asus P3B-F, obie oparte o chipset Intel 440BX. Okazało się, że żadna nie jest bezpośrednio wspierana, jednak istniał szablon ustawień dla płyt opartych na BXie, a także kilka gotowych konfiguracji pod podobne płyty innych producentów. Uznałem to jako dobry znak i postanowiłem podjąć dalsze działania.

Drugi etap polega na zakupie chipowej pamięci flaszowej o zwiększonej pojemności oraz podstawki ZIF (Zero Insert Force). Potrzeba kości o większej pojemności bierze się stąd, że normalne BIOSy zajmują zazwyczaj 256 KB natomiast LinuxBIOS wraz z kernelem może zajmować nawet prawie półtora megabajta. Dlatego konieczny jest zakup tak zwanego DoC'a (Disc-On-Chip), czyli silikonowego dysku w postaci normalnej kości flaszowej, firmy M-Systems. Pojemności tych kości zaczynają się od 8 MB (czyli 32 razy więcej niż normalna kość mieszcząca BIOS) aż do 1 GB!!! Po drobnych poszukiwaniach okazało się, że jest w Polsce jeden dystrybutor firmy M-Systems. Niestety nie miał tych pamięci w magazynie, ale sprowadził je dla mnie w zaledwie 4 tygodnie... Drugą potrzebną częścią jest podstawka ZIF. Jest to podstawka wtykana w normalne złącze BIOSu na płycie głównej, umożliwiająca łatwe podmienianie wetkniętej w nią kości. Wykorzystuje się ją w fazie programowania pamięci flaszowej (gdzie przy włączonym prądzie podmieniamy kość BIOSu), aby uniknąć zakupu drogiego urządzenia zewnętrznego.

Uzbrojony we wszystkie potrzebne części przystąpiłem do działania. Zainstalowałem na płycie głównej podstawkę ZIF (nie było łatwo, bo nie mieściła się z powodu slotu PCI znajdującego się w pobliżu, ale się udało). Dodałem do aktualnie działającego jądra obsługę dla urządzenia DoC i przystąpiłem do budowania LinuxBIOSa. Po przeprowadzeniu odpowiednich zmian konfiguracyjnych, dostosowałem pakiet do mojej płyty głównej oraz zbudowałem nowe jądro, które miało być umieszczone razem z LinuxBIOSem w pamięci flaszowej, a następnie wywołałem program flaszujący. Tu spotkało mnie pierwsze rozczarowanie. Nie zadziałał, a raczej zadziałał tylko nie do końca. Zapisał do DoCa linuxbiosa oraz kernel, ale nie zapisał jednego pliku na samym początku pamięci flaszowej, bo go nie było. Okazało się, że plik ten istnieje tylko dla nielicznych chipsetów (nie dla BX) i odpowiada za start LinuxBIOSa z Disk-On-Chipa (tego dowiedziałem się po przejrzeniu listy dyskusyjnej poświęconej LinuxBIOSowi). Bez niego nie jest możliwe korzystanie z powiększonej pamięci flaszowej, czyli na przykład można używać LiunuxBIOSa do załadowania programu w formacie elf, jak np. etherboot czy memtest86, który pomieści się w normalnej kości EEPROM. Także na płycie Asus P3B-F okazało się, że nie działa standardowa metoda odblokowywania zabezpieczenia przed zapisem dla gniazda BIOSu.

Po krótkich testach, które okazały się być bardzo kształcące, doszedłem do następujących wniosków na temat LinuxBIOSa:

Bibliografia

  1. Strona projektu LinuxBIOS http://www.linuxbios.org
  2. Ron Minnich, James Hendricks, Dale Webster, "The LinuxBIOS" in Preceedings of the 4th Annual Linux Showcase & Conference, Atlanta, October 2000
  3. Ron Minnich, "LOBOS: (Linux OS Boots OS) Booting a kernel in 32-bit mode" in Preceedings of the 4th Annual Linux Showcase & Conference, Atlanta, October 2000
  4. A. Agnew, A. Sulmicki, R. Minnich, W. A. Arbaugh, "Flexibility in ROM: A Stackable Open Source BIOS" in Proceedings of the FREENIX Track 2003 Usenix Annual Technical Conference, San Antonio, TX, pp. 115 - 124, June 2003 http://www.missl.cs.umd.edu/Projects/sebos/usenix03.pdf
  5. Antony Stone, "The LinuxBIOS project, Putting Linux on your motherboard" in Linux Magazine, www.linux-magazine.com, pp. 76-80,March 2003