Add device to access and modify Open Firmware NVRAM settings in

PowerPC-based Apple's machines and small utility to do it from
userland modelled after the similar utility in Darwin/OSX.

Only tested on 1.25GHz G4 Mac Mini.

MFC after:	1 month
This commit is contained in:
Maxim Sobolev 2006-08-01 22:19:01 +00:00
parent 78985e424a
commit e5d34218fb
Notes: svn2git 2020-12-20 02:59:44 +00:00
svn path=/head/; revision=160892
14 changed files with 929 additions and 1 deletions

View File

@ -56,6 +56,8 @@
..
ppbus
..
powermac_nvram
..
smbus
..
speaker

View File

@ -41,7 +41,7 @@ LDIRS= bsm cam geom net net80211 netatalk netatm netgraph netinet netinet6 \
LSUBDIRS= cam/scsi \
dev/acpica dev/an dev/bktr dev/firewire dev/hwpmc \
dev/ic dev/iicbus ${_dev_ieee488} dev/lmc dev/ofw \
dev/pbio dev/ppbus dev/smbus dev/speaker dev/usb dev/wi dev/utopia \
dev/pbio ${_dev_powermac_nvram} dev/ppbus dev/smbus dev/speaker dev/usb dev/wi dev/utopia \
fs/devfs fs/fdescfs fs/fifofs fs/msdosfs fs/ntfs fs/nullfs \
${_fs_nwfs} fs/portalfs fs/procfs fs/smbfs fs/udf fs/umapfs \
fs/unionfs \
@ -55,6 +55,10 @@ LSUBDIRS= cam/scsi \
security/mac_mls security/mac_partition \
ufs/ffs ufs/ufs
.if ${MACHINE_ARCH} == "powerpc"
_dev_powermac_nvram= dev/powermac_nvram
.endif
.if ${MK_GPIB} != "no"
_dev_ieee488= dev/ieee488
.endif

View File

@ -0,0 +1,7 @@
# $FreeBSD$
MAN= powermac_nvram.4
MANSUBDIR=/powerpc
.include <bsd.prog.mk>

View File

@ -0,0 +1,58 @@
.\"-
.\" Copyright (c) 2006 Maxim Sobolev <sobomax@FreeBSD.org>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd August 1, 2006
.Dt POWERMAC_NVRAM 4 powerpc
.Os
.Sh NAME
.Nm powermac_nvram
.Nd "non-volatile RAM"
.Sh SYNOPSIS
.Cd "device powermac_nvram"
.Sh DESCRIPTION
The
.Nm
driver provides access to the Open Firmware configuration NVRAM
available on the Apple PowerPC-based machines.
.Sh SEE ALSO
.Xr nvram 8 ,
.Xr eeprom 8
.Sh HISTORY
The
.Nm
driver first appeared in
.Fx 7.0 .
.Sh AUTHORS
The
.Nm
driver was written by
.An Maxim Sobolev Aq sobomax@FreeBSD.org .
.Sh BUGS
Currently,
.Nm
driver only supports systems equipped with AMD flash part and is only
tested on Apple G4-based Mac Mini machines.

View File

@ -128,3 +128,5 @@ powerpc/psim/openpic_iobus.c optional psim
powerpc/psim/uart_iobus.c optional uart psim
dev/scc/scc_bfe_macio.c optional scc powermac
dev/powermac_nvram/powermac_nvram.c optional powermac powermac_nvram

View File

@ -0,0 +1,423 @@
/*
* Copyright (c) 2006 Maxim Sobolev <sobomax@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/conf.h>
#include <sys/kernel.h>
#include <sys/uio.h>
#include <dev/ofw/openfirm.h>
#include <dev/ofw/ofw_pci.h>
#include <machine/bus.h>
#include <machine/md_var.h>
#include <machine/nexusvar.h>
#include <machine/resource.h>
#include <sys/rman.h>
#include <powerpc/ofw/ofw_pci.h>
#include <dev/powermac_nvram/powermac_nvramvar.h>
#include <vm/vm.h>
#include <vm/pmap.h>
/*
* Device interface.
*/
static int powermac_nvram_probe(device_t);
static int powermac_nvram_attach(device_t);
static int powermac_nvram_detach(device_t);
/* Helper functions */
static int powermac_nvram_check(void *data);
static int chrp_checksum(int sum, uint8_t *, uint8_t *);
static uint32_t adler_checksum(uint8_t *, int);
static int erase_bank(device_t, uint8_t *);
static int write_bank(device_t, uint8_t *, uint8_t *);
/*
* Driver methods.
*/
static device_method_t powermac_nvram_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, powermac_nvram_probe),
DEVMETHOD(device_attach, powermac_nvram_attach),
DEVMETHOD(device_detach, powermac_nvram_detach),
{ 0, 0 }
};
static driver_t powermac_nvram_driver = {
"powermac_nvram",
powermac_nvram_methods,
sizeof(struct powermac_nvram_softc)
};
static devclass_t powermac_nvram_devclass;
DRIVER_MODULE(powermac_nvram, nexus, powermac_nvram_driver, powermac_nvram_devclass, 0, 0);
/*
* Cdev methods.
*/
#define NVRAM_UNIT(dev) minor(dev)
#define NVRAM_SOFTC(unit) ((struct powermac_nvram_softc *) \
devclass_get_softc(powermac_nvram_devclass, unit))
static d_open_t powermac_nvram_open;
static d_close_t powermac_nvram_close;
static d_read_t powermac_nvram_read;
static d_write_t powermac_nvram_write;
static struct cdevsw powermac_nvram_cdevsw = {
.d_version = D_VERSION,
.d_flags = D_NEEDGIANT,
.d_open = powermac_nvram_open,
.d_close = powermac_nvram_close,
.d_read = powermac_nvram_read,
.d_write = powermac_nvram_write,
.d_name = "powermac_nvram",
};
static int
powermac_nvram_probe(device_t dev)
{
char *type, *compatible;
type = nexus_get_device_type(dev);
compatible = nexus_get_compatible(dev);
if (type == NULL || compatible == NULL)
return ENXIO;
if (strcmp(type, "nvram") != 0 || strcmp(compatible, "amd-0137") != 0)
return ENXIO;
device_set_desc(dev, "Apple NVRAM");
return 0;
}
static int
powermac_nvram_attach(device_t dev)
{
struct powermac_nvram_softc *sc;
phandle_t node;
u_int32_t reg[2];
int gen0, gen1;
node = nexus_get_node(dev);
sc = device_get_softc(dev);
if (OF_getprop(node, "reg", reg, sizeof(reg)) < 8)
return ENXIO;
sc->sc_dev = dev;
sc->sc_node = node;
sc->sc_bank0 = (vm_offset_t)pmap_mapdev(reg[0], NVRAM_SIZE * 2);
sc->sc_bank1 = sc->sc_bank0 + NVRAM_SIZE;
gen0 = powermac_nvram_check((void *)sc->sc_bank0);
gen1 = powermac_nvram_check((void *)sc->sc_bank1);
if (gen0 == -1 && gen1 == -1) {
if ((void *)sc->sc_bank0 != NULL)
pmap_unmapdev(sc->sc_bank0, NVRAM_SIZE * 2);
device_printf(dev, "both banks appear to be corrupt\n");
return ENXIO;
}
device_printf(dev, "bank0 generation %d, bank1 generation %d\n",
gen0, gen1);
sc->sc_bank = (gen0 > gen1) ? sc->sc_bank0 : sc->sc_bank1;
bcopy((void *)sc->sc_bank, (void *)sc->sc_data, NVRAM_SIZE);
sc->sc_cdev = make_dev(&powermac_nvram_cdevsw, 0, 0, 0, 0600,
"powermac_nvram");
return 0;
}
static int
powermac_nvram_detach(device_t dev)
{
struct powermac_nvram_softc *sc;
sc = device_get_softc(dev);
if ((void *)sc->sc_bank0 != NULL)
pmap_unmapdev(sc->sc_bank0, NVRAM_SIZE * 2);
if (sc->sc_cdev != NULL)
destroy_dev(sc->sc_cdev);
return 0;
}
static int
powermac_nvram_open(struct cdev *dev, int flags, int fmt, struct thread *td)
{
struct powermac_nvram_softc *sc;
sc = NVRAM_SOFTC(NVRAM_UNIT(dev));
if (sc->sc_isopen)
return EBUSY;
sc->sc_isopen = 1;
sc->sc_rpos = sc->sc_wpos = 0;
return 0;
}
static int
powermac_nvram_close(struct cdev *dev, int fflag, int devtype, struct thread *td)
{
struct powermac_nvram_softc *sc;
struct core99_header *header;
vm_offset_t bank;
sc = NVRAM_SOFTC(NVRAM_UNIT(dev));
if (sc->sc_wpos != sizeof(sc->sc_data)) {
/* Short write, restore in-memory copy */
bcopy((void *)sc->sc_bank, (void *)sc->sc_data, NVRAM_SIZE);
sc->sc_isopen = 0;
return 0;
}
header = (struct core99_header *)sc->sc_data;
header->generation = ((struct core99_header *)sc->sc_bank)->generation;
header->generation++;
header->chrp_header.signature = CORE99_SIGNATURE;
header->adler_checksum =
adler_checksum((uint8_t *)&(header->generation),
NVRAM_SIZE - offsetof(struct core99_header, generation));
header->chrp_header.chrp_checksum = chrp_checksum(header->chrp_header.signature,
(uint8_t *)&(header->chrp_header.length),
(uint8_t *)&(header->adler_checksum));
bank = (sc->sc_bank == sc->sc_bank0) ? sc->sc_bank1 : sc->sc_bank0;
if (erase_bank(sc->sc_dev, (uint8_t *)bank) != 0 ||
write_bank(sc->sc_dev, (uint8_t *)bank, sc->sc_data) != 0) {
sc->sc_isopen = 0;
return ENOSPC;
}
sc->sc_bank = bank;
sc->sc_isopen = 0;
return 0;
}
static int
powermac_nvram_read(struct cdev *dev, struct uio *uio, int ioflag)
{
int rv, amnt, data_available;
struct powermac_nvram_softc *sc;
sc = NVRAM_SOFTC(NVRAM_UNIT(dev));
rv = 0;
while (uio->uio_resid > 0) {
data_available = sizeof(sc->sc_data) - sc->sc_rpos;
if (data_available > 0) {
amnt = MIN(uio->uio_resid, data_available);
rv = uiomove((void *)(sc->sc_data + sc->sc_rpos),
amnt, uio);
if (rv != 0)
break;
sc->sc_rpos += amnt;
} else {
break;
}
}
return rv;
}
static int
powermac_nvram_write(struct cdev *dev, struct uio *uio, int ioflag)
{
int rv, amnt, data_available;
struct powermac_nvram_softc *sc;
sc = NVRAM_SOFTC(NVRAM_UNIT(dev));
if (sc->sc_wpos >= sizeof(sc->sc_data))
return EINVAL;
rv = 0;
while (uio->uio_resid > 0) {
data_available = sizeof(sc->sc_data) - sc->sc_wpos;
if (data_available > 0) {
amnt = MIN(uio->uio_resid, data_available);
rv = uiomove((void *)(sc->sc_data + sc->sc_wpos),
amnt, uio);
if (rv != 0)
break;
sc->sc_wpos += amnt;
} else {
break;
}
}
return rv;
}
static int
powermac_nvram_check(void *data)
{
struct core99_header *header;
header = (struct core99_header *)data;
if (header->chrp_header.signature != CORE99_SIGNATURE)
return -1;
if (header->chrp_header.chrp_checksum !=
chrp_checksum(header->chrp_header.signature,
(uint8_t *)&(header->chrp_header.length),
(uint8_t *)&(header->adler_checksum)))
return -1;
if (header->adler_checksum !=
adler_checksum((uint8_t *)&(header->generation),
NVRAM_SIZE - offsetof(struct core99_header, generation)))
return -1;
return header->generation;
}
static int
chrp_checksum(int sum, uint8_t *data, uint8_t *end)
{
for (; data < end; data++)
sum += data[0];
while (sum > 0xff)
sum = (sum & 0xff) + (sum >> 8);
return sum;
}
static uint32_t
adler_checksum(uint8_t *data, int len)
{
uint32_t low, high;
int i;
low = 1;
high = 0;
for (i = 0; i < len; i++) {
if ((i % 5000) == 0) {
high %= 65521UL;
high %= 65521UL;
}
low += data[i];
high += low;
}
low %= 65521UL;
high %= 65521UL;
return (high << 16) | low;
}
#define OUTB_DELAY(a, v) outb(a, v); DELAY(1);
static int
wait_operation_complete(uint8_t *bank)
{
int i;
for (i = 1000000; i != 0; i--)
if ((inb(bank) ^ inb(bank)) == 0)
return 0;
return -1;
}
static int
erase_bank(device_t dev, uint8_t *bank)
{
unsigned int i;
/* Unlock 1 */
OUTB_DELAY(bank + 0x555, 0xaa);
/* Unlock 2 */
OUTB_DELAY(bank + 0x2aa, 0x55);
/* Sector-Erase */
OUTB_DELAY(bank + 0x555, 0x80);
OUTB_DELAY(bank + 0x555, 0xaa);
OUTB_DELAY(bank + 0x2aa, 0x55);
OUTB_DELAY(bank, 0x30);
if (wait_operation_complete(bank) != 0) {
device_printf(dev, "flash erase timeout\n");
return -1;
}
/* Reset */
OUTB_DELAY(bank, 0xf0);
for (i = 0; i < NVRAM_SIZE; i++) {
if (bank[i] != 0xff) {
device_printf(dev, "flash erase has failed\n");
return -1;
}
}
return 0;
}
static int
write_bank(device_t dev, uint8_t *bank, uint8_t *data)
{
unsigned int i;
for (i = 0; i < NVRAM_SIZE; i++) {
/* Unlock 1 */
OUTB_DELAY(bank + 0x555, 0xaa);
/* Unlock 2 */
OUTB_DELAY(bank + 0x2aa, 0x55);
/* Write single word */
OUTB_DELAY(bank + 0x555, 0xa0);
OUTB_DELAY(bank + i, data[i]);
if (wait_operation_complete(bank) != 0) {
device_printf(dev, "flash write timeout\n");
return -1;
}
}
/* Reset */
OUTB_DELAY(bank, 0xf0);
for (i = 0; i < NVRAM_SIZE; i++) {
if (bank[i] != data[i]) {
device_printf(dev, "flash write has failed\n");
return -1;
}
}
return 0;
}

View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2006 Maxim Sobolev <sobomax@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#ifndef _POWERPC_POWERMAC_POWERMAC_NVRAMVAR_H_
#define _POWERPC_POWERMAC_POWERMAC_NVRAMVAR_H_
#define NVRAM_SIZE 0x2000
#define CORE99_SIGNATURE 0x5a
#ifdef _KERNEL
struct powermac_nvram_softc {
device_t sc_dev;
phandle_t sc_node;
vm_offset_t sc_bank;
vm_offset_t sc_bank0;
vm_offset_t sc_bank1;
uint8_t sc_data[NVRAM_SIZE];
struct cdev * sc_cdev;
int sc_isopen;
int sc_rpos;
int sc_wpos;
};
#endif /* _KERNEL */
struct chrp_header {
uint8_t signature;
uint8_t chrp_checksum;
uint16_t length;
char name[12];
};
struct core99_header {
struct chrp_header chrp_header;
uint32_t adler_checksum;
uint32_t generation;
uint32_t reserved[2];
};
#endif /* _POWERPC_POWERMAC_POWERMAC_NVRAMVAR_H_ */

View File

@ -190,6 +190,7 @@ SUBDIR= ${_3dfx} \
plip \
${_pmc} \
portalfs \
${_powermac_nvram} \
ppbus \
ppc \
ppi \
@ -521,6 +522,7 @@ _xe= xe
_an= an
_ath_hal= ath_hal
_gem= gem
_powermac_nvram= powermac_nvram
_smbfs= smbfs
.endif

View File

@ -0,0 +1,8 @@
# $FreeBSD$
.PATH: ${.CURDIR}/../../dev/powermac_nvram
KMOD= powermac_nvram
SRCS= powermac_nvram.c powermac_nvramvar.h
.include <bsd.kmod.mk>

View File

@ -145,6 +145,9 @@ device firewire # FireWire bus code
device sbp # SCSI over FireWire (Requires scbus and da)
device fwe # Ethernet over FireWire (non-standard!)
# Misc
device powermac_nvram # Open Firmware configuration NVRAM
options KTR
options KTR_COMPILE=0xffffffff
#options KTR_MASK=KTR_SIG

View File

@ -110,6 +110,7 @@ SUBDIR= ac \
nghook \
nologin \
ntp \
${_nvram} \
${_ofwdump} \
${_pccard} \
pciconf \
@ -369,6 +370,7 @@ _zzz= zzz
.if ${MACHINE_ARCH} == "powerpc"
_mount_smbfs= mount_smbfs
_nvram= nvram
.endif
.if ${MACHINE_ARCH} == "sparc64"

8
usr.sbin/nvram/Makefile Normal file
View File

@ -0,0 +1,8 @@
# $FreeBSD$
PROG= nvram
MAN= nvram.8
WARNS?= 6
.include <bsd.prog.mk>

119
usr.sbin/nvram/nvram.8 Normal file
View File

@ -0,0 +1,119 @@
.\"-
.\" Copyright (c) 2006 Maxim Sobolev <sobomax@FreeBSD.org>
.\" All rights reserved.
.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
.\" 1. Redistributions of source code must retain the above copyright
.\" notice, this list of conditions and the following disclaimer.
.\" 2. Redistributions in binary form must reproduce the above copyright
.\" notice, this list of conditions and the following disclaimer in the
.\" documentation and/or other materials provided with the distribution.
.\"
.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
.\" POSSIBILITY OF SUCH DAMAGE.
.\"
.\" $FreeBSD$
.\"
.Dd August 1, 2006
.Dt NVRAM 8 powerpc
.Os
.Sh NAME
.Nm nvram
.Nd "display or modify contents of the EEPROM or NVRAM"
.Sh SYNOPSIS
.Nm
.Fl p
.Nm
.Op Fl d Ar name
.Op Ar name Ns = Ns Ar value
.Ar ...
.Sh DESCRIPTION
The
.Nm
utility provides an interface for displaying and changing the system's
configuration variables contained in EEPROM or NVRAM.
In the first synopsis form, all available configuration variables and their
current values are printed.
In the second form, the variable selected by
.Ar name
is either removed or its value is changed to the
.Ar value
following by
.Ql =
sign.
.Pp
The following options are available:
.Bl -tag -width indent
.It Fl d Ar name
Delete the variable selected by
.Ar name
from the EEPROM or NVRAM. The
.Fl d
flag can be specified multiple times, in which case multiple variables
will be removed.
.It Fl p
Print all available configuration variables and their current values.
.El
.Sh EXAMPLES
Print all available configuration variables and their current values:
.Pp
.Dl "nvram -p"
.Pp
Remove the variable named
.Va local-mac-address? :
.Pp
.Dl "nvram -d local-mac-address\e?"
.Pp
Set the value of the
.Va local-mac-address?
variable to
.Dq Li true :
.Pp
.Dl "nvram local-mac-address\e?=true"
.Pp
Note that the
.Ql \e
in the above examples is used to keep the shell from interpreting the
.Ql \&? .
.Pp
Remove variables named
.Va foo
and
.Va bar
and set variable named
.Va baz
to
.Dq Li 100 :
.Pp
.Dl "nvram -d foo -d bar baz=100"
.Ed
.Sh SEE ALSO
.Xr powermac_nvram 4 ,
.Xr eeprom 8
.Sh HISTORY
The
.Nm
utility first appeared in
.Fx 7.0 .
It is inspired by the
Darwin/Mac OS X
.Xr nvram 8
utility.
.Sh AUTHORS
.An Maxim Sobolev Aq sobomax@FreeBSD.org .
.Sh BUGS
Currently,
.Nm
only supports systems equipped with AMD flash and is only tested on Apple
G4-based Mac Mini machines.

222
usr.sbin/nvram/nvram.c Normal file
View File

@ -0,0 +1,222 @@
/*
* Copyright (c) 2006 Maxim Sobolev <sobomax@FreeBSD.org>
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* $FreeBSD$
*/
#include <sys/types.h>
#include <sys/uio.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dev/powermac_nvram/powermac_nvramvar.h>
#define DEVICE_NAME (_PATH_DEV "powermac_nvram")
static void usage(void);
static int remove_var(uint8_t *, int, const char *);
static int append_var(uint8_t *, int, const char *, const char *);
struct deletelist {
char *name;
struct deletelist *next;
struct deletelist *last;
};
int
main(int argc, char **argv)
{
int opt, dump, fd, res, i, size;
uint8_t buf[NVRAM_SIZE], *cp, *common;
struct chrp_header *header;
struct deletelist *dl;
dump = 0;
dl = NULL;
while((opt = getopt(argc, argv, "d:p")) != -1) {
switch(opt) {
case 'p':
dump = 1;
break;
case 'd':
if (dl == NULL) {
dl = malloc(sizeof(*dl));
if (dl == NULL)
err(1, "malloc");
bzero(dl, sizeof(*dl));
dl->last = dl;
} else {
dl->last->next = malloc(sizeof(*dl));
if (dl->last->next == NULL)
err(1, "malloc");
dl->last = dl->last->next;
bzero(dl->last, sizeof(*dl));
}
dl->last->name = optarg;
break;
default:
usage();
/* Not reached */
}
}
argc -= optind;
argv += optind;
if (argc == 0 && dump == 0 && dl == NULL) {
usage();
/* Not reached */
}
fd = open(DEVICE_NAME, O_RDWR);
if (fd == -1)
err(1, DEVICE_NAME);
for (i = 0; i < (int)sizeof(buf);) {
res = read(fd, buf + i, sizeof(buf) - i);
if (res == -1 && errno != EINTR)
err(1, DEVICE_NAME);
if (res == 0)
break;
if (res > 0)
i += res;
}
if (i != sizeof(buf))
errx(1, "%s: short read", DEVICE_NAME);
/* Locate common block */
size = 0;
for (cp = buf; cp < buf + sizeof(buf); cp += size) {
header = (struct chrp_header *)cp;
size = header->length * 0x10;
if (strncmp(header->name, "common", 7) == 0)
break;
}
if (cp >= buf + sizeof(buf) || size <= (int)sizeof(struct chrp_header))
errx(1, "%s: no common block", DEVICE_NAME);
common = cp + sizeof(struct chrp_header);
size -= sizeof(struct chrp_header);
if (dump != 0) {
while (size > 0) {
i = strlen(common) + 1;
if (i == 1)
break;
printf("%s\n", common);
size -= i;
common += i;
}
exit(0);
}
for (;dl != NULL; dl = dl->next) {
if (remove_var(common, size, dl->name) == 0)
warnx("%s: no such variable", dl->name);
}
for (; argc > 0; argc--, argv++) {
cp = strchr(*argv, '=');
if (cp == NULL)
errx(1, "%s: invalid argument", *argv);
cp[0] = '\0';
cp++;
remove_var(common, size, *argv);
if (append_var(common, size, *argv, cp) == -1)
errx(1, "%s: error setting variable", *argv);
}
for (i = 0; i < (int)sizeof(buf);) {
res = write(fd, buf + i, sizeof(buf) - i);
if (res == -1 && errno != EINTR)
err(1, DEVICE_NAME);
if (res == 0)
break;
if (res > 0)
i += res;
}
if (i != sizeof(buf))
errx(1, "%s: short write", DEVICE_NAME);
if (close(fd) == -1)
err(1, DEVICE_NAME);
exit(0);
}
static void
usage(void)
{
fprintf(stderr, "usage: nvram [-p] | [-d name ...] [name=value ...]\n");
exit(1);
}
static int
remove_var(uint8_t *buf, int len, const char *var_name)
{
int nremoved, i, name_len;
nremoved = 0;
name_len = strlen(var_name);
while (len > 0) {
i = strlen(buf) + 1;
if (i == 1)
break;
if (strncmp(buf, var_name, name_len) == 0 && buf[name_len] == '=') {
memmove(buf, buf + i, len - i);
memset(buf + len - i, '\0', i);
nremoved += 1;
continue;
}
len -= i;
buf += i;
}
return nremoved;
}
static int
append_var(uint8_t *buf, int len, const char *var_name, const char *var_value)
{
int i, append_len;
while (len > 0) {
i = strlen(buf) + 1;
if (i == 1)
break;
len -= i;
buf += i;
}
append_len = strlen(var_name) + strlen(var_value) + 2;
if (len < append_len)
return -1;
sprintf(buf, "%s=%s", var_name, var_value);
return 0;
}