blob: afa62a0374eb4fc56da713f3705c46cb6621e2f3 [file] [log] [blame]
Nico Huberf03ef4f2017-03-04 13:57:09 +01001/*
2 * Copyright (C) 2017 Nico Huber <nico.h@gmx.de>
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14
15#include <stdint.h>
16#include <sys/mman.h>
Nico Huber967dd0d2017-07-09 15:51:08 +020017#include <sys/types.h>
18#include <sys/stat.h>
Nico Huberf03ef4f2017-03-04 13:57:09 +010019#include <unistd.h>
20#include <fcntl.h>
21#include <errno.h>
22
23#define HW_FILE_READ 0x01
24#define HW_FILE_WRITE 0x02
25
Nico Huber967dd0d2017-07-09 15:51:08 +020026static size_t stat_length(size_t *const len, const size_t off, const int fd)
27{
28 struct stat statbuf;
29
30 if (*len == 0 && !fstat(fd, &statbuf)) {
31 if (statbuf.st_size == 0)
32 errno = EINVAL;
33 else if (statbuf.st_size - (off_t)off <= SIZE_MAX)
34 *len = (size_t)(statbuf.st_size - (off_t)off);
35 else
36 *len = SIZE_MAX;
37 }
38
39 return *len;
40}
41
Nico Hubera43b1ee2017-07-09 15:40:25 +020042static int map_file(uint64_t *const addr,
43 const char *const path,
Nico Huber967dd0d2017-07-09 15:51:08 +020044 size_t len,
45 const size_t off,
Nico Huberf03ef4f2017-03-04 13:57:09 +010046 const uint32_t mode)
47{
48 const int prot = (mode & HW_FILE_READ ? PROT_READ : 0) |
49 (mode & HW_FILE_WRITE ? PROT_WRITE : 0);
50
51 if (mode & ~(HW_FILE_READ | HW_FILE_WRITE) ||
52 !(mode & (HW_FILE_READ | HW_FILE_WRITE)))
53 return EINVAL;
54
55 const int fd = open(path, mode & HW_FILE_WRITE ? O_RDWR : O_RDONLY);
56 if (fd < 0)
57 return errno;
58
Nico Huber967dd0d2017-07-09 15:51:08 +020059 if (!stat_length(&len, off, fd)) {
60 close(fd);
61 return errno;
62 }
63
64 void *const mapped = mmap(NULL, len, prot, MAP_SHARED, fd, (off_t)off);
Nico Huberf03ef4f2017-03-04 13:57:09 +010065 close(fd);
66 if (mapped == MAP_FAILED)
67 return errno;
68
Nico Hubera43b1ee2017-07-09 15:40:25 +020069 *addr = (uint64_t)(uintptr_t)mapped;
Nico Huberf03ef4f2017-03-04 13:57:09 +010070 return 0;
71}
72
Nico Hubera43b1ee2017-07-09 15:40:25 +020073static int map_fill_from_file(uint64_t *const addr,
74 const char *const path,
Nico Huber967dd0d2017-07-09 15:51:08 +020075 size_t len,
76 const size_t off,
Nico Huberf03ef4f2017-03-04 13:57:09 +010077 const uint32_t mode)
78{
Nico Huber967dd0d2017-07-09 15:51:08 +020079 size_t xferred;
Nico Huberf03ef4f2017-03-04 13:57:09 +010080 ssize_t read_ret;
81
82 if (mode != HW_FILE_READ)
83 return EINVAL;
84
Nico Huberf03ef4f2017-03-04 13:57:09 +010085 const int fd = open(path, O_RDONLY);
86 if (fd < 0)
Nico Huber967dd0d2017-07-09 15:51:08 +020087 return errno;
88
89 if (!stat_length(&len, off, fd))
90 goto _close;
91
92 void *const mapped = mmap(NULL, len, PROT_READ | PROT_WRITE,
93 MAP_ANONYMOUS | MAP_PRIVATE, -1, (off_t)off);
94 if (mapped == MAP_FAILED)
95 goto _close;
Nico Huberf03ef4f2017-03-04 13:57:09 +010096
97 xferred = 0;
98 do {
99 read_ret = read(fd, (uint8_t *)mapped + xferred, len - xferred);
100 if (read_ret > 0)
101 xferred += read_ret;
102 } while ((read_ret > 0 || (read_ret == -1 && errno == EINTR))
103 && xferred < len);
Nico Huberf03ef4f2017-03-04 13:57:09 +0100104
105 if (xferred < len)
106 goto _munmap;
107
108 if (mprotect(mapped, len, PROT_READ))
109 goto _munmap;
110
Nico Huber967dd0d2017-07-09 15:51:08 +0200111 close(fd);
112
Nico Hubera43b1ee2017-07-09 15:40:25 +0200113 *addr = (uint64_t)(uintptr_t)mapped;
Nico Huberf03ef4f2017-03-04 13:57:09 +0100114 return 0;
115
116_munmap:
117 munmap(mapped, len);
Nico Huber967dd0d2017-07-09 15:51:08 +0200118_close:
119 close(fd);
Nico Huberf03ef4f2017-03-04 13:57:09 +0100120 return errno;
121}
122
Nico Hubera43b1ee2017-07-09 15:40:25 +0200123int hw_file_map(uint64_t *const addr,
124 const char *const path,
Nico Huberf03ef4f2017-03-04 13:57:09 +0100125 const uint32_t len,
Nico Huber967dd0d2017-07-09 15:51:08 +0200126 const uint32_t off,
Nico Huberf03ef4f2017-03-04 13:57:09 +0100127 const uint32_t mode,
128 const int copy)
129{
130 if (copy)
Nico Huber967dd0d2017-07-09 15:51:08 +0200131 return map_fill_from_file(
132 addr, path, (size_t)len, (size_t)off, mode);
Nico Huberf03ef4f2017-03-04 13:57:09 +0100133 else
Nico Huber967dd0d2017-07-09 15:51:08 +0200134 return map_file(addr, path, (size_t)len, (size_t)off, mode);
Nico Huberf03ef4f2017-03-04 13:57:09 +0100135}