From e5d34218fb8e249657a8bebe19061c57215dc3e5 Mon Sep 17 00:00:00 2001 From: Maxim Sobolev Date: Tue, 1 Aug 2006 22:19:01 +0000 Subject: [PATCH] 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 --- etc/mtree/BSD.include.dist | 2 + include/Makefile | 6 +- share/man/man4/man4.powerpc/Makefile | 7 + share/man/man4/man4.powerpc/powermac_nvram.4 | 58 +++ sys/conf/files.powerpc | 2 + sys/dev/powermac_nvram/powermac_nvram.c | 423 +++++++++++++++++++ sys/dev/powermac_nvram/powermac_nvramvar.h | 68 +++ sys/modules/Makefile | 2 + sys/modules/powermac_nvram/Makefile | 8 + sys/powerpc/conf/GENERIC | 3 + usr.sbin/Makefile | 2 + usr.sbin/nvram/Makefile | 8 + usr.sbin/nvram/nvram.8 | 119 ++++++ usr.sbin/nvram/nvram.c | 222 ++++++++++ 14 files changed, 929 insertions(+), 1 deletion(-) create mode 100644 share/man/man4/man4.powerpc/Makefile create mode 100644 share/man/man4/man4.powerpc/powermac_nvram.4 create mode 100644 sys/dev/powermac_nvram/powermac_nvram.c create mode 100644 sys/dev/powermac_nvram/powermac_nvramvar.h create mode 100644 sys/modules/powermac_nvram/Makefile create mode 100644 usr.sbin/nvram/Makefile create mode 100644 usr.sbin/nvram/nvram.8 create mode 100644 usr.sbin/nvram/nvram.c diff --git a/etc/mtree/BSD.include.dist b/etc/mtree/BSD.include.dist index 755545c00dc9..a77154176e40 100644 --- a/etc/mtree/BSD.include.dist +++ b/etc/mtree/BSD.include.dist @@ -56,6 +56,8 @@ .. ppbus .. + powermac_nvram + .. smbus .. speaker diff --git a/include/Makefile b/include/Makefile index b274a54af56f..b7b653af6fdd 100644 --- a/include/Makefile +++ b/include/Makefile @@ -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 diff --git a/share/man/man4/man4.powerpc/Makefile b/share/man/man4/man4.powerpc/Makefile new file mode 100644 index 000000000000..55d73adb8653 --- /dev/null +++ b/share/man/man4/man4.powerpc/Makefile @@ -0,0 +1,7 @@ +# $FreeBSD$ + +MAN= powermac_nvram.4 + +MANSUBDIR=/powerpc + +.include diff --git a/share/man/man4/man4.powerpc/powermac_nvram.4 b/share/man/man4/man4.powerpc/powermac_nvram.4 new file mode 100644 index 000000000000..e8b18efde5c9 --- /dev/null +++ b/share/man/man4/man4.powerpc/powermac_nvram.4 @@ -0,0 +1,58 @@ +.\"- +.\" Copyright (c) 2006 Maxim Sobolev +.\" 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. diff --git a/sys/conf/files.powerpc b/sys/conf/files.powerpc index a7f882dbcb53..5a2b3a15b4c0 100644 --- a/sys/conf/files.powerpc +++ b/sys/conf/files.powerpc @@ -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 diff --git a/sys/dev/powermac_nvram/powermac_nvram.c b/sys/dev/powermac_nvram/powermac_nvram.c new file mode 100644 index 000000000000..e131d6a99484 --- /dev/null +++ b/sys/dev/powermac_nvram/powermac_nvram.c @@ -0,0 +1,423 @@ +/* + * Copyright (c) 2006 Maxim Sobolev + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include + +#include + +#include +#include + +#include +#include + +/* + * 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; +} diff --git a/sys/dev/powermac_nvram/powermac_nvramvar.h b/sys/dev/powermac_nvram/powermac_nvramvar.h new file mode 100644 index 000000000000..6f4fb26e8990 --- /dev/null +++ b/sys/dev/powermac_nvram/powermac_nvramvar.h @@ -0,0 +1,68 @@ +/* + * Copyright (c) 2006 Maxim Sobolev + * 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_ */ diff --git a/sys/modules/Makefile b/sys/modules/Makefile index 701ba282363e..3c0d31520f3a 100644 --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -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 diff --git a/sys/modules/powermac_nvram/Makefile b/sys/modules/powermac_nvram/Makefile new file mode 100644 index 000000000000..a4d069366c08 --- /dev/null +++ b/sys/modules/powermac_nvram/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +.PATH: ${.CURDIR}/../../dev/powermac_nvram + +KMOD= powermac_nvram +SRCS= powermac_nvram.c powermac_nvramvar.h + +.include diff --git a/sys/powerpc/conf/GENERIC b/sys/powerpc/conf/GENERIC index f068105ec374..efa9fa8b4c69 100644 --- a/sys/powerpc/conf/GENERIC +++ b/sys/powerpc/conf/GENERIC @@ -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 diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile index ea8a426e8329..0ae29502e009 100644 --- a/usr.sbin/Makefile +++ b/usr.sbin/Makefile @@ -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" diff --git a/usr.sbin/nvram/Makefile b/usr.sbin/nvram/Makefile new file mode 100644 index 000000000000..7abbfff82b5e --- /dev/null +++ b/usr.sbin/nvram/Makefile @@ -0,0 +1,8 @@ +# $FreeBSD$ + +PROG= nvram +MAN= nvram.8 + +WARNS?= 6 + +.include diff --git a/usr.sbin/nvram/nvram.8 b/usr.sbin/nvram/nvram.8 new file mode 100644 index 000000000000..63e1c21c21ab --- /dev/null +++ b/usr.sbin/nvram/nvram.8 @@ -0,0 +1,119 @@ +.\"- +.\" Copyright (c) 2006 Maxim Sobolev +.\" 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. diff --git a/usr.sbin/nvram/nvram.c b/usr.sbin/nvram/nvram.c new file mode 100644 index 000000000000..9d56f9d537b1 --- /dev/null +++ b/usr.sbin/nvram/nvram.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 2006 Maxim Sobolev + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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; +}