EEPROM
The EEPROM is the chip a computer boots from. Its contents are the program the CPU runs at power-on, and a program can read or reflash them.
Facts
| | |
|—|—|
| Type | eeprom |
| In /dev | eeprom (there is only ever one) |
| Address | eeprom |
| Interrupt | None |
| Info | used: the size of its contents in bytes |
For the chip you craft and place, see EEPROM.
How it works
At power-on the CPU loads its program from the EEPROM’s fixed slot, as a reset vector, and runs it. A CPU that cannot (the chip is empty or its code does not load) panics. The EEPROM’s contents are plain text, one character a byte, and for the Lua CPU they are Lua code. The mod has no size limit for it yet.
The chip a computer comes with boots the first disk, in bus order, that holds /init.lua, handing
it the disk’s address, and says boot: no bootable disk and halts when none does. The code on the
chip runs before any OS, so it calls the board directly (board.invoke, board.list).
The chip keeps its contents on its item: reads and writes go straight there. A write takes effect at the next power-on.
In mcos
/dev/eeprom
/dev/eeprom is the chip’s image, as flashrom -r and -w read and write a board’s flash.
- Read gives the whole image, as it was when the file was first read. Seeking back to the start reads it afresh.
- Write keeps what is written until the file is closed, and then replaces the whole image in one call, all or nothing, as a firmware flash commits. Until then the chip is as it was.
- For
sys.poll, the file is always ready to read and to write, and never hasPRI(the chip has no interrupt).
cp /dev/eeprom /home/eeprom.bak # back it up
cp /home/new.lua /dev/eeprom # flash a new image
reboot # run it
Its ioctls are its methods
The file’s ioctls are the methods below, by name, in any open mode: get gives the image and
set flashes one at once, with no close to wait for. setLabel (the kernel’s, on every device
file) puts a label on the chip’s item; a chip with no item to keep it on refuses with EPERM.
local image = sys.ioctl(sys.fileno(io.open("/dev/eeprom")), "get")
Methods
get() → string
The chip’s contents.
set(data) → nothing
Replaces the chip’s contents with data. It takes effect at the next power-on.
Events
None.
Examples
Back up the chip
-- Save the EEPROM's image to a file, and check the copy against the chip.
--
-- usage: lua eeprom-backup.lua [file] (default /home/eeprom.bak)
--
-- Reading /dev/eeprom gives the chip's whole image, as flashrom -r reads a board's flash. Restore it
-- later with eeprom-flash.lua, or with cp file /dev/eeprom.
local FILE = ... or "/home/eeprom.bak"
local function slurp(path)
local file = assert(io.open(path, "rb"))
local data = file:read("a")
file:close()
return data
end
local image = slurp("/dev/eeprom")
local out = assert(io.open(FILE, "wb"))
assert(out:write(image))
assert(out:close()) -- (a disk's file is written whole at close)
if slurp(FILE) ~= image then
io.stderr:write("eeprom-backup: ", FILE, " doesn't match the chip\n")
os.exit(1)
end
print("saved " .. #image .. " bytes of the EEPROM to " .. FILE)
Flash a new image, safely
Refuses a file that does not compile (it would leave the computer unable to boot), keeps the old image, and reads the chip back to check.
-- Flash a Lua file onto the EEPROM, safely: refuse one that doesn't compile, keep the old image, and
-- check the chip holds the new one.
--
-- usage: lua eeprom-flash.lua file [backup] (backup: default /home/eeprom.old)
--
-- What's written to /dev/eeprom is kept until the file is closed, then replaces the whole image in
-- one go (all or nothing, as a firmware flash commits). The CPU runs the image as Lua at the next
-- power-on, so an image that doesn't compile would leave the computer unable to boot: this checks
-- first. Then it reads the chip back with the "get" ioctl (its method) to be sure.
local sys = require("sys")
local file, backup = ...
backup = backup or "/home/eeprom.old"
if not file then
io.stderr:write("usage: eeprom-flash.lua file [backup]\n")
os.exit(2)
end
local function slurp(path)
local f = assert(io.open(path, "rb"))
local data = f:read("a")
f:close()
return data
end
local image = slurp(file)
local compiled, why = load(image, "=" .. file)
if not compiled then
io.stderr:write("eeprom-flash: not flashed: ", why, "\n")
os.exit(1)
end
-- Keep what's there now.
local old = assert(io.open(backup, "wb"))
old:write(slurp("/dev/eeprom"))
assert(old:close())
-- Flash: written at close.
local chip = assert(io.open("/dev/eeprom", "wb"))
chip:write(image)
assert(chip:close())
-- Read it back from the chip itself.
local check = assert(io.open("/dev/eeprom"))
local now = assert(sys.ioctl(sys.fileno(check), "get"))
check:close()
if now ~= image then
io.stderr:write("eeprom-flash: the chip doesn't hold what was written; the old image is in ", backup, "\n")
os.exit(1)
end
print("flashed " .. #image .. " bytes; the old image is in " .. backup .. "; reboot to run it")
lua eeprom-flash.lua /home/boot.lua
reboot
Boot one disk only
Writes a new boot image that boots one disk, by its id, wherever it sits on the bus. The image it writes is firmware-level code: a whole EEPROM program, calling the board directly.
-- Make the computer boot one disk only, whatever else is on the bus: write a new boot image for the
-- EEPROM that looks for /init.lua on that disk alone, by its id.
--
-- usage: lua eeprom-pin-boot.lua [disk] (the disk's name in /dev; default storage0, the one now)
--
-- The EEPROM a computer comes with boots the first disk, in bus order, holding /init.lua; a disk put
-- in an earlier slot would then boot instead. A disk's id never changes when it moves, so an image
-- naming it boots that disk wherever it sits. The image is built from IMAGE below and flashed with
-- the "set" ioctl (the EEPROM's own method: the whole image, at once), after "get" saves the old one.
local sys = require("sys")
local BACKUP = "/home/eeprom.old" -- the image there before
-- The new image: firmware-level Lua (it runs before any OS, so it calls the board directly).
-- DISK is filled in with the disk's id.
local IMAGE = [=[
-- pinned boot: boots /init.lua from one disk, by its id, or halts saying why.
local DISK = %q
local function say(text)
board.invoke("console", "write", text)
end
local opened, file = board.invoke(DISK, "open", "/init.lua")
if not opened then
say("boot: " .. DISK .. ": " .. tostring(file) .. "\r\n")
board.halt()
end
local parts = {}
while true do
local ok, chunk = board.invoke(file, "read", 4096)
if not ok or chunk == nil then
break
end
parts[#parts + 1] = chunk
end
board.invoke(file, "close")
local init, why = load(table.concat(parts), "=init.lua")
if not init then
say("boot: " .. DISK .. ": " .. why .. "\r\n")
board.halt()
end
return init(DISK)
]=]
local diskname = ... or "storage0"
local disk
for _, device in ipairs(sys.devices()) do
if device.name == diskname and device.type == "storage" then
disk = device
end
end
if not disk then
io.stderr:write("eeprom-pin-boot: ", diskname, ": no such disk\n")
os.exit(1)
end
local image = string.format(IMAGE, disk.id)
assert(load(image, "=pinned boot")) -- (it must compile: the CPU runs it at power-on)
local chip = assert(io.open("/dev/eeprom"))
local fd = sys.fileno(chip)
local old = assert(sys.ioctl(fd, "get"))
local saved = assert(io.open(BACKUP, "wb"))
saved:write(old)
assert(saved:close())
assert(sys.ioctl(fd, "set", image)) -- (true: done)
chip:close()
print("the EEPROM now boots " .. disk.id .. " only; the old image is in " .. BACKUP)
To undo it, flash the old image back: lua eeprom-flash.lua /home/eeprom.old.