Messages in this thread Patch in this message | | | Date | Tue, 25 Nov 1997 22:12:19 -0600 (CST) | From | Oliver Xymoron <> | Subject | ROM disk driver for 2.0.32 (patch!) |
| |
This is the ROM disk driver that I've been tinkering with. This is a block device that uses data directly linked into the driver. Together with romfs, this allows you to make a minimal kernel that can boot with just a zImage! This should prove useful for embedded systems.
Features: - low fat - the driver code and a minimal bootable image take less than a page - works as a module - can work with RLE-compressed disk images to (slightly) reduce memory requirements - might work on non-intel systems - completely experimental - uses an unregistered major number!
diff -urpN linux-orig/Documentation/Configure.help linux/Documentation/Configure.help --- linux-orig/Documentation/Configure.help Fri Sep 5 22:43:58 1997 +++ linux/Documentation/Configure.help Mon Nov 24 10:12:49 1997 @@ -112,6 +112,13 @@ CONFIG_BLK_DEV_INITRD "real" root file system, etc. See Documentation/initrd.txt for details. +ROM disk support +CONFIG_BLK_DEV_ROM + This allows you to embed a read-only filesystem image directly in the + kernel image. Combined with ROMFS, you can use this to create a minimal + kernel image that contains its own boot filesystem. See Documentation/rom.txt + for details. Most users should say N here. + Loop device support CONFIG_BLK_DEV_LOOP Enabling this option will allow you to mount a file as a file diff -urpN linux-orig/Documentation/rom.txt linux/Documentation/rom.txt --- linux-orig/Documentation/rom.txt Wed Dec 31 18:00:00 1969 +++ linux/Documentation/rom.txt Tue Nov 25 16:46:31 1997 @@ -0,0 +1,319 @@ +Using ROM disk support +====================== + +oxymoron@waste.org 11/97 + += Introduction = + +ROM disk allows you to embed a read-only filesystem image directly in +the kernel image. Combined with ROMFS, you can use this to create a +minimal kernel image that contains its own boot filesystem. This is +especially useful for embedded systems that have a minimum of memory +and no disk drives. The driver itself is very small and supports +run-length encoding of disk images, allowing the creation of boot +images as small as one page! + +The ROM disk driver works by building the filesystem data into an +array that is linked into the kernel. This array is represented as C +source code in drivers/block/romdisk.inc and can be built from an +existing disk image with the genromdisk perl script attached to the +end of this file. + += Building an image = + +To create a ROM disk image, first you must create a filesystem image +in another device or file. This can be done for instance with the genromfs +package. You can then use the attached perl script to create the romdisk.inc +source file: + + perl genromdisk < diskimage > drivers/block/romdisk.inc + +Then rebuild your kernel with ROM disk and appropriate filesystem +support (probably ROMFS). You will also want to create the appropriate +/dev entry for the device: + + mknod /dev/rom0 b 60 0 + +To build a bootable ROM disk image, you will need to build a +filesystem with at least the following files: + + /dev + console + /etc + init + +The following is the source for a minimal statically linked init: + +----init.c +/* + minimal init - oxymoron@waste.org + compile with gcc -s -static init.c -nostdlib -lc -o init +*/ +#include <unistd.h> +#include <fcntl.h> + +main() { + int fd; + fd=open("/dev/console",O_RDWR); + write(fd,"Init!\n",7); + for(;;); +} +---- + += Image Format = + +The image array include an 8 byte header, the first 4 bytes of which +indicate the size of the data, and the second 4 indicating encoding +format. All multibyte values are in network order. + +Encoding format 0 is uncompressed and the remainder of the array +consists of raw data bytes of the specified size. + +Encoding format 1 is run length encoded (RLE). Directly following the +header is an table of 4 byte offsets, one entry for each block in the +uncompressed image. These offsets are the number of bytes from the +beginning of the table to the start of the compressed data in the +image for a given block and reduce the overhead of decompression for +random access. + +The RLE compressed data begins immediately after the table. Run length +encoding replaces runs of identical bytes with an escape byte, +followed by a one byte repeat count, followed by the character to be +repeated. Escape bytes in the input are encoded as an escape byte and +a zero count in the output. Other single bytes are written directly. + +--- genromdisk --- +#!/usr/bin/perl +$blocksize=1024; +$escape=chr 167; + +while(<STDIN>) +{ + $disk.=$_; +} + +$size=length($disk); +if($size%$blocksize) +{ + $diff=($blocksize-($size%$blocksize)); + $disk.="\0"x$diff; + $size=length($disk); + warn "Padded $diff bytes to $size\n"; +} + +$blocknum=0; + +warn "Compressing...\n"; + +while($pos<$size) +{ + $pos[$blocknum]=length $cdisk; + $block=substr($disk,$pos,$blocksize); + + $s=length($block); + +# warn "Block $blocknum pos $pos length $s\n"; + + $last="foo"; + $run=0; + $i=0; + while($i<$blocksize) + { + $c=substr($block,$i,1); + $a=ord $c; + $hist{$a}++; + if($c eq $last) + { + $run++; + if($run eq 255) + { + $runs++; + $runhist{$a}++; + $runlength{$run-3}++; + $cdisk.=pack("ACA",$escape,$run,$c); + $run=0; + } + } + else + { + if($run) + { + if($last eq $escape) + { + $overhead++; + if($run==1) + { + $cdisk.=$escape."\0"; + } + if($run>1) + { + if($run>3) + { + $runhist{ord $escape}++; + $runlength{$run-3}++; + $runs++; + } + $cdisk.=pack("ACA",$escape,$run,$escape); + } + } + elsif($run == 1) + { + $cdisk.=$last; + } + elsif($run<4) + { + $cdisk.=$last x $run; + } + elsif($run>3) + { + $runs++; + $runhist{ord $last}++; + $runlength{$run-3}++; + $cdisk.=pack("ACA",$escape,$run,$last); + } + } + $last=$c; + $run=1; + } + $i++; + } + + if($run) + { + if($last eq $escape) + { + $overhead++; + if($run==1) + { + $cdisk.=$escape x 2; + } + if($run>1) + { + if($run>3) + { + $runhist{ord $escape}++; + $runlength{$run-3}++; + $runs++; + } + $cdisk.=pack("ACA",$escape,$run,$escape); + } + } + elsif($run == 1) + { + $cdisk.=$last; + } + elsif($run<4) + { + $cdisk.=$last x $run; + } + elsif($run>3) + { + $runs++; + $runhist{ord $last}++; + $runlength{$run}++; + $cdisk.=pack("ACA",$escape,$run,$last); + } + } + + $pos+=$blocksize; + $blocknum++; +} + +$offset=$blocknum*4; + +#open(DUMP,">cdisk"); +#print DUMP $cdisk; +#close DUMP; + +foreach $pos (@pos) +{ +# warn "$pos\n"; + $blockmap.=pack("N",$pos+$offset); +} + +$cdisk=$blockmap.$cdisk; + +print "/*\n"; +print "Escape: ",ord $escape," Block size: $blocksize\n"; +print "Input hist:\n"; +foreach $a (sort {$hist{$b}<=>$hist{$a}} keys %hist) +{ + print "$a\t$hist{$a}\n"; +} + +print "Runchar hist:\n"; +foreach $a (sort {$runhist{$b}<=>$runhist{$a}} keys %runhist) +{ + print "$a\t$runhist{$a}\n"; +} + +print "Runlength hist:\n"; +foreach $a (sort {$runlength{$b}*$b<=>$runlength{$a}*$a} keys %runlength) +{ + print "$a\t$runlength{$a} (",$runlength{$a}*$a,")\n"; +} + +print "*/\n"; + +warn "Original size: ",length $disk," compressed: ",length $cdisk," (o: $overhead r: $runs)\n"; + +if(length $cdisk < length $disk) +{ + $type=1; + $disk=$cdisk; +} + +$disk=pack("NN",$size,$type).$disk; +$size=length $disk; + +warn "Encoding ($size bytes)...\n"; + +print "const unsigned char romdisk[$size]=\n"; + +$i=0; +while($i<$size) +{ + $c=substr($disk,$i,1); + + $a=ord $c; + + if($c eq "\\") + { + $l.='\\'; + } + if($c eq "\"") + { + $l.='\"'; + } + elsif($a>31&&$a<127) + { + $l.=$c; + } + elsif($a==13) + { + $l.="\\r"; + } + elsif($a==10) + { + $l.="\\n"; + } + else + { + $l.="\\".int($a/64).int(($a%64)/8).int($a%8); + } + + $i++; + if(length $l>74||$i==$size) + { + print "\"$l\""; + $l=""; + print ";" if $i==$size; + print "\n"; + } +} + + + + + + diff -urpN linux-orig/drivers/block/Config.in linux/drivers/block/Config.in --- linux-orig/drivers/block/Config.in Mon Aug 4 13:45:55 1997 +++ linux/drivers/block/Config.in Fri Nov 21 16:40:17 1997 @@ -51,7 +51,7 @@ if [ "$CONFIG_BLK_DEV_RAM" = "y" ]; then bool ' Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD fi tristate 'XT harddisk support' CONFIG_BLK_DEV_XD - +tristate 'ROM block device support' CONFIG_BLK_DEV_ROM if [ "$CONFIG_BLK_DEV_HD_IDE" = "y" -o "$CONFIG_BLK_DEV_HD_ONLY" = "y" ]; then define_bool CONFIG_BLK_DEV_HD y diff -urpN linux-orig/drivers/block/Makefile linux/drivers/block/Makefile --- linux-orig/drivers/block/Makefile Mon Aug 4 13:45:55 1997 +++ linux/drivers/block/Makefile Fri Nov 21 16:44:21 1997 @@ -45,6 +45,30 @@ else endif endif +ifeq ($(CONFIG_BLK_DEV_ROM),y) +L_OBJS += rom.o +else + ifeq ($(CONFIG_BLK_DEV_ROM),m) + M_OBJS += rom.o + endif +endif + +ifeq ($(CONFIG_BLK_DEV_ROM),y) +L_OBJS += rom.o +else + ifeq ($(CONFIG_BLK_DEV_ROM),m) + M_OBJS += rom.o + endif +endif + +ifeq ($(CONFIG_BLK_DEV_ROM),y) +L_OBJS += rom.o +else + ifeq ($(CONFIG_BLK_DEV_ROM),m) + M_OBJS += rom.o + endif +endif + ifeq ($(CONFIG_BLK_DEV_HD),y) L_OBJS += hd.o endif diff -urpN linux-orig/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c --- linux-orig/drivers/block/ll_rw_blk.c Wed Feb 26 13:10:15 1997 +++ linux/drivers/block/ll_rw_blk.c Fri Nov 21 16:40:17 1997 @@ -684,5 +684,8 @@ int blk_dev_init(void) #ifdef CONFIG_BLK_DEV_MD md_init(); #endif CONFIG_BLK_DEV_MD +#ifdef CONFIG_BLK_DEV_ROM + rom_init(); +#endif return 0; } diff -urpN linux-orig/drivers/block/rom.c linux/drivers/block/rom.c --- linux-orig/drivers/block/rom.c Wed Dec 31 18:00:00 1969 +++ linux/drivers/block/rom.c Tue Nov 25 16:46:56 1997 @@ -0,0 +1,326 @@ +/* + * rom.c v0.1 oxymoron@waste.org 11/97 + * + * Based largely on: + * linux/drivers/block/loop.c + * Copyright 1993 by Theodore Ts'o. Redistribution of this file is + * permitted under the GNU Public License. + */ + +#include <linux/module.h> + +#include <linux/config.h> +#include <linux/fs.h> +#include <linux/stat.h> +#include <linux/errno.h> +#include <linux/major.h> +#include <linux/string.h> + +#include <asm/segment.h> +#include <asm/byteorder.h> + +#include <linux/rom.h> + +#define DEBUG_BLKDEV_ROM + +#define MAJOR_NR 60 + +#define DEVICE_NAME "rom" +#define DEVICE_REQUEST do_rom_request +#define DEVICE_NR(device) (MINOR(device)) +#define DEVICE_ON(device) +#define DEVICE_OFF(device) +#define DEVICE_NO_RANDOM +#define TIMEOUT_VALUE (6 * HZ) +#include <linux/blk.h> + +#define MAX_ROM 8 +static struct rom_device rom_dev[MAX_ROM]; +static int rom_sizes[MAX_ROM]; +static int rom_blksizes[MAX_ROM]; + +#define ESCAPE 167 +#define BLOCKSIZE 1024 + +#include "romdisk.inc" + +static void do_rom_request(void) +{ + unsigned int minor; + int offset, len; + struct rom_device *rom; + int block,boffset,run,c; + const unsigned char *data; + unsigned char *dest; + +#ifdef DEBUG_BLKDEV_ROM + printk("do_rom_request\n"); +#endif + +repeat: + INIT_REQUEST; + + minor=MINOR(CURRENT->rq_dev); + + if ((minor >= MAX_ROM) || + (CURRENT->cmd != READ)) { +#ifdef DEBUG_BLKDEV_ROM + printk(" bad minor or not read\n"); +#endif + end_request(0); + goto repeat; + } + + rom = &rom_dev[minor]; + + offset = CURRENT->sector << 9; + len = CURRENT->current_nr_sectors << 9; + +#ifdef DEBUG_BLKDEV_ROM + printk(" %x(%d)\n",offset,len); +#endif + + if((offset+len) > rom->length) { +#ifdef DEBUG_BLKDEV_ROM + printk(" too long\n"); +#endif + end_request(0); + goto repeat; + } + + if(!rom->rle) { + memcpy(CURRENT->buffer,rom->data+offset,len); + } + else { + dest=CURRENT->buffer; + + block=offset/BLOCKSIZE; /* Locate encoded block */ + boffset=offset%BLOCKSIZE; + data=rom->data+ntohl(*(long *)(rom->data+(block*4))); + +#ifdef DEBUG_BLKDEV_ROM + printk(" rle blk %d off %d data %p\n",block,boffset,data); +#endif + + while(boffset>0) { + if(*data != ESCAPE) { /* Normal character */ + boffset--; /* We've skipped a char */ + } + else { /* ESCAPE indicates RLE */ + data++; + run=*data; + if(run) { /* Run count? */ + data++; + c=(int)*data; + boffset-=run; + } + } + data++; + } + + if(boffset<0) { /* Did an RLE span take us too far? */ + boffset=-boffset>len?len:-boffset; + memset(dest,c,boffset); + len+=boffset; + dest+=boffset; + } + +#ifdef DEBUG_BLKDEV_ROM + printk(" cpy off %d len %d data %p\n",boffset,len,data); +#endif + + while(len) { + if(*data!=ESCAPE) { + *dest++=*data; + len--; + } + else { /* RLE */ + data++; + run=*data; + if(run) { + data++; + run=run>len?len:run; + memset(dest,*data,run); + dest+=run; + len-=run; + } + else { + *dest++=ESCAPE; + len--; + } + } + data++; + } + } + + end_request(1); + goto repeat; +} + +static int rom_ioctl(struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct rom_device *rom; + int dev, err; + +#ifdef DEBUG_BLKDEV_ROM + printk("rom_ioctl: %d:%d cmd %d arg %lx\n",MAJOR(inode->i_rdev), + MINOR(inode->i_rdev), cmd, arg); +#endif + + if (!inode) + return -EINVAL; + if (MAJOR(inode->i_rdev) != MAJOR_NR) { + printk("rom_ioctl: pseudo-major != %d\n", MAJOR_NR); + return -ENODEV; + } + dev = MINOR(inode->i_rdev); + if (dev >= MAX_ROM) + return -ENODEV; + rom = &rom_dev[dev]; + switch (cmd) { + case BLKGETSIZE: /* Return device size */ + if (!arg) return -EINVAL; + err = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long)); + if (err) + return err; + put_fs_long((rom->length)>>9, (long *) arg); +#ifdef DEBUG_BLKDEV_ROM + printk(" BLKGETSIZE= %ld\n",(rom->length)>>9); +#endif + + return 0; + default: + return -EINVAL; + } + return 0; +} + +static int rom_open(struct inode *inode, struct file *file) +{ + struct rom_device *rom; + int dev; + +#ifdef DEBUG_BLKDEV_ROM + printk("rom_open: %d\n",MINOR(inode->i_rdev)); +#endif + + if (!inode) + return -EINVAL; + if (MAJOR(inode->i_rdev) != MAJOR_NR) { + printk("rom_open: pseudo-major != %d\n", MAJOR_NR); + return -ENODEV; + } + dev = MINOR(inode->i_rdev); + if (dev >= MAX_ROM) + return -ENODEV; + rom = &rom_dev[dev]; + rom->rom_refcnt++; + MOD_INC_USE_COUNT; + +#ifdef DEBUG_BLKDEV_ROM + printk(" rom %d: %p (%ld) (%d)\n", + dev,rom->data,rom->length,rom->rom_refcnt); +#endif + return 0; +} + +static void rom_release(struct inode *inode, struct file *file) +{ + struct rom_device *rom; + int dev; + +#ifdef DEBUG_BLKDEV_ROM + printk("rom_release: %d\n",MINOR(inode->i_rdev)); +#endif + + if (!inode) + return; + if (MAJOR(inode->i_rdev) != MAJOR_NR) { + printk("rom_release: pseudo-major != %d\n", MAJOR_NR); + return; + } + dev = MINOR(inode->i_rdev); + if (dev >= MAX_ROM) + return; + fsync_dev(inode->i_rdev); + rom = &rom_dev[dev]; + if (rom->rom_refcnt <= 0) + printk("rom_release: refcount(%d) <= 0\n", rom->rom_refcnt); + else { + rom->rom_refcnt--; + MOD_DEC_USE_COUNT; + } +} + +static struct file_operations rom_fops = { + NULL, /* lseek - default */ + block_read, /* read - general block-dev read */ + block_write, /* write - general block-dev write */ + NULL, /* readdir - bad */ + NULL, /* select */ + rom_ioctl, /* ioctl */ + NULL, /* mmap */ + rom_open, /* open */ + rom_release /* release */ +}; + +/* + * And now the modules code and kernel interface. + */ +#ifdef MODULE +#define rom_init init_module +#endif + +int +rom_init( void ) { + int i; + +#ifdef DEBUG_BLKDEV_ROM + printk("*** rom_init\n"); +#endif + + if (register_blkdev(MAJOR_NR, "rom", &rom_fops)) { + printk("Unable to get major number %d for rom device\n", + MAJOR_NR); + return -EIO; + } +#ifndef MODULE + printk("rom: registered device at major %d\n", MAJOR_NR); +#endif + + blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST; + + for (i=0; i < MAX_ROM; i++) { + memset(&rom_dev[i], 0, sizeof(struct rom_device)); + rom_sizes[i]=0; + rom_blksizes[i]=BLOCKSIZE; + } + + blk_size[MAJOR_NR] = rom_sizes; + blksize_size[MAJOR_NR] = rom_blksizes; + + rom_dev[0].length=ntohl(*(long *)romdisk); + rom_dev[0].rle=(int)ntohl(*(long *)(romdisk+4)); + rom_dev[0].data=romdisk+8; + rom_sizes[0]=rom_dev[0].length/BLOCKSIZE; + +#ifdef DEBUG_BLKDEV_ROM + printk("romdisk: %p\n",romdisk); + printk("rom_dev[0]: loc %p data %p length %ld\n", + &rom_dev[0],rom_dev[0].data,rom_dev[0].length); +#endif + + return 0; +} + +#ifdef MODULE +void +cleanup_module( void ) { +#ifdef DEBUG_BLKDEV_ROM + printk("rom: cleanup_module\n"); +#endif + if (unregister_blkdev(MAJOR_NR, "rom") != 0) + printk("rom: cleanup_module failed\n"); +} +#endif diff -urpN linux-orig/drivers/block/romdisk.inc linux/drivers/block/romdisk.inc --- linux-orig/drivers/block/romdisk.inc Wed Dec 31 18:00:00 1969 +++ linux/drivers/block/romdisk.inc Tue Nov 25 16:47:35 1997 @@ -0,0 +1 @@ +#error "You haven't created a romdisk! See Documentation/rom.txt" diff -urpN linux-orig/include/linux/rom.h linux/include/linux/rom.h --- linux-orig/include/linux/rom.h Wed Dec 31 18:00:00 1969 +++ linux/include/linux/rom.h Tue Nov 25 16:33:12 1997 @@ -0,0 +1,15 @@ +#ifndef _LINUX_ROM_H +#define _LINUX_ROM_H + +#ifdef __KERNEL__ + +struct rom_device { + int rle; + int rom_refcnt; + const unsigned char *data; + long length; +}; + +#endif /* __KERNEL__ */ +#endif + -- "Love the dolphins," she advised him. "Write by W.A.S.T.E.."
| |