1
0
mirror of https://github.com/taigrr/gopher-os synced 2025-01-18 04:43:13 -08:00

Map physical address of the vga text console framebuffer in DriverInit

Currently, the kernel can write to 0xb80000 because this is part of the
initial identify mapping set up by the rt0 code. When we establish new
mappings for the kernel using its real VMA address then writes to the
framebuffer will cause a page fault unless we explicitly map it.
This commit is contained in:
Achilleas Anagnostopoulos 2017-07-08 20:38:14 +01:00
parent 8ac2ba82cc
commit 13ef4cd08d
3 changed files with 72 additions and 20 deletions

View File

@ -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

View File

@ -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 {

View File

@ -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) {