From 80f7980e74701bf8c96234183118c7a8044585cb Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Thu, 30 Mar 2017 07:34:00 +0100 Subject: [PATCH 1/4] Move multiboot package under the hal package --- kernel/hal/hal.go | 0 kernel/{ => hal}/multiboot/multiboot.go | 0 kernel/{ => hal}/multiboot/multiboot_test.go | 0 kernel/kmain.go | 2 +- 4 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 kernel/hal/hal.go rename kernel/{ => hal}/multiboot/multiboot.go (100%) rename kernel/{ => hal}/multiboot/multiboot_test.go (100%) diff --git a/kernel/hal/hal.go b/kernel/hal/hal.go new file mode 100644 index 0000000..e69de29 diff --git a/kernel/multiboot/multiboot.go b/kernel/hal/multiboot/multiboot.go similarity index 100% rename from kernel/multiboot/multiboot.go rename to kernel/hal/multiboot/multiboot.go diff --git a/kernel/multiboot/multiboot_test.go b/kernel/hal/multiboot/multiboot_test.go similarity index 100% rename from kernel/multiboot/multiboot_test.go rename to kernel/hal/multiboot/multiboot_test.go diff --git a/kernel/kmain.go b/kernel/kmain.go index 1943eff..f0ebda4 100644 --- a/kernel/kmain.go +++ b/kernel/kmain.go @@ -3,7 +3,7 @@ package kernel import ( _ "unsafe" // required for go:linkname - "github.com/achilleasa/gopher-os/kernel/multiboot" + "github.com/achilleasa/gopher-os/kernel/hal/multiboot" ) // Kmain is the only Go symbol that is visible (exported) from the rt0 initialization From 7dac10a23b38a219c4a71cfb3da2eab1b0471b80 Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Thu, 30 Mar 2017 07:44:09 +0100 Subject: [PATCH 2/4] Rename Vga console driver to Ega and improve Init() signature --- .../driver/video/console/{vga.go => ega.go} | 33 +++----- .../console/{vga_test.go => ega_test.go} | 76 +++++++++---------- 2 files changed, 46 insertions(+), 63 deletions(-) rename kernel/driver/video/console/{vga.go => ega.go} (70%) rename kernel/driver/video/console/{vga_test.go => ega_test.go} (75%) diff --git a/kernel/driver/video/console/vga.go b/kernel/driver/video/console/ega.go similarity index 70% rename from kernel/driver/video/console/vga.go rename to kernel/driver/video/console/ega.go index 6eab30d..87a6d35 100644 --- a/kernel/driver/video/console/vga.go +++ b/kernel/driver/video/console/ega.go @@ -11,11 +11,11 @@ const ( clearChar = byte(' ') ) -// Vga implements an EGA-compatible text console. At the moment, it uses the +// Ega implements an EGA-compatible text console. At the moment, it uses the // ega console physical address as its outpucons. After implementing a memory // allocator, each console will use its own framebuffer while the active console // will periodically sync its internal buffer with the physical screen buffer. -type Vga struct { +type Ega struct { sync.Mutex width uint16 @@ -25,32 +25,19 @@ type Vga struct { } // Init sets up the console. -func (cons *Vga) Init() { - cons.width = 80 - cons.height = 25 - - // Set up our frame buffer object by creating a fake slice object pointing - // to the physical address of the screen buffer. - if cons.fb != nil { - return - } +func (cons *Ega) Init(width, height uint16, fbPhysAddr uintptr) { + cons.width = width + cons.height = height cons.fb = *(*[]uint16)(unsafe.Pointer(&reflect.SliceHeader{ Len: int(cons.width * cons.height), Cap: int(cons.width * cons.height), - Data: uintptr(0xB8000), + Data: fbPhysAddr, })) } -// OverrideFb overrides the console framebuffer slice with the supplied slice. -// This is a temporary function used by tests that will be removed once we can work -// with interfaces. -func (cons *Vga) OverrideFb(fb []uint16) { - cons.fb = fb -} - // Clear clears the specified rectangular region -func (cons *Vga) Clear(x, y, width, height uint16) { +func (cons *Ega) Clear(x, y, width, height uint16) { var ( attr = uint16((clearColor << 4) | clearColor) clr = attr | uint16(clearChar) @@ -81,12 +68,12 @@ func (cons *Vga) Clear(x, y, width, height uint16) { } // Dimensions returns the console width and height in characters. -func (cons *Vga) Dimensions() (uint16, uint16) { +func (cons *Ega) Dimensions() (uint16, uint16) { return cons.width, cons.height } // Scroll a particular number of lines to the specified direction. -func (cons *Vga) Scroll(dir ScrollDir, lines uint16) { +func (cons *Ega) Scroll(dir ScrollDir, lines uint16) { if lines == 0 || lines > cons.height { return } @@ -107,7 +94,7 @@ func (cons *Vga) Scroll(dir ScrollDir, lines uint16) { } // Write a char to the specified location. -func (cons *Vga) Write(ch byte, attr Attr, x, y uint16) { +func (cons *Ega) Write(ch byte, attr Attr, x, y uint16) { if x >= cons.width || y >= cons.height { return } diff --git a/kernel/driver/video/console/vga_test.go b/kernel/driver/video/console/ega_test.go similarity index 75% rename from kernel/driver/video/console/vga_test.go rename to kernel/driver/video/console/ega_test.go index 918acff..664a841 100644 --- a/kernel/driver/video/console/vga_test.go +++ b/kernel/driver/video/console/ega_test.go @@ -1,10 +1,13 @@ package console -import "testing" +import ( + "testing" + "unsafe" +) -func TestVgaInit(t *testing.T) { - var cons Vga - cons.Init() +func TestEgaInit(t *testing.T) { + var cons Ega + cons.Init(80, 25, 0xB8000) var expWidth uint16 = 80 var expHeight uint16 = 25 @@ -14,7 +17,7 @@ func TestVgaInit(t *testing.T) { } } -func TestVgaClear(t *testing.T) { +func TestEgaClear(t *testing.T) { specs := []struct { // Input rect x, y, w, h uint16 @@ -48,8 +51,9 @@ func TestVgaClear(t *testing.T) { }, } - var cons = Vga{fb: make([]uint16, 80*25)} - cons.Init() + fb := make([]uint16, 80*25) + var cons Ega + cons.Init(80, 25, uintptr(unsafe.Pointer(&fb[0]))) testPat := uint16(0xDEAD) clearPat := (uint16(clearColor) << 8) | uint16(clearChar) @@ -58,7 +62,7 @@ nextSpec: for specIndex, spec := range specs { // Fill FB with test pattern for i := 0; i < len(cons.fb); i++ { - cons.fb[i] = testPat + fb[i] = testPat } cons.Clear(spec.x, spec.y, spec.w, spec.h) @@ -66,7 +70,7 @@ nextSpec: var x, y uint16 for y = 0; y < cons.height; y++ { for x = 0; x < cons.width; x++ { - fbVal := cons.fb[(y*cons.width)+x] + fbVal := fb[(y*cons.width)+x] if x < spec.expX || y < spec.expY || x >= spec.expX+spec.expW || y >= spec.expY+spec.expH { if fbVal != testPat { @@ -84,15 +88,16 @@ nextSpec: } } -func TestVgaScrollUp(t *testing.T) { +func TestEgaScrollUp(t *testing.T) { specs := []uint16{ 0, 1, 2, } - var cons = Vga{fb: make([]uint16, 80*25)} - cons.Init() + fb := make([]uint16, 80*25) + var cons Ega + cons.Init(80, 25, uintptr(unsafe.Pointer(&fb[0]))) nextSpec: for specIndex, lines := range specs { @@ -100,7 +105,7 @@ nextSpec: var x, y, index uint16 for y = 0; y < cons.height; y++ { for x = 0; x < cons.width; x++ { - cons.fb[index] = (y << 8) | x + fb[index] = (y << 8) | x index++ } } @@ -112,7 +117,7 @@ nextSpec: for y = 0; y < cons.height-lines; y++ { for x = 0; x < cons.width; x++ { expVal := ((y + lines) << 8) | x - if cons.fb[index] != expVal { + if fb[index] != expVal { t.Errorf("[spec %d] expected value at (%d, %d) to be %d; got %d", specIndex, x, y, expVal, cons.fb[index]) continue nextSpec } @@ -122,15 +127,16 @@ nextSpec: } } -func TestVgaScrollDown(t *testing.T) { +func TestEgaScrollDown(t *testing.T) { specs := []uint16{ 0, 1, 2, } - var cons = Vga{fb: make([]uint16, 80*25)} - cons.Init() + fb := make([]uint16, 80*25) + var cons Ega + cons.Init(80, 25, uintptr(unsafe.Pointer(&fb[0]))) nextSpec: for specIndex, lines := range specs { @@ -138,7 +144,7 @@ nextSpec: var x, y, index uint16 for y = 0; y < cons.height; y++ { for x = 0; x < cons.width; x++ { - cons.fb[index] = (y << 8) | x + fb[index] = (y << 8) | x index++ } } @@ -150,7 +156,7 @@ nextSpec: for y = lines; y < cons.height-lines; y++ { for x = 0; x < cons.width; x++ { expVal := ((y - lines) << 8) | x - if cons.fb[index] != expVal { + if fb[index] != expVal { t.Errorf("[spec %d] expected value at (%d, %d) to be %d; got %d", specIndex, x, y, expVal, cons.fb[index]) continue nextSpec } @@ -160,9 +166,10 @@ nextSpec: } } -func TestVgaWriteWithOffScreenCoords(t *testing.T) { - var cons = Vga{fb: make([]uint16, 80*25)} - cons.Init() +func TestEgaWriteWithOffScreenCoords(t *testing.T) { + fb := make([]uint16, 80*25) + var cons Ega + cons.Init(80, 25, uintptr(unsafe.Pointer(&fb[0]))) specs := []struct { x, y uint16 @@ -176,13 +183,13 @@ func TestVgaWriteWithOffScreenCoords(t *testing.T) { nextSpec: for specIndex, spec := range specs { for i := 0; i < len(cons.fb); i++ { - cons.fb[i] = 0 + fb[i] = 0 } cons.Write('!', Red, spec.x, spec.y) for i := 0; i < len(cons.fb); i++ { - if got := cons.fb[i]; got != 0 { + if got := fb[i]; got != 0 { t.Errorf("[spec %d] expected Write() with off-screen coords to be a no-op", specIndex) continue nextSpec } @@ -190,27 +197,16 @@ nextSpec: } } -func TestVgaWrite(t *testing.T) { - var cons = Vga{fb: make([]uint16, 80*25)} - cons.Init() +func TestEgaWrite(t *testing.T) { + fb := make([]uint16, 80*25) + var cons Ega + cons.Init(80, 25, uintptr(unsafe.Pointer(&fb[0]))) attr := (Black << 4) | Red cons.Write('!', attr, 0, 0) expVal := uint16(attr<<8) | uint16('!') - if got := cons.fb[0]; got != expVal { + if got := fb[0]; got != expVal { t.Errorf("expected call to Write() to set fb[0] to %d; got %d", expVal, got) } } - -func TestVgaOverrideFb(t *testing.T) { - var cons = Vga{} - cons.Init() - - fb := []uint16{} - cons.OverrideFb(fb) - - if len(cons.fb) != len(fb) { - t.Fatalf("expected calling OverrideFb to change the framebuffer for the console") - } -} From 50da8ab1064eb4d03682b1193d90329890fa6e1e Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Thu, 30 Mar 2017 08:06:43 +0100 Subject: [PATCH 3/4] Remove all locking code Since the Go runtime is not yet setup there is no support for spinlocking if a lock cannot be acquired. This makes the locking code useless for now. --- kernel/driver/tty/vt.go | 19 ++++--------------- kernel/driver/tty/vt_test.go | 15 ++++++++------- kernel/driver/video/console/console.go | 4 ---- kernel/driver/video/console/ega.go | 3 --- 4 files changed, 12 insertions(+), 29 deletions(-) diff --git a/kernel/driver/tty/vt.go b/kernel/driver/tty/vt.go index 20afa6e..8ecd2a5 100644 --- a/kernel/driver/tty/vt.go +++ b/kernel/driver/tty/vt.go @@ -12,7 +12,7 @@ const ( type Vt struct { // Go interfaces will not work before we can get memory allocation working. // Till then we need to use concrete types instead. - cons *console.Vga + cons *console.Ega width uint16 height uint16 @@ -22,7 +22,9 @@ type Vt struct { curAttr console.Attr } -func (t *Vt) Init(cons *console.Vga) { +// AttachTo links the terminal with the specified console device and updates +// the terminal's dimensions to match the ones reported by the attached device. +func (t *Vt) AttachTo(cons *console.Ega) { t.cons = cons t.width, t.height = cons.Dimensions() t.curX = 0 @@ -30,30 +32,20 @@ func (t *Vt) Init(cons *console.Vga) { // Default to lightgrey on black text. t.curAttr = makeAttr(defaultFg, defaultBg) - } // Clear clears the terminal. func (t *Vt) Clear() { - t.cons.Lock() - defer t.cons.Unlock() - t.clear() } // Position returns the current cursor position (x, y). func (t *Vt) Position() (uint16, uint16) { - t.cons.Lock() - defer t.cons.Unlock() - return t.curX, t.curY } // SetPosition sets the current cursor position to (x,y). func (t *Vt) SetPosition(x, y uint16) { - t.cons.Lock() - defer t.cons.Unlock() - if x >= t.width { x = t.width - 1 } @@ -67,9 +59,6 @@ func (t *Vt) SetPosition(x, y uint16) { // Write implements io.Writer. func (t *Vt) Write(data []byte) (int, error) { - t.cons.Lock() - defer t.cons.Unlock() - attr := t.curAttr for _, b := range data { switch b { diff --git a/kernel/driver/tty/vt_test.go b/kernel/driver/tty/vt_test.go index 9d6f4f9..19233b0 100644 --- a/kernel/driver/tty/vt_test.go +++ b/kernel/driver/tty/vt_test.go @@ -2,6 +2,7 @@ package tty import ( "testing" + "unsafe" "github.com/achilleasa/gopher-os/kernel/driver/video/console" ) @@ -18,11 +19,12 @@ func TestVtPosition(t *testing.T) { {100, 100, 79, 24}, } - var cons console.Vga - cons.Init() + fb := make([]uint16, 80*25) + var cons console.Ega + cons.Init(80, 25, uintptr(unsafe.Pointer(&fb[0]))) var vt Vt - vt.Init(&cons) + vt.AttachTo(&cons) for specIndex, spec := range specs { vt.SetPosition(spec.inX, spec.inY) @@ -34,12 +36,11 @@ func TestVtPosition(t *testing.T) { func TestWrite(t *testing.T) { fb := make([]uint16, 80*25) - cons := &console.Vga{} - cons.OverrideFb(fb) - cons.Init() + var cons console.Ega + cons.Init(80, 25, uintptr(unsafe.Pointer(&fb[0]))) var vt Vt - vt.Init(cons) + vt.AttachTo(&cons) vt.Clear() vt.SetPosition(0, 1) diff --git a/kernel/driver/video/console/console.go b/kernel/driver/video/console/console.go index 8762d70..0b2cf1f 100644 --- a/kernel/driver/video/console/console.go +++ b/kernel/driver/video/console/console.go @@ -1,7 +1,5 @@ package console -import "sync" - // Attr defines a color attribute. type Attr uint16 @@ -36,8 +34,6 @@ const ( // The Console interface is implemented by objects that can function as physical consoles. type Console interface { - sync.Locker - // Dimensions returns the width and height of the console in characters. Dimensions() (uint16, uint16) diff --git a/kernel/driver/video/console/ega.go b/kernel/driver/video/console/ega.go index 87a6d35..3ed939e 100644 --- a/kernel/driver/video/console/ega.go +++ b/kernel/driver/video/console/ega.go @@ -2,7 +2,6 @@ package console import ( "reflect" - "sync" "unsafe" ) @@ -16,8 +15,6 @@ const ( // allocator, each console will use its own framebuffer while the active console // will periodically sync its internal buffer with the physical screen buffer. type Ega struct { - sync.Mutex - width uint16 height uint16 From 66eedf1200c4af0eccd450fbfb51a2b667d32275 Mon Sep 17 00:00:00 2001 From: Achilleas Anagnostopoulos Date: Thu, 30 Mar 2017 08:37:03 +0100 Subject: [PATCH 4/4] Initialize a minimal terminal attached to a text console device --- kernel/hal/hal.go | 21 +++++++++++++++++++++ kernel/kmain.go | 5 +++++ 2 files changed, 26 insertions(+) diff --git a/kernel/hal/hal.go b/kernel/hal/hal.go index e69de29..61bdc9e 100644 --- a/kernel/hal/hal.go +++ b/kernel/hal/hal.go @@ -0,0 +1,21 @@ +package hal + +import ( + "github.com/achilleasa/gopher-os/kernel/driver/tty" + "github.com/achilleasa/gopher-os/kernel/driver/video/console" + "github.com/achilleasa/gopher-os/kernel/hal/multiboot" +) + +var ( + egaConsole = &console.Ega{} + ActiveTerminal = &tty.Vt{} +) + +// InitTerminal provides a basic terminal to allow the kernel to emit some output +// till everything is properly setup +func InitTerminal() { + fbInfo := multiboot.GetFramebufferInfo() + + egaConsole.Init(uint16(fbInfo.Width), uint16(fbInfo.Height), uintptr(fbInfo.PhysAddr)) + ActiveTerminal.AttachTo(egaConsole) +} diff --git a/kernel/kmain.go b/kernel/kmain.go index f0ebda4..3f1127b 100644 --- a/kernel/kmain.go +++ b/kernel/kmain.go @@ -3,6 +3,7 @@ package kernel import ( _ "unsafe" // required for go:linkname + "github.com/achilleasa/gopher-os/kernel/hal" "github.com/achilleasa/gopher-os/kernel/hal/multiboot" ) @@ -19,4 +20,8 @@ import ( //go:noinline func Kmain(multibootInfoPtr uint32) { multiboot.SetInfoPtr(uintptr(multibootInfoPtr)) + + // Initialize and clear the terminal + hal.InitTerminal() + hal.ActiveTerminal.Clear() }