import uloader-2.4.17
[uloader.git] / uloader_mod.c
1 /*
2  * uloader_mod.c
3  *
4  * Loads a RAM build of u-boot and starts it
5  * The load address is hardcoded to 0x03f00000 (for LS HLAN)
6  *
7  * Copyright (C) 2006 Mihai Georgian <u-boot@linuxnotincluded.org.uk>
8  *
9  * This program is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU General Public License as
11  * published by the Free Software Foundation; either version 2 of
12  * the License, or (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
22  * MA 02111-1307 USA
23  *
24  * Based on:
25  *   loader_mod.c by Chih-Chung Chang <jochang@gmail.com>
26  */
27 #include <linux/module.h>
28 #include <linux/kernel.h>
29 #include <asm/uaccess.h>
30 #include <linux/file.h>
31 #include <linux/mm.h>
32 #include <linux/vmalloc.h>
33 #include <linux/slab.h>
34 #include <linux/reboot.h>
35
36 MODULE_LICENSE("GPL");
37 MODULE_AUTHOR("Mihai Georgian <u-boot@linuxnotincluded.org.uk>");
38 MODULE_PARM(uboot, "s");
39 MODULE_PARM(laddr, "l");
40 MODULE_PARM_DESC(uboot, "u-boot file name (must be binary)");
41 MODULE_PARM_DESC(laddr, "u-boot load address");
42
43 static char* uboot = "u-boot.bin";
44 static unsigned long laddr = 0x03f00000;
45
46 #define LOW_MEM 0x800000  /* no smaller than kernel + 1M (bss) + initrd */
47
48 void load_uboot(unsigned long pa_load_uboot,
49                 unsigned long pa_uboot_buf,
50                 unsigned long laddr);
51
52 struct indirect_buffer {
53   int size;
54   unsigned long paddr[1]; /* physical address of each 4K page */
55                           /* terminate with zero */
56 };
57 static struct indirect_buffer *uboot_buf;
58
59 static int uloader_notify_reboot (struct notifier_block *this, \
60                                   unsigned long event, void *x);
61 static struct notifier_block uloader_notifier = {
62   uloader_notify_reboot,
63   NULL,
64   0
65 };
66
67 #define MAX_INDIRECT_BUFFER_SIZE ((PAGE_SIZE/4-1)*PAGE_SIZE)
68
69 /*
70  *  Allocate a page with physical address >= LOWMEM
71  */
72 static void **save;
73 static int saved_pages;
74 static void *alloc_high_page(void)
75 {
76   void *ptr;
77   if(!save)
78   {
79     save = vmalloc(((LOW_MEM+PAGE_SIZE-1)/PAGE_SIZE)*4);
80     if(!save) return 0;
81   }
82
83   while(1)
84   {
85     ptr = kmalloc(PAGE_SIZE, GFP_KERNEL);
86     if(!ptr) return 0;
87     if(__pa(ptr) >= LOW_MEM) break;
88     save[saved_pages++] = ptr;
89   }
90   return ptr;
91 }
92
93 static void free_saved_pages(void)
94 {
95   if(save)
96   {
97     int i;
98     for(i=0;i<saved_pages;i++)
99       kfree(save[i]);
100     vfree(save);
101   }
102 }
103
104 static void free_ibuffer(struct indirect_buffer *ibuf);
105
106 /*
107  *  Read input file into an indirect buffer
108  */
109 static int read_file(char *filename, struct indirect_buffer **indirect_buf)
110 {
111   struct file* file;
112   struct inode* inode;
113   struct indirect_buffer *ibuf;
114   size_t size, got, i;
115   mm_segment_t fs;
116   int err;
117
118   file = filp_open(filename, O_RDONLY, 0);
119   if(IS_ERR(file))
120     return PTR_ERR(file);
121
122   err = -EIO;
123   if(!file->f_op || !file->f_op->read)
124     goto out;
125
126   err = -EACCES;
127   inode = file->f_dentry->d_inode;
128   if(!S_ISREG(inode->i_mode))
129     goto out;
130
131   err = -ENOMEM;
132   ibuf = (struct indirect_buffer*)alloc_high_page();
133   if(!ibuf) goto out;
134   memset(ibuf, 0, PAGE_SIZE);
135
136   if(inode->i_size > MAX_INDIRECT_BUFFER_SIZE) goto out2;
137   size = (size_t)inode->i_size;
138   ibuf->size = size;
139
140   for(i=0;i<size;i+=PAGE_SIZE)
141   {
142     size_t todo = min(size-i, (size_t)PAGE_SIZE);
143     void *buf;
144
145     err = -ENOMEM;
146     buf = alloc_high_page();
147     if(!buf) goto out2;
148     ibuf->paddr[i/PAGE_SIZE] = __pa(buf);
149
150     err = -EIO;
151     file->f_pos = i;
152     fs = get_fs();
153     set_fs(KERNEL_DS);
154     got = file->f_op->read(file, buf, todo, &file->f_pos);
155     set_fs(fs);
156     if(got != todo) goto out2;
157   }
158
159   *indirect_buf = ibuf;
160   err = 0;
161
162 out:
163   filp_close(file, NULL);
164   return err;
165 out2:
166   free_ibuffer(ibuf);
167   goto out;
168 }
169
170 static void free_ibuffer(struct indirect_buffer *ibuf)
171 {
172   int i;
173   for(i=0;ibuf->paddr[i];i++)
174     kfree((void *)__va(ibuf->paddr[i]));
175   kfree(ibuf);
176 }
177
178 /* convert vmalloc'ed memory to physical address */
179 static unsigned long va2pa(void *p)
180 {
181   return iopa((unsigned long)p);
182 }
183
184 static int uloader_notify_reboot (struct notifier_block *this, \
185                                   unsigned long event, void *x)
186 {
187   switch (event) {
188     case SYS_RESTART:
189       break;
190     default:
191       return NOTIFY_DONE;
192   }
193
194   printk(KERN_INFO "uloader module booting u-boot\n");
195
196   load_uboot(va2pa(load_uboot), va2pa(uboot_buf), laddr);
197
198   return NOTIFY_DONE;  /* This should never be reached */
199 }
200
201 int init_module(void)
202 {
203   int err;
204
205   printk(KERN_INFO "uloader module loaded\n");
206   printk(KERN_INFO "uboot=%s\n", uboot);
207
208   if((err = read_file(uboot, &uboot_buf)))
209     goto out;
210   register_reboot_notifier(&uloader_notifier);
211   return 0;
212 out:
213   free_saved_pages();
214   return err;
215 }
216
217 void cleanup_module(void)
218 {
219   printk(KERN_INFO "uloader module unloaded\n");
220 }