📜 ⬆️ ⬇️

OS Development on Go + asm Part 0x01

Greetings,% username%.

Here is the second article from the cycle on OS development on Go + asm.

Part 0x00
Part 0x01
')
Initially, I planned that the second article will be about interrupt handling, but Go imposes its own corrections - a simple memory allocation will now be described and a part of Go runtime that will be useful for us, but will be rewritten. In fact, this is preparation for the third article - heap and finishing runtime.

There will be no code for this article on the githaba (I forgot to make a commit in time, but now it’s simply too lazy to restore it if someone sends a pull request - I’m grateful).

Nervous, please close the article - everything that can be written on Go will be written on it!

Remember in multiboot.s there were stub functions for the compiler? Transfer them to the runtime.s file (create it).

Step 0x00


First, let's learn how to allocate memory - create a memory.go file:

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


Let's get down to runtime. Create a runtime.go file

 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 //  } 

The reader, who has studied the comments to the previous article, will ask the question: “How is that, new () is __go_new, and not go.runtime.New?”

The answer is in the runtime.s file (remember I asked to transfer functions from multiboot.s to it?):

 ;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 

Now we bring the file kernel.go to the following form:

 package kernel import ( "memory" "screen" ) func Load() { memory.Init() screen.Init() screen.Clear() str := new(string) *str = "Habrahabr" screen.PrintStr(*str) } 

After compilation and launch we will see the inscription "Habrahabr".

WARNING: Actually, it’s not worthwhile to create vectors for slices, strings, and other types of variable lengths, otherwise overlap is possible.

_____________________________________

Discussion on osdev.ru:
http://osdev.ru/viewtopic.php?f=4&t=1100
Chat in slug (in golang-ru):
https://golang-ru.slack.com/messages/daria/

Source: https://habr.com/ru/post/259839/


All Articles