Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 1 | /* |
| 2 | * arch/um/kernel/mem_user.c |
| 3 | * |
| 4 | * BRIEF MODULE DESCRIPTION |
| 5 | * user side memory routines for supporting IO memory inside user mode linux |
| 6 | * |
| 7 | * Copyright (C) 2001 RidgeRun, Inc. |
| 8 | * Author: RidgeRun, Inc. |
| 9 | * Greg Lonnon glonnon@ridgerun.com or info@ridgerun.com |
| 10 | * |
| 11 | * This program is free software; you can redistribute it and/or modify it |
| 12 | * under the terms of the GNU General Public License as published by the |
| 13 | * Free Software Foundation; either version 2 of the License, or (at your |
| 14 | * option) any later version. |
| 15 | * |
| 16 | * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED |
| 17 | * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| 18 | * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN |
| 19 | * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
| 20 | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| 21 | * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF |
| 22 | * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON |
| 23 | * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| 25 | * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 26 | * |
| 27 | * You should have received a copy of the GNU General Public License along |
| 28 | * with this program; if not, write to the Free Software Foundation, Inc., |
| 29 | * 675 Mass Ave, Cambridge, MA 02139, USA. |
| 30 | */ |
| 31 | |
| 32 | #include <stdio.h> |
| 33 | #include <stdlib.h> |
| 34 | #include <stddef.h> |
| 35 | #include <stdarg.h> |
| 36 | #include <unistd.h> |
| 37 | #include <errno.h> |
| 38 | #include <string.h> |
| 39 | #include <fcntl.h> |
| 40 | #include <sys/types.h> |
| 41 | #include <sys/mman.h> |
| 42 | #include "kern_util.h" |
| 43 | #include "user.h" |
| 44 | #include "user_util.h" |
| 45 | #include "mem_user.h" |
| 46 | #include "init.h" |
| 47 | #include "os.h" |
| 48 | #include "tempfile.h" |
| 49 | #include "kern_constants.h" |
| 50 | |
| 51 | #define TEMPNAME_TEMPLATE "vm_file-XXXXXX" |
| 52 | |
| 53 | static int create_tmp_file(unsigned long len) |
| 54 | { |
| 55 | int fd, err; |
| 56 | char zero; |
| 57 | |
| 58 | fd = make_tempfile(TEMPNAME_TEMPLATE, NULL, 1); |
| 59 | if(fd < 0) { |
| 60 | os_print_error(fd, "make_tempfile"); |
| 61 | exit(1); |
| 62 | } |
| 63 | |
| 64 | err = os_mode_fd(fd, 0777); |
| 65 | if(err < 0){ |
| 66 | os_print_error(err, "os_mode_fd"); |
| 67 | exit(1); |
| 68 | } |
| 69 | err = os_seek_file(fd, len); |
| 70 | if(err < 0){ |
| 71 | os_print_error(err, "os_seek_file"); |
| 72 | exit(1); |
| 73 | } |
| 74 | zero = 0; |
| 75 | err = os_write_file(fd, &zero, 1); |
| 76 | if(err != 1){ |
| 77 | os_print_error(err, "os_write_file"); |
| 78 | exit(1); |
| 79 | } |
| 80 | |
| 81 | return(fd); |
| 82 | } |
| 83 | |
| 84 | void check_tmpexec(void) |
| 85 | { |
| 86 | void *addr; |
| 87 | int err, fd = create_tmp_file(UM_KERN_PAGE_SIZE); |
| 88 | |
| 89 | addr = mmap(NULL, UM_KERN_PAGE_SIZE, |
| 90 | PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE, fd, 0); |
| 91 | printf("Checking PROT_EXEC mmap in /tmp..."); |
| 92 | fflush(stdout); |
| 93 | if(addr == MAP_FAILED){ |
| 94 | err = errno; |
| 95 | perror("failed"); |
| 96 | if(err == EPERM) |
| 97 | printf("/tmp must be not mounted noexec\n"); |
| 98 | exit(1); |
| 99 | } |
| 100 | printf("OK\n"); |
| 101 | munmap(addr, UM_KERN_PAGE_SIZE); |
| 102 | |
| 103 | os_close_file(fd); |
| 104 | } |
| 105 | |
| 106 | static int have_devanon = 0; |
| 107 | |
| 108 | void check_devanon(void) |
| 109 | { |
| 110 | int fd; |
| 111 | |
| 112 | printk("Checking for /dev/anon on the host..."); |
| 113 | fd = open("/dev/anon", O_RDWR); |
| 114 | if(fd < 0){ |
| 115 | printk("Not available (open failed with errno %d)\n", errno); |
| 116 | return; |
| 117 | } |
| 118 | |
| 119 | printk("OK\n"); |
| 120 | have_devanon = 1; |
| 121 | } |
| 122 | |
| 123 | static int create_anon_file(unsigned long len) |
| 124 | { |
| 125 | void *addr; |
| 126 | int fd; |
| 127 | |
| 128 | fd = open("/dev/anon", O_RDWR); |
| 129 | if(fd < 0) { |
| 130 | os_print_error(fd, "opening /dev/anon"); |
| 131 | exit(1); |
| 132 | } |
| 133 | |
| 134 | addr = mmap(NULL, len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); |
| 135 | if(addr == MAP_FAILED){ |
| 136 | perror("mapping physmem file"); |
| 137 | exit(1); |
| 138 | } |
| 139 | munmap(addr, len); |
| 140 | |
| 141 | return(fd); |
| 142 | } |
| 143 | |
| 144 | int create_mem_file(unsigned long len) |
| 145 | { |
| 146 | int err, fd; |
| 147 | |
| 148 | if(have_devanon) |
| 149 | fd = create_anon_file(len); |
| 150 | else fd = create_tmp_file(len); |
| 151 | |
| 152 | err = os_set_exec_close(fd, 1); |
| 153 | if(err < 0) |
| 154 | os_print_error(err, "exec_close"); |
| 155 | return(fd); |
| 156 | } |
| 157 | |
| 158 | struct iomem_region *iomem_regions = NULL; |
| 159 | int iomem_size = 0; |
| 160 | |
| 161 | static int __init parse_iomem(char *str, int *add) |
| 162 | { |
| 163 | struct iomem_region *new; |
| 164 | struct uml_stat buf; |
| 165 | char *file, *driver; |
| 166 | int fd, err, size; |
| 167 | |
| 168 | driver = str; |
| 169 | file = strchr(str,','); |
| 170 | if(file == NULL){ |
| 171 | printf("parse_iomem : failed to parse iomem\n"); |
| 172 | goto out; |
| 173 | } |
| 174 | *file = '\0'; |
| 175 | file++; |
| 176 | fd = os_open_file(file, of_rdwr(OPENFLAGS()), 0); |
| 177 | if(fd < 0){ |
| 178 | os_print_error(fd, "parse_iomem - Couldn't open io file"); |
| 179 | goto out; |
| 180 | } |
| 181 | |
| 182 | err = os_stat_fd(fd, &buf); |
| 183 | if(err < 0){ |
| 184 | os_print_error(err, "parse_iomem - cannot stat_fd file"); |
| 185 | goto out_close; |
| 186 | } |
| 187 | |
| 188 | new = malloc(sizeof(*new)); |
| 189 | if(new == NULL){ |
| 190 | perror("Couldn't allocate iomem_region struct"); |
| 191 | goto out_close; |
| 192 | } |
| 193 | |
| 194 | size = (buf.ust_size + UM_KERN_PAGE_SIZE) & ~(UM_KERN_PAGE_SIZE - 1); |
| 195 | |
| 196 | *new = ((struct iomem_region) { .next = iomem_regions, |
| 197 | .driver = driver, |
| 198 | .fd = fd, |
| 199 | .size = size, |
| 200 | .phys = 0, |
| 201 | .virt = 0 }); |
| 202 | iomem_regions = new; |
| 203 | iomem_size += new->size + UM_KERN_PAGE_SIZE; |
| 204 | |
| 205 | return(0); |
| 206 | out_close: |
| 207 | os_close_file(fd); |
| 208 | out: |
| 209 | return(1); |
| 210 | } |
| 211 | |
| 212 | __uml_setup("iomem=", parse_iomem, |
| 213 | "iomem=<name>,<file>\n" |
| 214 | " Configure <file> as an IO memory region named <name>.\n\n" |
| 215 | ); |
| 216 | |
| 217 | int protect_memory(unsigned long addr, unsigned long len, int r, int w, int x, |
| 218 | int must_succeed) |
| 219 | { |
| 220 | int err; |
| 221 | |
| 222 | err = os_protect_memory((void *) addr, len, r, w, x); |
| 223 | if(err < 0){ |
| 224 | if(must_succeed) |
| 225 | panic("protect failed, err = %d", -err); |
| 226 | else return(err); |
| 227 | } |
| 228 | return(0); |
| 229 | } |
| 230 | |
| 231 | #if 0 |
| 232 | /* Debugging facility for dumping stuff out to the host, avoiding the timing |
| 233 | * problems that come with printf and breakpoints. |
| 234 | * Enable in case of emergency. |
| 235 | */ |
| 236 | |
| 237 | int logging = 1; |
| 238 | int logging_fd = -1; |
| 239 | |
| 240 | int logging_line = 0; |
| 241 | char logging_buf[512]; |
| 242 | |
| 243 | void log(char *fmt, ...) |
| 244 | { |
| 245 | va_list ap; |
| 246 | struct timeval tv; |
| 247 | struct openflags flags; |
| 248 | |
| 249 | if(logging == 0) return; |
| 250 | if(logging_fd < 0){ |
| 251 | flags = of_create(of_trunc(of_rdwr(OPENFLAGS()))); |
| 252 | logging_fd = os_open_file("log", flags, 0644); |
| 253 | } |
| 254 | gettimeofday(&tv, NULL); |
| 255 | sprintf(logging_buf, "%d\t %u.%u ", logging_line++, tv.tv_sec, |
| 256 | tv.tv_usec); |
| 257 | va_start(ap, fmt); |
| 258 | vsprintf(&logging_buf[strlen(logging_buf)], fmt, ap); |
| 259 | va_end(ap); |
| 260 | write(logging_fd, logging_buf, strlen(logging_buf)); |
| 261 | } |
| 262 | #endif |
| 263 | |
| 264 | /* |
| 265 | * Overrides for Emacs so that we follow Linus's tabbing style. |
| 266 | * Emacs will notice this stuff at the end of the file and automatically |
| 267 | * adjust the settings for this buffer only. This must remain at the end |
| 268 | * of the file. |
| 269 | * --------------------------------------------------------------------------- |
| 270 | * Local variables: |
| 271 | * c-file-style: "linux" |
| 272 | * End: |
| 273 | */ |