📜 ⬆️ ⬇️

OS Development on Go + asm Part 0x00

Good day% username%.

I wanted to pee something abnormal. The choice fell on the OS, in the end, each programmer must write his own OS, even if only a training one.

As some people know, I really like Go language well, and decided to try writing on it. What came out of it is under the habrakat.
')
Part 0x00
Part 0x01


Step 0x00


I will not write my bootloader; not for that, smart people came up with the multiboot spec.

First, let's write the initial load code (file 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 $ 


Now we will create a kernel.go file with the following content:
 package kernel func Load(){ //        } 


Create a file 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 = .; } 


and 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) $< 


Now, after running make in the project directory, you will get the kernel file, which can be loaded using qemu:

qemu-system-i386 -kernel ./kernel

The kernel will successfully boot and will not do anything)

Step 0x01



It is time to say hello to the world.

First, add the following lines to 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 


the __unsafe_get_addr function is needed so that we can convert uint32 to pointers inside Go

The other two functions are just plugs for the compiler.

Now create the screen.go file
 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]) } } 


Now we need to connect our screen module to the kernel - add import "screen" to kernel.go, and in the Load () function, we also write:
  screen.Init() screen.Clear() screen.PrintStr("Hello Habrahar!") 


Now we need to tell the compiler how to collect the whole thing; we will need to add the following lines to the Makefile:
 %.gox: %.go.o $(OBJCOPY) -j .go_export $< $@ 

And there, in the variable SOURCES between multiboot.o and kernel.go.o add screen.go.o and screen.gox

After all the manipulations, we invoke the make command and run qemu with our kernel. Wait for the download and enjoy

PS Please forgive me for typos, if any. I pledge to fix it.
PPS The other day the code will be posted on github
github.com/t0pep0/Daria/tree/Part0x00

_____________________________________

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


All Articles