github.com/platinasystems/nvram@v1.0.1-0.20190709235807-51a23abd5aec/cmos_hw.go (about)

     1  // Copyright © 2019 Platina Systems, Inc. All rights reserved.
     2  // Use of this source code is governed by the GPL-2 license described in the
     3  // LICENSE file.
     4  
     5  package nvram
     6  
     7  import (
     8  	"fmt"
     9  	"github.com/platinasystems/nvram/debug"
    10  	"os"
    11  	"syscall"
    12  )
    13  
    14  const (
    15  	sys_iopl   = 172 //amd64
    16  	sys_ioperm = 173 //amd64
    17  )
    18  
    19  type CMOSHW struct {
    20  	port_file *os.File
    21  }
    22  
    23  func (c *CMOSHW) Open() (err error) {
    24  	// Close in case it is already opened
    25  	c.Close()
    26  
    27  	// Close on any error
    28  	defer func() {
    29  		if err != nil {
    30  			c.Close()
    31  		}
    32  	}()
    33  
    34  	debug.Trace(debug.LevelMSG1, "Opening CMOS HW\n")
    35  
    36  	// Set IO privilege level to 3. 
    37  	if _, _, errno := syscall.Syscall(sys_iopl,
    38  		uintptr(3), 0, 0); errno != 0 {
    39  		return errno
    40  	}
    41  
    42  	// Open device ports for access to CMOS NVRAM
    43  	c.port_file, err = os.OpenFile("/dev/port", os.O_RDWR|os.O_SYNC, 0755)
    44  	if err != nil {
    45  		return
    46  	}
    47  
    48  	return
    49  }
    50  
    51  func (c *CMOSHW) Close() error {
    52  
    53  	debug.Trace(debug.LevelMSG1, "Closing CMOS HW\n")
    54  
    55  	// Set IO privilege level to normal
    56  	if _, _, errno := syscall.Syscall(sys_iopl,
    57  		uintptr(0), 0, 0); errno != 0 {
    58  		return errno
    59  	}
    60  
    61  	// Close port file if opened
    62  	if c.port_file != nil {
    63  		c.port_file.Close()
    64  		c.port_file = nil
    65  	}
    66  
    67  	return nil
    68  }
    69  
    70  func (c *CMOSHW) ReadByte(off uint) (byte, error) {
    71  	if c.port_file == nil {
    72  		return 0, ErrCMOSNotOpen
    73  	}
    74  	if !verifyCMOSByteIndex(off) {
    75  		return 0, ErrInvalidCMOSIndex
    76  	}
    77  
    78  	// Find port0 and 1 to set CMOS data offset
    79  	var port_0, port_1 int64
    80  	if off < 128 {
    81  		port_0 = 0x70
    82  		port_1 = 0x71
    83  	} else {
    84  		port_0 = 0x72
    85  		port_1 = 0x73
    86  	}
    87  
    88  	// Set offset
    89  	if err := c.ioWriteReg8(port_0, byte(off)); err != nil {
    90  		return 0, err
    91  	}
    92  
    93  	// Read data from NVRAM at offset
    94  	return c.ioReadReg8(port_1)
    95  }
    96  
    97  func (c *CMOSHW) WriteByte(off uint, b byte) error {
    98  	if c.port_file == nil {
    99  		return ErrCMOSNotOpen
   100  	}
   101  
   102  	if !verifyCMOSByteIndex(off) {
   103  		return ErrInvalidCMOSIndex
   104  	}
   105  
   106  	// Find port0 and 1 to set CMOS data offset
   107  	var port_0, port_1 int64
   108  	if off < 128 {
   109  		port_0 = 0x70
   110  		port_1 = 0x71
   111  	} else {
   112  		port_0 = 0x72
   113  		port_1 = 0x73
   114  	}
   115  
   116  	// Set offset
   117  	if err := c.ioWriteReg8(port_0, byte(off)); err != nil {
   118  		return err
   119  	}
   120  
   121  	// Write data to NVRAM at offset
   122  	if err := c.ioWriteReg8(port_1, b); err != nil {
   123  		return err
   124  	}
   125  
   126  	return nil
   127  }
   128  
   129  func (c *CMOSHW) ioReadReg8(addr int64) (b byte, err error) {
   130  	// Seek to port address
   131  	if _, err = c.port_file.Seek(addr, 0); err != nil {
   132  		return
   133  	}
   134  
   135  	// Read data from port into buffer
   136  	buf := make([]byte, 1)
   137  	n, err := c.port_file.Read(buf)
   138  	if err != nil {
   139  		return
   140  	}
   141  
   142  	if n != 1 {
   143  		err = fmt.Errorf("nvram: Unable to read port.")
   144  		return
   145  	}
   146  
   147  	// Return data read
   148  	b = buf[0]
   149  	return
   150  }
   151  
   152  func (c *CMOSHW) ioWriteReg8(addr int64, b byte) (err error) {
   153  	// Prepare write buffer
   154  	buf := make([]byte, 1)
   155  	buf[0] = b
   156  
   157  	// Seek to port address
   158  	if _, err = c.port_file.Seek(addr, 0); err != nil {
   159  		return err
   160  	}
   161  
   162  	// Write data to port
   163  	n, err := c.port_file.Write(buf)
   164  	if err != nil {
   165  		return err
   166  	}
   167  
   168  	// Sync write
   169  	c.port_file.Sync()
   170  
   171  	if n != 1 {
   172  		return fmt.Errorf("nvram: Unable to write port.")
   173  	}
   174  
   175  	return
   176  }