mirror of
https://github.com/taigrr/gopher-os
synced 2025-01-18 04:43:13 -08:00
Merge pull request #40 from achilleasa/map-vga-text-fb
Map vga text console framebuffer
This commit is contained in:
commit
86609fea06
@ -1,9 +1,15 @@
|
|||||||
package console
|
package console
|
||||||
|
|
||||||
import "gopheros/device"
|
import (
|
||||||
import "gopheros/kernel/hal/multiboot"
|
"gopheros/device"
|
||||||
|
"gopheros/kernel/cpu"
|
||||||
|
"gopheros/kernel/hal/multiboot"
|
||||||
|
"gopheros/kernel/mem/vmm"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
mapRegionFn = vmm.MapRegion
|
||||||
|
portWriteByteFn = cpu.PortWriteByte
|
||||||
getFramebufferInfoFn = multiboot.GetFramebufferInfo
|
getFramebufferInfoFn = multiboot.GetFramebufferInfo
|
||||||
|
|
||||||
// ProbeFuncs is a slice of device probe functions that is used by
|
// ProbeFuncs is a slice of device probe functions that is used by
|
||||||
|
@ -3,16 +3,17 @@ package console
|
|||||||
import (
|
import (
|
||||||
"gopheros/device"
|
"gopheros/device"
|
||||||
"gopheros/kernel"
|
"gopheros/kernel"
|
||||||
"gopheros/kernel/cpu"
|
|
||||||
"gopheros/kernel/hal/multiboot"
|
"gopheros/kernel/hal/multiboot"
|
||||||
|
"gopheros/kernel/kfmt"
|
||||||
|
"gopheros/kernel/mem"
|
||||||
|
"gopheros/kernel/mem/pmm"
|
||||||
|
"gopheros/kernel/mem/vmm"
|
||||||
"image/color"
|
"image/color"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
var portWriteByteFn = cpu.PortWriteByte
|
|
||||||
|
|
||||||
// VgaTextConsole implements an EGA-compatible 80x25 text console using VGA
|
// VgaTextConsole implements an EGA-compatible 80x25 text console using VGA
|
||||||
// mode 0x3. The console supports the default 16 EGA colors which can be
|
// mode 0x3. The console supports the default 16 EGA colors which can be
|
||||||
// overridden using the SetPaletteColor method.
|
// overridden using the SetPaletteColor method.
|
||||||
@ -28,7 +29,8 @@ type VgaTextConsole struct {
|
|||||||
width uint32
|
width uint32
|
||||||
height uint32
|
height uint32
|
||||||
|
|
||||||
fb []uint16
|
fbPhysAddr uintptr
|
||||||
|
fb []uint16
|
||||||
|
|
||||||
palette color.Palette
|
palette color.Palette
|
||||||
defaultFg uint8
|
defaultFg uint8
|
||||||
@ -40,15 +42,10 @@ type VgaTextConsole struct {
|
|||||||
// framebuffer mapped to fbPhysAddr.
|
// framebuffer mapped to fbPhysAddr.
|
||||||
func NewVgaTextConsole(columns, rows uint32, fbPhysAddr uintptr) *VgaTextConsole {
|
func NewVgaTextConsole(columns, rows uint32, fbPhysAddr uintptr) *VgaTextConsole {
|
||||||
return &VgaTextConsole{
|
return &VgaTextConsole{
|
||||||
width: columns,
|
width: columns,
|
||||||
height: rows,
|
height: rows,
|
||||||
clearChar: uint16(' '),
|
fbPhysAddr: fbPhysAddr,
|
||||||
// overlay a 16bit slice over the fbPhysAddr
|
clearChar: uint16(' '),
|
||||||
fb: *(*[]uint16)(unsafe.Pointer(&reflect.SliceHeader{
|
|
||||||
Len: int(columns * rows),
|
|
||||||
Cap: int(columns * rows),
|
|
||||||
Data: fbPhysAddr,
|
|
||||||
})),
|
|
||||||
palette: color.Palette{
|
palette: color.Palette{
|
||||||
color.RGBA{R: 0, G: 0, B: 1}, /* black */
|
color.RGBA{R: 0, G: 0, B: 1}, /* black */
|
||||||
color.RGBA{R: 0, G: 0, B: 128}, /* blue */
|
color.RGBA{R: 0, G: 0, B: 128}, /* blue */
|
||||||
@ -198,7 +195,29 @@ func (cons *VgaTextConsole) DriverVersion() (uint16, uint16, uint16) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// DriverInit initializes this driver.
|
// DriverInit initializes this driver.
|
||||||
func (cons *VgaTextConsole) DriverInit(_ io.Writer) *kernel.Error { return nil }
|
func (cons *VgaTextConsole) DriverInit(w io.Writer) *kernel.Error {
|
||||||
|
// Map the framebuffer so we can write to it
|
||||||
|
fbSize := mem.Size(cons.width * cons.height * 2)
|
||||||
|
fbPage, err := mapRegionFn(
|
||||||
|
pmm.Frame(cons.fbPhysAddr>>mem.PageShift),
|
||||||
|
fbSize,
|
||||||
|
vmm.FlagPresent|vmm.FlagRW,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
cons.fb = *(*[]uint16)(unsafe.Pointer(&reflect.SliceHeader{
|
||||||
|
Len: int(fbSize >> 1),
|
||||||
|
Cap: int(fbSize >> 1),
|
||||||
|
Data: fbPage.Address(),
|
||||||
|
}))
|
||||||
|
|
||||||
|
kfmt.Fprintf(w, "mapped framebuffer to 0x%x\n", fbPage.Address())
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// probeForVgaTextConsole checks for the presence of a vga text console.
|
// probeForVgaTextConsole checks for the presence of a vga text console.
|
||||||
func probeForVgaTextConsole() device.Driver {
|
func probeForVgaTextConsole() device.Driver {
|
||||||
|
@ -2,8 +2,12 @@ package console
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"gopheros/device"
|
"gopheros/device"
|
||||||
|
"gopheros/kernel"
|
||||||
"gopheros/kernel/cpu"
|
"gopheros/kernel/cpu"
|
||||||
"gopheros/kernel/hal/multiboot"
|
"gopheros/kernel/hal/multiboot"
|
||||||
|
"gopheros/kernel/mem"
|
||||||
|
"gopheros/kernel/mem/pmm"
|
||||||
|
"gopheros/kernel/mem/vmm"
|
||||||
"image/color"
|
"image/color"
|
||||||
"testing"
|
"testing"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
@ -63,6 +67,7 @@ func TestVgaTextFill(t *testing.T) {
|
|||||||
|
|
||||||
fb := make([]uint16, 80*25)
|
fb := make([]uint16, 80*25)
|
||||||
cons := NewVgaTextConsole(80, 25, uintptr(unsafe.Pointer(&fb[0])))
|
cons := NewVgaTextConsole(80, 25, uintptr(unsafe.Pointer(&fb[0])))
|
||||||
|
cons.fb = fb
|
||||||
cw, ch := cons.Dimensions()
|
cw, ch := cons.Dimensions()
|
||||||
|
|
||||||
testPat := uint16(0xDEAD)
|
testPat := uint16(0xDEAD)
|
||||||
@ -101,6 +106,7 @@ nextSpec:
|
|||||||
func TestVgaTextScroll(t *testing.T) {
|
func TestVgaTextScroll(t *testing.T) {
|
||||||
fb := make([]uint16, 80*25)
|
fb := make([]uint16, 80*25)
|
||||||
cons := NewVgaTextConsole(80, 25, uintptr(unsafe.Pointer(&fb[0])))
|
cons := NewVgaTextConsole(80, 25, uintptr(unsafe.Pointer(&fb[0])))
|
||||||
|
cons.fb = fb
|
||||||
cw, ch := cons.Dimensions()
|
cw, ch := cons.Dimensions()
|
||||||
|
|
||||||
t.Run("up", func(t *testing.T) {
|
t.Run("up", func(t *testing.T) {
|
||||||
@ -176,6 +182,7 @@ func TestVgaTextScroll(t *testing.T) {
|
|||||||
func TestVgaTextWrite(t *testing.T) {
|
func TestVgaTextWrite(t *testing.T) {
|
||||||
fb := make([]uint16, 80*25)
|
fb := make([]uint16, 80*25)
|
||||||
cons := NewVgaTextConsole(80, 25, uintptr(unsafe.Pointer(&fb[0])))
|
cons := NewVgaTextConsole(80, 25, uintptr(unsafe.Pointer(&fb[0])))
|
||||||
|
cons.fb = fb
|
||||||
defaultFg, defaultBg := cons.DefaultColors()
|
defaultFg, defaultBg := cons.DefaultColors()
|
||||||
|
|
||||||
t.Run("off-screen", func(t *testing.T) {
|
t.Run("off-screen", func(t *testing.T) {
|
||||||
@ -309,12 +316,11 @@ func TestVgaTextSetPaletteColor(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestVgaTextDriverInterface(t *testing.T) {
|
func TestVgaTextDriverInterface(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
mapRegionFn = vmm.MapRegion
|
||||||
|
}()
|
||||||
var dev device.Driver = NewVgaTextConsole(80, 25, 0)
|
var dev device.Driver = NewVgaTextConsole(80, 25, 0)
|
||||||
|
|
||||||
if err := dev.DriverInit(nil); err != nil {
|
|
||||||
t.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if dev.DriverName() == "" {
|
if dev.DriverName() == "" {
|
||||||
t.Fatal("DriverName() returned an empty string")
|
t.Fatal("DriverName() returned an empty string")
|
||||||
}
|
}
|
||||||
@ -322,6 +328,27 @@ func TestVgaTextDriverInterface(t *testing.T) {
|
|||||||
if major, minor, patch := dev.DriverVersion(); major+minor+patch == 0 {
|
if major, minor, patch := dev.DriverVersion(); major+minor+patch == 0 {
|
||||||
t.Fatal("DriverVersion() returned an invalid version number")
|
t.Fatal("DriverVersion() returned an invalid version number")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t.Run("init success", func(t *testing.T) {
|
||||||
|
mapRegionFn = func(_ pmm.Frame, _ mem.Size, _ vmm.PageTableEntryFlag) (vmm.Page, *kernel.Error) {
|
||||||
|
return 0xb8000, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dev.DriverInit(nil); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("init fail", func(t *testing.T) {
|
||||||
|
expErr := &kernel.Error{Module: "test", Message: "something went wrong"}
|
||||||
|
mapRegionFn = func(_ pmm.Frame, _ mem.Size, _ vmm.PageTableEntryFlag) (vmm.Page, *kernel.Error) {
|
||||||
|
return 0, expErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dev.DriverInit(nil); err != expErr {
|
||||||
|
t.Fatalf("expected error: %v; got %v", expErr, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestVgaTextProbe(t *testing.T) {
|
func TestVgaTextProbe(t *testing.T) {
|
||||||
|
@ -47,6 +47,8 @@ var (
|
|||||||
// which will cause a fault if called in user-mode.
|
// which will cause a fault if called in user-mode.
|
||||||
flushTLBEntryFn = cpu.FlushTLBEntry
|
flushTLBEntryFn = cpu.FlushTLBEntry
|
||||||
|
|
||||||
|
earlyReserveRegionFn = EarlyReserveRegion
|
||||||
|
|
||||||
errNoHugePageSupport = &kernel.Error{Module: "vmm", Message: "huge pages are not supported"}
|
errNoHugePageSupport = &kernel.Error{Module: "vmm", Message: "huge pages are not supported"}
|
||||||
errAttemptToRWMapReservedFrame = &kernel.Error{Module: "vmm", Message: "reserved blank frame cannot be mapped with a RW flag"}
|
errAttemptToRWMapReservedFrame = &kernel.Error{Module: "vmm", Message: "reserved blank frame cannot be mapped with a RW flag"}
|
||||||
)
|
)
|
||||||
@ -105,6 +107,29 @@ func Map(page Page, frame pmm.Frame, flags PageTableEntryFlag) *kernel.Error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MapRegion establishes a mapping to the physical memory region which starts
|
||||||
|
// at the given frame and ends at frame + pages(size). The size argument is
|
||||||
|
// always rounded up to the nearest page boundary. MapRegion reserves the next
|
||||||
|
// available region in the active virtual address space, establishes the
|
||||||
|
// mapping and returns back the Page that corresponds to the region start.
|
||||||
|
func MapRegion(frame pmm.Frame, size mem.Size, flags PageTableEntryFlag) (Page, *kernel.Error) {
|
||||||
|
// Reserve next free block in the address space
|
||||||
|
size = (size + (mem.PageSize - 1)) & ^(mem.PageSize - 1)
|
||||||
|
startPage, err := earlyReserveRegionFn(size)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
pageCount := size >> mem.PageShift
|
||||||
|
for page := PageFromAddress(startPage); pageCount > 0; pageCount, page, frame = pageCount-1, page+1, frame+1 {
|
||||||
|
if err := mapFn(page, frame, flags); err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PageFromAddress(startPage), nil
|
||||||
|
}
|
||||||
|
|
||||||
// MapTemporary establishes a temporary RW mapping of a physical memory frame
|
// MapTemporary establishes a temporary RW mapping of a physical memory frame
|
||||||
// to a fixed virtual address overwriting any previous mapping. The temporary
|
// to a fixed virtual address overwriting any previous mapping. The temporary
|
||||||
// mapping mechanism is primarily used by the kernel to access and initialize
|
// mapping mechanism is primarily used by the kernel to access and initialize
|
||||||
|
@ -97,6 +97,73 @@ func TestMapTemporaryAmd64(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMapRegion(t *testing.T) {
|
||||||
|
defer func() {
|
||||||
|
mapFn = Map
|
||||||
|
earlyReserveRegionFn = EarlyReserveRegion
|
||||||
|
}()
|
||||||
|
|
||||||
|
t.Run("success", func(t *testing.T) {
|
||||||
|
mapCallCount := 0
|
||||||
|
mapFn = func(_ Page, _ pmm.Frame, flags PageTableEntryFlag) *kernel.Error {
|
||||||
|
mapCallCount++
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
earlyReserveRegionCallCount := 0
|
||||||
|
earlyReserveRegionFn = func(_ mem.Size) (uintptr, *kernel.Error) {
|
||||||
|
earlyReserveRegionCallCount++
|
||||||
|
return 0xf00, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := MapRegion(pmm.Frame(0xdf0000), 4097, FlagPresent|FlagRW); err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exp := 2; mapCallCount != exp {
|
||||||
|
t.Errorf("expected Map to be called %d time(s); got %d", exp, mapCallCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exp := 1; earlyReserveRegionCallCount != exp {
|
||||||
|
t.Errorf("expected EarlyReserveRegion to be called %d time(s); got %d", exp, earlyReserveRegionCallCount)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("EarlyReserveRegion fails", func(t *testing.T) {
|
||||||
|
expErr := &kernel.Error{Module: "test", Message: "out of address space"}
|
||||||
|
|
||||||
|
earlyReserveRegionFn = func(_ mem.Size) (uintptr, *kernel.Error) {
|
||||||
|
return 0, expErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := MapRegion(pmm.Frame(0xdf0000), 128000, FlagPresent|FlagRW); err != expErr {
|
||||||
|
t.Fatalf("expected error: %v; got %v", expErr, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
t.Run("Map fails", func(t *testing.T) {
|
||||||
|
expErr := &kernel.Error{Module: "test", Message: "map failed"}
|
||||||
|
|
||||||
|
earlyReserveRegionCallCount := 0
|
||||||
|
earlyReserveRegionFn = func(_ mem.Size) (uintptr, *kernel.Error) {
|
||||||
|
earlyReserveRegionCallCount++
|
||||||
|
return 0xf00, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
mapFn = func(_ Page, _ pmm.Frame, flags PageTableEntryFlag) *kernel.Error {
|
||||||
|
return expErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := MapRegion(pmm.Frame(0xdf0000), 128000, FlagPresent|FlagRW); err != expErr {
|
||||||
|
t.Fatalf("expected error: %v; got %v", expErr, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if exp := 1; earlyReserveRegionCallCount != exp {
|
||||||
|
t.Errorf("expected EarlyReserveRegion to be called %d time(s); got %d", exp, earlyReserveRegionCallCount)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func TestMapTemporaryErrorsAmd64(t *testing.T) {
|
func TestMapTemporaryErrorsAmd64(t *testing.T) {
|
||||||
if runtime.GOARCH != "amd64" {
|
if runtime.GOARCH != "amd64" {
|
||||||
t.Skip("test requires amd64 runtime; skipping")
|
t.Skip("test requires amd64 runtime; skipping")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user