ecd5b8cda9f01202f91c09751c579f24b12b5220
[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 // 0x01f00000
45 // 0x07f00000
46 static unsigned long laddr = 0x07f00000;
47
48 #define LOW_MEM 0x00800000  /* no smaller than kernel + 1M (bss) + initrd */
49
50 void load_uboot(unsigned long pa_load_uboot,
51                 unsigned long pa_uboot_buf,
52                 unsigned long laddr);
53
54 struct indirect_buffer {
55   int size;
56   unsigned long paddr[1]; /* physical address of each 4K page */
57                           /* terminate with zero */
58 };
59 static struct indirect_buffer *uboot_buf;
60
61 static int uloader_notify_reboot (struct notifier_block *this, \
62                                   unsigned long event, void *x);
63 static struct notifier_block uloader_notifier = {
64   uloader_notify_reboot,
65   NULL,
66   0
67 };
68
69 #define MAX_INDIRECT_BUFFER_SIZE ((PAGE_SIZE/4-1)*PAGE_SIZE)
70
71 #define VMALLOC_SIZE ((LOW_MEM+PAGE_SIZE-1)/PAGE_SIZE)*4
72
73 /*
74  *  Allocate a page with physical address >= LOWMEM
75  */
76 static void **save;
77 static int saved_pages = 0;
78 static void *alloc_high_page(void)
79 {
80   void *ptr;
81   if(!save)
82   {
83     save = vmalloc( VMALLOC_SIZE );
84     if(!save) {
85         printk(KERN_INFO "can't vmalloc(%x)\n", VMALLOC_SIZE );
86         return 0;
87     } else {
88         printk(KERN_INFO "vmalloc(%x) = %08x\n", VMALLOC_SIZE, save );
89     }
90   }
91
92   while(1)
93   {
94     ptr = kmalloc(PAGE_SIZE, GFP_KERNEL);
95     if(!ptr) {
96         printk(KERN_INFO "can't kmalloc(%0x)\n", PAGE_SIZE);
97         return 0;
98     }
99     if( __pa(ptr) >= LOW_MEM ) {
100         printk(KERN_INFO "kmalloc(%x) = %08x __pa: %08x >= %08x skipped\n", PAGE_SIZE, ptr, __pa(ptr), LOW_MEM );
101         break;
102     }
103     printk(KERN_INFO "%d: kmalloc(%x) = %08x __pa: %08x\n", saved_pages, PAGE_SIZE, ptr, __pa(ptr) );
104     save[saved_pages++] = ptr;
105   }
106   printk(KERN_INFO "alloc_high_page %08x __pa: %08x\n", ptr, __pa(ptr) );
107   return ptr;
108 }
109
110 static void free_saved_pages(void)
111 {
112   if(save)
113   {
114     int i;
115     for(i=0;i<saved_pages;i++)
116       kfree(save[i]);
117     vfree(save);
118   }
119 }
120
121 static void free_ibuffer(struct indirect_buffer *ibuf);
122
123 /*
124  *  Read input file into an indirect buffer
125  */
126 static int read_file(char *filename, struct indirect_buffer **indirect_buf)
127 {
128   struct file* file;
129   struct inode* inode;
130   struct indirect_buffer *ibuf;
131   size_t size, got, i;
132   mm_segment_t fs;
133   int err;
134
135   file = filp_open(filename, O_RDONLY, 0);
136   if(IS_ERR(file)) {
137     printk(KERN_INFO "can't open filename %s\n", filename);
138     return PTR_ERR(file);
139   }
140   printk(KERN_INFO "opened file %s at position %x", filename, file->pos);
141   file->pos = 0;
142
143   err = -EIO;
144   if(!file->f_op || !file->f_op->read) {
145     printk(KERN_INFO "EIO\n");
146     goto out;
147   }
148
149   err = -EACCES;
150   inode = file->f_dentry->d_inode;
151   if(!S_ISREG(inode->i_mode)) {
152     printk(KERN_INFO "EACCES\n");
153     goto out;
154   }
155
156   err = -ENOMEM;
157   ibuf = (struct indirect_buffer*)alloc_high_page();
158   if(!ibuf) {
159     printk(KERN_INFO "ENOMEM\n");
160     goto out;
161   }
162   memset(ibuf, 0, PAGE_SIZE);
163
164   if(inode->i_size > MAX_INDIRECT_BUFFER_SIZE) {
165         printk(KERN_INFO "## %8x > %8x\n", inode->i_size, MAX_INDIRECT_BUFFER_SIZE);
166         goto out2;
167   }
168   size = (size_t)inode->i_size;
169   ibuf->size = size;
170   printk(KERN_INFO "loading %s %d bytes\n", filename, size);
171
172   for(i=0;i<size;i+=PAGE_SIZE)
173   {
174     size_t todo = min(size-i, (size_t)PAGE_SIZE);
175     void *buf;
176
177     err = -ENOMEM;
178     buf = alloc_high_page();
179     if(!buf) {
180       printk(KERN_INFO "can't allocate page %d\n", i);
181       goto out2;
182     }
183     ibuf->paddr[i/PAGE_SIZE] = __pa(buf);
184
185     err = -EIO;
186     file->f_pos = i;
187     fs = get_fs();
188     set_fs(KERNEL_DS);
189     printk(KERN_DEBUG "KERNEL_DS\n");
190     got = file->f_op->read(file, buf, todo, &file->f_pos);
191     set_fs(fs);
192     printk(KERN_DEBUG "fs\n");
193     if(got != todo) {
194       printk(KERN_INFO "for block %d got %d expected %8x\n", i, got, todo);
195       goto out2;
196     }
197   }
198
199   *indirect_buf = ibuf;
200   err = 0;
201
202   printk(KERN_INFO "loaded %s\n", filename);
203 out:
204   filp_close(file, NULL);
205   printk(KERN_INFO "return code %d\n", err);
206   return err;
207 out2:
208   printk(KERN_INFO "failed loading %s\n", filename);
209   free_ibuffer(ibuf);
210   goto out;
211 }
212
213 static void free_ibuffer(struct indirect_buffer *ibuf)
214 {
215   int i;
216   for(i=0;ibuf->paddr[i];i++)
217     kfree((void *)__va(ibuf->paddr[i]));
218   kfree(ibuf);
219 }
220
221 /* convert vmalloc'ed memory to physical address */
222 static unsigned long va2pa(void *p)
223 {
224   return iopa((unsigned long)p);
225 }
226
227 static int uloader_notify_reboot (struct notifier_block *this, \
228                                   unsigned long event, void *x)
229 {
230   switch (event) {
231     case SYS_RESTART:
232       break;
233     default:
234       return NOTIFY_DONE;
235   }
236
237   printk(KERN_INFO "uloader module booting u-boot\n");
238
239   load_uboot(va2pa(load_uboot), va2pa(uboot_buf), laddr);
240
241   return NOTIFY_DONE;  /* This should never be reached */
242 }
243
244 int init_module(void)
245 {
246   int err;
247
248   printk(KERN_INFO "uloader module loaded\n");
249   printk(KERN_INFO "uboot=%s<--\n", uboot);
250
251   if((err = read_file(uboot, &uboot_buf))) {
252     printk(KERN_INFO "Error loading %s\n", uboot);
253     goto out;
254   }
255   register_reboot_notifier(&uloader_notifier);
256   return 0;
257 out:
258   free_saved_pages();
259   return err;
260 }
261
262 void cleanup_module(void)
263 {
264   printk(KERN_INFO "uloader module unloaded\n");
265 }