4
0

added update & uboot functions

This commit is contained in:
Benjamin Böhmke 2019-12-01 21:20:32 +01:00
parent 1e9904b64b
commit cecb06c7ac
6 changed files with 327 additions and 3 deletions

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/bash
set -e
# get current partition index

View File

@ -1,4 +1,4 @@
#!/bin/bash
#!/bin/bash
set -e
image_file=$1

View File

@ -71,7 +71,7 @@ int main(int argc, char* argv[]) {
data[1] = 0;
// boot partition
data[2] = 2; // A=2, B=3
data[2] = 2; // A=1, B=2
}
// handle commands

View File

@ -65,6 +65,7 @@ func SystemDisableSSH() error {
return nil
}
// SystemShutdown start shutdown of system
func SystemShutdown() error {
cmd := exec.Command(systemShutdown)
err := cmd.Run()
@ -74,6 +75,7 @@ func SystemShutdown() error {
return nil
}
// SystemReboot start reboot of system
func SystemReboot() error {
cmd := exec.Command(systemReboot)
err := cmd.Run()

143
update.go Normal file
View File

@ -0,0 +1,143 @@
package alpine_builder
import (
"compress/gzip"
"encoding/binary"
"errors"
"fmt"
"hash/crc32"
"io"
"io/ioutil"
"log"
"os"
"os/exec"
)
var ubootFile = "/uboot/uboot.dat"
var ubootRemountRW = "mount -o remount,rw /uboot"
var ubootRemountRO = "mount -o remount,ro /uboot"
var rootPartitionA = "/dev/mmcblk0p1"
var rootPartitionB = "/dev/mmcblk0p2"
// loadUbootDat file to byte array
func loadUbootDat() ([]byte, error) {
defaultData := make([]byte, 1024)
defaultData[0] = 1 // file version
defaultData[1] = 0 // boot counter
defaultData[2] = 1 // boot partition A=1, B=2
data, err := ioutil.ReadFile(ubootFile)
if err != nil {
return defaultData, fmt.Errorf("failed to open file: %w", err)
}
// invalid dat file?
if len(data) < 1024 {
return defaultData, errors.New("invalid dat file -> fallback to defaults")
}
crc := binary.LittleEndian.Uint32(data[1020:])
bla := crc32.ChecksumIEEE(data[:1020])
if crc != bla {
return defaultData, errors.New("invalid crc -> fallback to defaults")
}
return data, nil
}
// saveUbootDat from byte array
func saveUbootDat(data []byte) error {
// update crc
binary.LittleEndian.PutUint32(data[1020:],
crc32.ChecksumIEEE(data[:1020]))
// remount boot partition - writable
cmd := exec.Command(ubootRemountRW)
err := cmd.Run()
if err != nil {
return fmt.Errorf("failed to remount RW: %w", err)
}
// update uboot dat file
err = ioutil.WriteFile(ubootFile, data[:1024], os.ModePerm)
if err != nil {
return fmt.Errorf("failed write uboot dat: %w", err)
}
// remount boot partition - readonly
cmd = exec.Command(ubootRemountRO)
err = cmd.Run()
if err != nil {
return fmt.Errorf("failed to remount RO: %w", err)
}
return nil
}
// UBootResetCounter to 0
func UBootResetCounter() error {
data, _ := loadUbootDat()
data[1] = 0
return saveUbootDat(data)
}
// UBootActive returns the active partition. A=1, B=2
func UBootActive() uint8 {
data, _ := loadUbootDat()
return data[2]
}
// UBootSetActive sets the active partition. A=1, B=2
func UBootSetActive(active uint8) error {
data, _ := loadUbootDat()
if active == 1 {
data[2] = 1
} else {
data[2] = 2
}
return saveUbootDat(data)
}
// UpdateSystem with the given image
func UpdateSystem(image string) error {
data, _ := loadUbootDat()
rootPart := rootPartitionA
if data[2] == 1 {
rootPart = rootPartitionB
}
// open image file
inFile, err := os.Open(image)
if err != nil {
log.Fatal(err)
}
defer inFile.Close()
// decompress image
inDecompress, err := gzip.NewReader(inFile)
if err != nil {
log.Fatal(err)
}
defer inDecompress.Close()
// open root partition
out, err := os.OpenFile(rootPart,
os.O_WRONLY|os.O_TRUNC|os.O_SYNC, os.ModePerm)
if err != nil {
log.Fatal(err)
}
defer out.Close()
// write update
_, err = io.Copy(out, inDecompress)
if err != nil {
log.Fatal(err)
}
// switch active partition
if data[2] == 1 {
return UBootSetActive(2)
} else {
return UBootSetActive(1)
}
}

179
update_test.go Normal file
View File

@ -0,0 +1,179 @@
package alpine_builder
import (
"bytes"
"compress/gzip"
"crypto/rand"
"encoding/binary"
"io"
"io/ioutil"
"os"
"testing"
"github.com/stretchr/testify/assert"
)
func init() {
ubootFile = "test_uboot"
ubootRemountRW = "true"
ubootRemountRO = "true"
rootPartitionA = "test_rootA"
rootPartitionB = "test_rootB"
}
func TestLoadUbootDat(t *testing.T) {
ass := assert.New(t)
// file missing
data, err := loadUbootDat()
ass.EqualError(err, "failed to open file: open test_uboot: no such file or directory")
ass.Equal([]byte{1, 0, 1}, data[:3])
// file with invalid data
ass.NoError(ioutil.WriteFile(ubootFile, []byte{}, os.ModePerm))
data, err = loadUbootDat()
ass.EqualError(err, "invalid dat file -> fallback to defaults")
ass.Equal([]byte{1, 0, 1}, data[:3])
// file with invalid CRC
testData := make([]byte, 1024)
testData[0] = 1
testData[1] = 2
testData[2] = 2
ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
data, err = loadUbootDat()
ass.EqualError(err, "invalid crc -> fallback to defaults")
ass.Equal([]byte{1, 0, 1}, data[:3])
// file with valid CRC
binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
data, err = loadUbootDat()
ass.NoError(err)
ass.Equal(testData, data)
_ = os.Remove(ubootFile)
}
func TestSaveUbootDat(t *testing.T) {
ass := assert.New(t)
testData := make([]byte, 1024)
testData[0] = 1
testData[1] = 2
testData[2] = 2
ass.NoError(saveUbootDat(testData))
binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
data, err := ioutil.ReadFile(ubootFile)
ass.NoError(err)
ass.Equal(testData, data)
_ = os.Remove(ubootFile)
}
func TestUBootResetCounter(t *testing.T) {
ass := assert.New(t)
// write test file
testData := make([]byte, 1024)
testData[0] = 1
testData[1] = 2
testData[2] = 2
binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
ass.NoError(UBootResetCounter())
data, err := ioutil.ReadFile(ubootFile)
ass.NoError(err)
ass.Zero(data[1])
_ = os.Remove(ubootFile)
}
func TestUBootActive(t *testing.T) {
ass := assert.New(t)
// write test file
testData := make([]byte, 1024)
testData[0] = 1
testData[1] = 2
testData[2] = 2
binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
ass.Equal(uint8(2), UBootActive())
_ = os.Remove(ubootFile)
}
func TestUBootSetActive(t *testing.T) {
ass := assert.New(t)
// write test file
testData := make([]byte, 1024)
testData[0] = 1
testData[1] = 2
testData[2] = 2
binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
ass.NoError(UBootSetActive(1))
data, err := ioutil.ReadFile(ubootFile)
ass.NoError(err)
ass.Equal(uint8(1), data[2])
_ = os.Remove(ubootFile)
}
func TestUpdateSystem(t *testing.T) {
ass := assert.New(t)
// test uboot file
testData := make([]byte, 1024)
testData[0] = 1
testData[1] = 2
testData[2] = 2
binary.LittleEndian.PutUint32(testData[1020:], 0x982E8B7A)
ass.NoError(ioutil.WriteFile(ubootFile, testData, os.ModePerm))
// generate test image content
size := int64(1024 * 1024 * 5)
testImgData := make([]byte, size)
buffer := bytes.NewBuffer(testImgData)
_, err := io.CopyN(buffer, rand.Reader, size)
ass.NoError(err)
// write compressed image file
file, err := os.Create("test_image.gz")
ass.NoError(err)
gzipWriter := gzip.NewWriter(file)
_, err = gzipWriter.Write(testImgData)
ass.NoError(err)
ass.NoError(gzipWriter.Close())
ass.NoError(file.Close())
ass.NoError(ioutil.WriteFile(rootPartitionA, nil, os.ModePerm))
ass.NoError(UpdateSystem("test_image.gz"))
// check if image was written
data, err := ioutil.ReadFile(rootPartitionA)
ass.NoError(err)
ass.Equal(testImgData, data)
// check if uboot dat was updated
data, err = ioutil.ReadFile(ubootFile)
ass.NoError(err)
ass.Equal(uint8(1), data[2])
_ = os.Remove("test_image.gz")
_ = os.Remove(ubootFile)
_ = os.Remove(rootPartitionA)
_ = os.Remove(rootPartitionB)
}