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

Приветствую, %username%.Перед вами вторая статья из цикла о разработке ОС на Go + asm.

Part 0×00Part 0×01

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

Слабонервным просьба закрыть статью — все, что может быть написанно на Go будет написанно на нем!

Помните в multiboot.s были функции заглушки для компилятора? Перенесите их в файл runtime.s (создайте его)

Step 0×00

Для начала давайте научимся выделять память — создадим файл memory.go

package memory

//extern end var end uint32 //берется из link.ld

var placement_address uint32 //Текущий адрес

//extern __unsafe_get_addr func pointer2uint32(pointer *uint32) uint32 //Все как в прошлый раз, разве что тип аргумента изменился, но это не существенно

func Init () { placement_address = pointer2uint32(&end) //получаем начальный адресс }

func kmalloc (size uint32, align int, phys *uint32) uint32 { //выделение памяти if align == 1 && (placement_address&0xFFFFF000) != uint32(0) { //Если адрес не выровнен по границе — то выравниваем. placement_address &= 0xFFFFF000 placement_address += 0×1000 } if phys!= nil { //если необходимо — возвращаем физический адрес *phys = placement_address } res:= placement_address //возвращаем текущий адрес placement_address += size //изменяем текущий адрес return res }

func Kmalloc (size uint32) uint32 { //Простое выделение памяти return kmalloc (size, 0, nil) } Step 0×01

Приступим к рантайму. Создаем файл runtime.go

package runtime

import ( «memory» )

//extern __unsafe_get_addr func pointer2byteSlice (ptr uint32) *[]byte

//extern __unsafe_get_addr func pointer2uint32(ptr interface{}) uint32

func memset (buf_ptr uint32, value byte, count uint32) { //Примитивная реализация memset var buf *[]byte buf = pointer2byteSlice (buf_ptr) for i:= uint32(0); i < count; i++ { (*buf)[i] = value } }

func memcpy (dst, src uint32, size uint32) { //Так же примитивная реализация memcpy var dest, source *[]byte dest = pointer2byteSlice (dst) source = pointer2byteSlice (src) for i:= uint32(0); i < size; i++ { (*dest)[i] = (*source)[i] } }

func New (typeDescriptor uint32, size uint32) uint32 { //А вот и будущая new () //На данном этапе typeDescriptor не нужен buf_ptr:= memory.Kmalloc (size) //Выделяем память memset (buf_ptr, 0, size) //забиваем выделенную память нулями return buf_ptr //Возращаем указатель } Читатель, изучивший коментарии к преведущей статье задаст вопрос: «Как же так, new () это __go_new, а не go.runtime.New?»

Ответ в файле runtime.s (Помните я просил перенести функции из multiboot.s в него?)

; gccgo compability global __go_runtime_error global __go_register_gc_roots

__go_register_gc_roots: __go_runtime_error: ret

global __unsafe_get_addr; convert uint32 to pointer

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

extern go.runtime.New

global __go_new global __go_new_nopointers

__go_new: ; прокидываем go.runtime.New как __go_new __go_new_nopointers: call go.runtime.New ret Теперь приведем файл kernel.go к следующему виду:

package kernel

import ( «memory» «screen» )

func Load () { memory.Init () screen.Init () screen.Clear () str:= new (string) *str = «Habrahabr» screen.PrintStr (*str) } После компиляции и запуска мы увидим надпись «Habrahabr».

ПРЕДУПРЕЖДЕНИЕ: На самом деле пока не стоит создавать вектора на слайсы, строки и другие типы переменной длинны иначе возможно наложение.

© Habrahabr.ru