...

суббота, 6 июня 2015 г.

Разработка OS на Go+asm Part 0x00

Доброго времени суток %username%.

Захотелось мне пописать что-то ненормальное. Выбор пал на ОС, в конце-концов каждый программист должен написать свою ОС, пусть хотя бы учебную.

Как некоторым известно, я очень люблю язык Go ну, и решил попробовать написать на нем. Что из этого получилось — под хабракатом.


Писать свой загрузчик я не буду, не для того умные люди придумывали спеку multiboot.

Для начала напишем код первоначальной загрузки (файл multiboot.s)

MBOOT_PAGE_ALIGN    equ 1<<0
MBOOT_MEM_INFO      equ 1<<1
MBOOT_HEADER_MAGIC  equ 0x1BADB002
MBOOT_HEADER_FLAGS  equ MBOOT_PAGE_ALIGN | MBOOT_MEM_INFO
MBOOT_CHECKSUM      equ -(MBOOT_HEADER_MAGIC + MBOOT_HEADER_FLAGS)

[BITS 32]

[GLOBAL mboot]
[EXTERN code]
[EXTERN bss]
[EXTERN end]

mboot:
  dd    MBOOT_HEADER_MAGIC
  dd    MBOOT_HEADER_FLAGS
  dd    MBOOT_CHECKSUM
  dd    mboot
  dd    code
  dd    bss
  dd    end
  dd    start

[GLOBAL start]
extern go.kernel.Load ;Указываем на то, что у нас есть внешняя функция на Go

start:
  push  ebx
  cli
  call  go.kernel.Load ;Вызываем внешнюю функцию, которая содержит основной код ядра
  jmp   $

теперь создадим файл kernel.go следующего содержания:

package kernel

function Load(){
//Как видим наше ядро пока ничего не делает
}

Создадим файл link.ld

ENTRY(start)
SECTIONS
{

    .text 0x100000 :
    {
        code = .; _code = .; __code = .;
        *(.text)
        . = ALIGN(4096);
    }

    .data :
    {
        data = .; _data = .; __data = .;
        *(.data)
        *(.rodata)
        . = ALIGN(4096);
    }

    .bss :
    {
        bss = .; _bss = .; __bss = .;
        *(.bss)
        . = ALIGN(4096);
    }

    end = .; _end = .; __end = .;
}

и Makefile

SOURCES=multiboot.o kernel.go.o

GOFLAGS= -nostdlib -nostdinc -fno-stack-protector -fno-split-stack -static -m32 -g -I.
GO=gccgo
ASFLAGS= -felf
NASM= nasm $(ASFLAGS)
OBJCOPY=objcopy

LDFLAGS=-T link.ld -m elf_i386
 

all: $(SOURCES) link

clean: 
        rm *.o  kernel 

link:
        ld $(LDFLAGS) -o kernel $(SOURCES)


%.go.o: %.go
        $(GO)   $(GOFLAGS) -o $@ -c $<

%.o: %.s
        $(NASM) $<

Теперь, выполнив make в дирректории проекта вы получите на выходе файл kernel, который можно загрузить с помощью qemu:

qemu-system-i386 -kernel ./kernel

Ядро успешно загрузится и ничего не будет делать )

Настало время поздороваться с миром.

Для начала добавим в multiboot.s следующие строки:

global __go_runtime_error
global __go_register_gc_roots
global __unsafe_get_addr

__unsafe_get_addr:
  push ebp
  mov ebp, esp
  mov eax, [ebp+8]
  mov esp, ebp
  pop ebp
  ret

__go_register_gc_roots:
__go_runtime_error:
  ret

функция __unsafe_get_addr нужна для того, что бы мы могли конвертировать uint32 в указатели внутри Go

Другие две функции — просто затычки для компилятора

Теперь создадим файл screen.go

package screen

var (
        frameBuffer      *[totalMax]uint16 //Старшие 8 бит - символ, младшие - его атрибуты
        cursorX, cursorY uint8
)

const (
        frameBufferAddr = 0xB8000
        maxX            = 80
        maxY            = 25
        totalMax        = maxX * maxY
        whiteOnBlack    = 0x07
)
//Ниже мы создаем обертку для Go над нашей функцией __unsafe_get_addr
//extern __unsafe_get_addr
func getAddr(addr uint32) *[totalMax]uint16

func Init() {
        cursorX = 0
        cursorY = 0
        frameBuffer = getAddr(frameBufferAddr) //Получаем доступ к видеобуферу
}

//Очистка экрана, просто заполняем весь видеобуфер нулями
func Clear() {
        for i := 0; i < totalMax; i++ {
                frameBuffer[i] = 0
        }
        cursorX = 0
        cursorY = 0
}

//Меняем позицию курсора
func SetCursor(x, y uint8) {
        cursorX = x
        cursorY = y
}

//Скроллим экран если он заполнен
func scroll() {
        if cursorY >= maxY { 
                for i := 0; i < 24*maxX; i++ {
                        frameBuffer[i] = frameBuffer[i+80] //Смещаем все строки на одну вверх
                }
                for i := 24 * 80; i < totalMax; i++ {
                        //Очищаем нижнюю строку
                        frameBuffer[i] = 0x20 | (((0 << 4) | (15 & 0x0F)) << 8)
                        frameBuffer[i] = 0
                }
                cursorY = 24
                cursorX = 0
        }
}

//Вывод символов
func putChar(c byte) {
        switch c {
        case 0x08: //backspace
                if cursorX > 0 {
                        cursorX--
                }
        case 0x09: //tab
                cursorX = (cursorX + 8) & (8 - 1)
        case '\r': //return
                cursorX = 0
        case '\n': //new line
                cursorX = 0
                cursorY++
        default: 
                if c >= 0x20 { //Все печатные символы
                        frameBuffer[cursorY*80+cursorX] = uint16(c) | (((0 << 4) | (15 & 0x0F)) << 8)
                        cursorX++
                }
        }
        if cursorX >= 80 { //Если надо перемещаем курсор
                cursorX = 0
                cursorY++
        }
        scroll()
}

//Выводим строку
func PrintStr(s string) {
        for i := 0; i < len(s); i++ {
                putChar(s[i])
        }
}

Теперь надо подключить наш модуль screen к ядру — в kernel.go добавляем import «screen», там же, в функци Load() пишем:

  screen.Init()
        screen.Clear()
        screen.PrintStr("Hello Habrahar!")

Теперь надо указать компилятору как все это дело собирать нам понадобится добавить в Makefile следующие строки:

%.gox: %.go.o
                $(OBJCOPY) -j .go_export $< $@


И там же, в переменную SOURCES между multiboot.o и kernel.go.o добавить screen.go.o и screen.gox

После проведения всех манипуляций вызываем команду make и запускаем qemu с нашим ядром. Дожидаемся загрузки и радуемся

P.S. Прошу простить меня за опечатки, если они есть. Обязуюсь исправить.
P.P.S. На днях код будет выложен на github

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

This entry passed through the Full-Text RSS service - if this is your content and you're reading it on someone else's site, please read the FAQ at http://ift.tt/jcXqJW.

Комментариев нет:

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