Разработка 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».
ПРЕДУПРЕЖДЕНИЕ: На самом деле пока не стоит создавать вектора на слайсы, строки и другие типы переменной длинны иначе возможно наложение.