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

от автора

Приветствую, %username%.

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

Part 0x00

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

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

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

Step 0x00

Для начала давайте научимся выделять память — создадим файл 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 += 0x1000 	} 	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 0x01

Приступим к рантайму. Создаем файл 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(string(*str)) } 

После компиляции и запуска мы увидим надпись «Habrahabr».

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

ссылка на оригинал статьи http://habrahabr.ru/post/259839/