Skip to content

Commit

Permalink
Add package device
Browse files Browse the repository at this point in the history
Signed-off-by: Christopher Meis <[email protected]>
  • Loading branch information
ChriMarMe committed Sep 18, 2023
1 parent be721b0 commit e8425f7
Show file tree
Hide file tree
Showing 6 changed files with 313 additions and 0 deletions.
51 changes: 51 additions & 0 deletions device/acpi_pm_timer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package device

import (
"encoding/binary"
"time"
)

type ACPIPMTimer struct {
Start time.Time
}

const (
pmTimerFreqHz uint64 = 3_579_545
nanosPerSecond uint64 = 1_000_000_000
)

func NewACPIPMTimer() *ACPIPMTimer {
return &ACPIPMTimer{
Start: time.Now(),
}
}

func (a *ACPIPMTimer) Read(base uint64, data []byte) error {
if len(data) != 4 {
return errDataLenInvalid
}

since := time.Since(a.Start)
nanos := since.Nanoseconds()
counter := (nanos * int64(pmTimerFreqHz)) / int64(nanosPerSecond)
counter32 := uint32(counter & 0xFFFF_FFFF)
counterbyte := make([]byte, 4)

binary.LittleEndian.PutUint32(counterbyte, counter32)

copy(data[0:], counterbyte)

return nil
}

func (a *ACPIPMTimer) Write(base uint64, data []byte) error {
return nil
}

func (a *ACPIPMTimer) IOPort() uint64 {
return 0x608
}

func (a *ACPIPMTimer) Size() uint64 {
return 0x4
}
56 changes: 56 additions & 0 deletions device/acpi_shutdown_device.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package device

import "log"

// This device is used by EDK2/CloudHv to let the host know about a shutdown.
// No implementation of handling the event on host side yet.
// See: https://github.com/cloud-hypervisor/edk2/blob/ch/OvmfPkg/Include/IndustryStandard/CloudHv.h

const (
ACPIShutDownDevPort = uint64(0x600)
)

type ACPIShutDownDevice struct {
Port uint64
// ExitEvent chan int
// ResetEvent chan int
}

func NewACPIShutDownEvent() *ACPIShutDownDevice {
return &ACPIShutDownDevice{
Port: ACPIShutDownDevPort,
}
}

func (a *ACPIShutDownDevice) Read(base uint64, data []byte) error {
data[0] = 0

return nil
}

func (a *ACPIShutDownDevice) Write(base uint64, data []byte) error {
if data[0] == 1 {
// Send 1 to ResetEvent
// a.ResetEvent <- 1
log.Println("ACPI Reboot signaled")
}
// The ACPI DSDT table specifies the S5 sleep state (shutdown) as value 5
S5SleepVal := uint8(5)
SleepStatusENBit := uint8(5)
SleepValBit := uint8(2)

if data[0] == (S5SleepVal<<SleepValBit)|(1<<SleepStatusENBit) {
// a.ExitEvent <- 1
log.Println("ACPI Shutdown signalled")
}

return nil
}

func (a *ACPIShutDownDevice) IOPort() uint64 {
return a.Port
}

func (a *ACPIShutDownDevice) Size() uint64 {
return 0x8
}
127 changes: 127 additions & 0 deletions device/cmos.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package device

import (
"time"
)

const (
indexMask = uint8(0x7F)
indexOffset = uint64(0x70)
dataOffset = uint64(0x71)
dataLen = uint64(128)
)

type CMOS struct {
Index uint8
Data []uint8
}

func NewCMOS(memBelow4G, memAbove4G uint64) *CMOS {
cmos := &CMOS{
Index: 0,
Data: make([]uint8, dataLen),
}

// We assume 3G RAM at all times for now.....
extMem := uint16(0xBC00)

cmos.Data[0x34] = uint8(extMem)
cmos.Data[0x35] = uint8(extMem >> 8)

// Only valid for PVH boot of firmware with 3G RAM fixed.....
cmos.Data[0x5b] = 0
cmos.Data[0x5c] = 0
cmos.Data[0x5d] = 0

return cmos
}

func (c *CMOS) Read(base uint64, data []byte) error {
if len(data) != 1 {
return errDataLenInvalid
}

var d uint8

// Reading CMOS RAM also requires two steps:
// 1. OUT to port hex 70 with the CMOS address that is to be read from.
// 2. IN from port hex 71, and the data read is returned in the AL register.
// Ref: http://bitsavers.trailing-edge.com/pdf/ibm/pc/at/1502494_PC_AT_Technical_Reference_Mar84.pdf
switch base {
case indexOffset:
data[0] = c.Index
case dataOffset:
dt := time.Now()
secs := dt.Second()
min := dt.Minute()
hour := dt.Hour()
weekd := dt.Weekday()
day := dt.Day()
month := dt.Month()
year := dt.Year()

switch c.Index {
case 0x00:
d = toBCD(uint8(secs))
case 0x02:
d = toBCD(uint8(min))
case 0x04:
d = toBCD(uint8(hour))
case 0x06:
d = toBCD(uint8(weekd))
case 0x07:
d = toBCD(uint8(day))
case 0x08:
d = toBCD(uint8(month))
case 0x09:
d = toBCD(uint8(year % 100))
case 0x0A:
d = 1<<5 | 0<<7 // 32kHz Clock and we assume no update in progress
case 0x0D:
d = 1 << 7
case 0x32:
d = toBCD((uint8(year+1900) / 100))
default:
d = c.Data[c.Index&indexMask]
}

data[0] = d
}

return nil
}

func (c *CMOS) Write(base uint64, data []byte) error {
if len(data) != 1 {
return errDataLenInvalid
}

// Writing to CMOS RAM involves two steps:
// 1. OUT to port hex 70 with the CMOS address that will be written to.
// 2. OUT to port hex 71 with the data to be written.
// Ref: http://bitsavers.trailing-edge.com/pdf/ibm/pc/at/1502494_PC_AT_Technical_Reference_Mar84.pdf
switch base {
case indexOffset:
c.Index = data[0]
case dataOffset:
if c.Index == 0x8F && data[0] == 0 {
// CMOS reset - we ignore for now
} else {
c.Data[c.Index&indexMask] = data[0]
}
}

return nil
}

func toBCD(v uint8) uint8 {
return ((v / 100) << 4) | (v % 10)
}

func (c *CMOS) IOPort() uint64 {
return 0x70
}

func (c *CMOS) Size() uint64 {
return 0x2
}
15 changes: 15 additions & 0 deletions device/device.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package device

import "errors"

var errDataLenInvalid = errors.New("invalid data size on port")

// IODevice describes the interface a IO-Port device must implement regardless of the
// bus it is attached to.
// Clean up and unifying pci.Device and IODevice of this package will be required.
type IODevice interface {
Read(uint64, []byte) error
Write(uint64, []byte) error
IOPort() uint64
Size() uint64
}
42 changes: 42 additions & 0 deletions device/fw_debug_device.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package device

import (
"fmt"
)

type FWDebugDevice struct{}

func (f *FWDebugDevice) Read(port uint64, data []byte) error {
if len(data) == 1 {
// This magic value is read from the Port to indicate the availablity of the debug port.
// See: https://github.com/cloud-hypervisor/edk2/blob/ch/OvmfPkg/Library/PlatformDebugLibIoPort/DebugIoPortQemu.c
data[0] = 0xE9
} else {
return errDataLenInvalid
}

return nil
}

func (f *FWDebugDevice) Write(port uint64, data []byte) error {
if len(data) == 1 {
if data[0] == '\000' {
fmt.Printf("\r\n")
} else {
fmt.Printf("%c", data[0])
}
} else {
return errDataLenInvalid
}

return nil
}

func (f *FWDebugDevice) IOPort() uint64 {
// https://github.com/tianocore/edk2/commit/bf23b44d926982dfc9ecc7785cea17e0889a9297
return 0x402
}

func (f *FWDebugDevice) Size() uint64 {
return 0x1
}
22 changes: 22 additions & 0 deletions device/noop_device.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package device

type NoopDevice struct {
Port uint64
Psize uint64
}

func (n *NoopDevice) Read(port uint64, data []byte) error {
return nil
}

func (n *NoopDevice) Write(port uint64, data []byte) error {
return nil
}

func (n *NoopDevice) IOPort() uint64 {
return n.Port
}

func (n *NoopDevice) Size() uint64 {
return n.Psize
}

0 comments on commit e8425f7

Please sign in to comment.