2.4 kernel support, manual module loading for 2.6
[warm.git] / warm.c
1 /*
2  * wARM - exporting ARM processor specific privileged services to userspace
3  * userspace part
4  *
5  * Copyright (c) GraÅžvydas "notaz" Ignotas, 2009
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *     * Redistributions of source code must retain the above copyright
10  *       notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above copyright
12  *       notice, this list of conditions and the following disclaimer in the
13  *       documentation and/or other materials provided with the distribution.
14  *     * Neither the name of the organization nor the
15  *       names of its contributors may be used to endorse or promote products
16  *       derived from this software without specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
22  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
25  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
26  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <unistd.h>
36 #include <fcntl.h>
37 #include <sys/ioctl.h>
38 #include <sys/utsname.h>
39 #include <sys/syscall.h>
40 #include <errno.h>
41
42 #define WARM_CODE
43 #include "warm.h"
44
45 extern long init_module(void *, unsigned long, const char *);   /* provided by glibc */
46
47 static int warm_fd = -1;
48 static int kernel_version;
49
50 static void sys_cacheflush(void *start, void *end)
51 {
52 #ifdef __ARM_EABI__
53         /* EABI version */
54         int num = __ARM_NR_cacheflush;
55         __asm__("mov  r0, %0 ;"
56                 "mov  r1, %1 ;"
57                 "mov  r2, #0 ;"
58                 "mov  r7, %2 ;"
59                 "swi  0" : : "r" (start), "r" (end), "r" (num)
60                         : "r0", "r1", "r2", "r3", "r7");
61 #else
62         /* OABI */
63         __asm__("mov  r0, %0 ;"
64                 "mov  r1, %1 ;"
65                 "mov  r2, #0 ;"
66                 "swi  %2" : : "r" (start), "r" (end), "i" __ARM_NR_cacheflush
67                         : "r0", "r1", "r2", "r3");
68 #endif
69 }
70
71 static int manual_insmod_26(const char *fname, const char *opts)
72 {
73         unsigned long len, read_len;
74         int ret = -1;
75         void *buff;
76         FILE *f;
77
78         f = fopen(fname, "rb");
79         if (f == NULL)
80                 return -1;
81
82         fseek(f, 0, SEEK_END);
83         len = ftell(f);
84         fseek(f, 0, SEEK_SET);
85
86         buff = malloc(len);
87         if (buff == NULL)
88                 goto fail0;
89
90         read_len = fread(buff, 1, len, f);
91         if (read_len != len) {
92                 fprintf(stderr, "failed to read module\n");
93                 goto fail1;
94         }
95
96         ret = init_module(buff, len, opts);
97
98 fail1:
99         free(buff);
100 fail0:
101         fclose(f);
102         return ret;
103 }
104
105 int warm_init(void)
106 {
107         struct utsname unm;
108         char buff1[32], buff2[128];
109         int ret;
110
111         warm_fd = open("/proc/warm", O_RDWR);
112         if (warm_fd >= 0)
113                 return 0;
114
115         memset(&unm, 0, sizeof(unm));
116         uname(&unm);
117
118         if (strlen(unm.release) < 3 || unm.release[1] != '.') {
119                 fprintf(stderr, "unexpected version string: %s\n", unm.release);
120                 goto fail;
121         }
122         kernel_version = ((unm.release[0] - '0') << 4) | (unm.release[2] - '0');
123
124         snprintf(buff1, sizeof(buff1), "warm_%s.%s", unm.release, kernel_version >= 0x26 ? "ko" : "o");
125         snprintf(buff2, sizeof(buff2), "/sbin/insmod %s verbose=1", buff1);
126
127         /* try to insmod */
128         ret = system(buff2);
129         if (ret != 0) {
130                 fprintf(stderr, "system/insmod failed: %d %d\n", ret, errno);
131                 if (kernel_version >= 0x26) {
132                         ret = manual_insmod_26(buff1, "verbose=1");
133                         if (ret != 0)
134                                 fprintf(stderr, "manual insmod also failed: %d\n", ret);
135                 }
136         }
137
138         warm_fd = open("/proc/warm", O_RDWR);
139         if (warm_fd >= 0)
140                 return 0;
141
142 fail:
143         fprintf(stderr, "wARM: can't init, acting as sys_cacheflush wrapper\n");
144         return -1;
145 }
146
147 void warm_finish(void)
148 {
149         char cmd[64];
150
151         if (warm_fd < 0)
152                 return;
153
154         close(warm_fd);
155         warm_fd = -1;
156
157         if (kernel_version < 0x26) {
158                 struct utsname unm;
159                 memset(&unm, 0, sizeof(unm));
160                 uname(&unm);
161                 snprintf(cmd, sizeof(cmd), "/sbin/rmmod warm_%s", unm.release);
162         }
163         else
164                 strcpy(cmd, "/sbin/rmmod warm");
165
166         system(cmd);
167 }
168
169 int warm_cache_op_range(int op, void *addr, unsigned long size)
170 {
171         struct warm_cache_op wop;
172         int ret;
173
174         if (warm_fd < 0) {
175                 /* note that this won't work for warm_cache_op_all */
176                 sys_cacheflush(addr, (char *)addr + size);
177                 return -1;
178         }
179
180         wop.ops = op;
181         wop.addr = (unsigned long)addr;
182         wop.size = size;
183
184         ret = ioctl(warm_fd, WARMC_CACHE_OP, &wop);
185         if (ret != 0) {
186                 perror("WARMC_CACHE_OP failed");
187                 return -1;
188         }
189
190         return 0;
191 }
192
193 int warm_cache_op_all(int op)
194 {
195         return warm_cache_op_range(op, NULL, (unsigned long)-1);
196 }
197
198 int warm_change_cb_range(int cb, int is_set, void *addr, unsigned long size)
199 {
200         struct warm_change_cb ccb;
201         int ret;
202
203         if (warm_fd < 0)
204                 return -1;
205         
206         ccb.addr = (unsigned long)addr;
207         ccb.size = size;
208         ccb.cb = cb;
209         ccb.is_set = is_set;
210
211         ret = ioctl(warm_fd, WARMC_CHANGE_CB, &ccb);
212         if (ret != 0) {
213                 perror("WARMC_CHANGE_CB failed");
214                 return -1;
215         }
216
217         return 0;
218 }
219
220 int warm_change_cb_upper(int cb, int is_set)
221 {
222         return warm_change_cb_range(cb, is_set, 0, 0);
223 }
224
225 unsigned long warm_virt2phys(const void *ptr)
226 {
227         unsigned long ptrio;
228         int ret;
229
230         ptrio = (unsigned long)ptr;
231         ret = ioctl(warm_fd, WARMC_VIRT2PHYS, &ptrio);
232         if (ret != 0) {
233                 perror("WARMC_VIRT2PHYS failed");
234                 return (unsigned long)-1;
235         }
236
237         return ptrio;
238 }
239