강좌 & 팁
안녕하세요~ 호서대학교 석사(과정) 이우영 입니다.
점점 루즈해져 가는것 같습니다 ㅎㅎ.
오늘은 다시 잼있게 해봅시다!
/dev/mem
/dev/mem 은 메모리를 컨트롤 하는 디바이스라고 했습니다.
그럼 어떻게 제어하는지 알아 볼까요?
mem의 file_operations 는 다음과 같이 구성되어 있습니다.
802 static const struct file_operations mem_fops = {
803 .llseek = memory_lseek,
804 .read = read_mem,
805 .write = write_mem,
806 .mmap = mmap_mem,
807 .open = open_mem,
808 .get_unmapped_area = get_unmapped_area_mem,
809};
먼전 open_mem 부터 보면 다음과 같이 define 되어 실제로는 open_port 함수를 봐야 합니다.
open_port 함수는 다음과 같습니다.
789static int open_port(struct inode * inode, struct file * filp)
790{
791 return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
792}
capable 이라는 함수를 사용하는데요 이건 실제로 연산을 수행 할 수 있는지 검사하는 루틴입니다.
이 함수의 리터 값을 받아 0 이나 에러 코드를 리턴 해 줍니다.
다음은 read_mem 입니다. 주석을 보면 물리적 메모리에서 읽어 들인다고 나와 있습니다.
113/*
114 * This funcion reads the *physical* memory. The f_pos points directly to the
115 * memory location.
116 */
117static ssize_t read_mem(struct file * file, char __user * buf,
118 size_t count, loff_t *ppos)
119{
120 unsigned long p = *ppos;
121 ssize_t read, sz;
122 char *ptr;
123
124 if (!valid_phys_addr_range(p, count))
125 return -EFAULT;
126 read = 0;
127#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
128 /* we don't have page 0 mapped on sparc and m68k.. */
129 if (p < PAGE_SIZE) {
130 sz = PAGE_SIZE - p;
131 if (sz > count)
132 sz = count;
133 if (sz > 0) {
134 if (clear_user(buf, sz))
135 return -EFAULT;
136 buf += sz;
137 p += sz;
138 count -= sz;
139 read += sz;
140 }
141 }
142#endif
143
144 while (count > 0) {
145 /*
146 * Handle first page in case it's not aligned
147 */
148 if (-p & (PAGE_SIZE - 1))
149 sz = -p & (PAGE_SIZE - 1);
150 else
151 sz = PAGE_SIZE;
152
153 sz = min_t(unsigned long, sz, count);
154
155 if (!range_is_allowed(p >> PAGE_SHIFT, count))
156 return -EPERM;
157
158 /*
159 * On ia64 if a page has been mapped somewhere as
160 * uncached, then it must also be accessed uncached
161 * by the kernel or data corruption may occur
162 */
163 ptr = xlate_dev_mem_ptr(p);
164 if (!ptr)
165 return -EFAULT;
166
167 if (copy_to_user(buf, ptr, sz)) {
168 unxlate_dev_mem_ptr(p, ptr);
169 return -EFAULT;
170 }
171
172 unxlate_dev_mem_ptr(p, ptr);
173
174 buf += sz;
175 p += sz;
176 count -= sz;
177 read += sz;
178 }
179
180 *ppos += read;
181 return read;
182}
일단 valid_phys_addr_range 를 통해 읽고 쓰기에 문제가 없는지 확인 합니다.
(127~142 라인은 m68k 아키텍터에 따른 소스이기 때문에 보실 필요는 없습니다.)
기본적으로 while 문을 돌면서 count 값이 0이 될때 까지 읽기 작업을 합니다.
163라인에서 xlate_dev_mem_ptr을 통해 가상주소를 물리주소로 변환해 옵니다.
167라인에서 buf로 PAGE_SIZE 만큼 읽어 오고
다음에 읽은 data를 쓸수 있도록 buf의 주소공간을 읽어 들인 만큼 더해줍니다.
p(파일의 읽는 위치) 도 증가 시키고 read(읽어들인 총 크기) 도 증가 시킵니다.
count (읽어야 하는 남은 크기)값은 반대로 감소 시켜줍니다.
count 값이 0이 되면 ppos의 값을 읽어 들인 크기 만큼 증가시키고
read를 리턴해 얼마만큼 읽어 들였는지 알려 줍니다.
(원리는 생각 보다 간단하죠? 하지만 구현하라고 하면 못합니다 ㅎㅎ)
weite_mem도 형식은 read_mem과 같습니다.
그저 copy_to_user 에서 copy_from_user로 변했습니다.
(써야 하니까요 ㅎㅎ)
소스코드는 다음과 같습니다.
184static ssize_t write_mem(struct file * file, const char __user * buf,
185 size_t count, loff_t *ppos)
186{
187 unsigned long p = *ppos;
188 ssize_t written, sz;
189 unsigned long copied;
190 void *ptr;
191
192 if (!valid_phys_addr_range(p, count))
193 return -EFAULT;
194
195 written = 0;
196
197#ifdef __ARCH_HAS_NO_PAGE_ZERO_MAPPED
198 /* we don't have page 0 mapped on sparc and m68k.. */
199 if (p < PAGE_SIZE) {
200 unsigned long sz = PAGE_SIZE - p;
201 if (sz > count)
202 sz = count;
203 /* Hmm. Do something? */
204 buf += sz;
205 p += sz;
206 count -= sz;
207 written += sz;
208 }
209#endif
210
211 while (count > 0) {
212 /*
213 * Handle first page in case it's not aligned
214 */
215 if (-p & (PAGE_SIZE - 1))
216 sz = -p & (PAGE_SIZE - 1);
217 else
218 sz = PAGE_SIZE;
219
220 sz = min_t(unsigned long, sz, count);
221
222 if (!range_is_allowed(p >> PAGE_SHIFT, sz))
223 return -EPERM;
224
225 /*
226 * On ia64 if a page has been mapped somewhere as
227 * uncached, then it must also be accessed uncached
228 * by the kernel or data corruption may occur
229 */
230 ptr = xlate_dev_mem_ptr(p);
231 if (!ptr) {
232 if (written)
233 break;
234 return -EFAULT;
235 }
236
237 copied = copy_from_user(ptr, buf, sz);
238 if (copied) {
239 written += sz - copied;
240 unxlate_dev_mem_ptr(p, ptr);
241 if (written)
242 break;
243 return -EFAULT;
244 }
245
246 unxlate_dev_mem_ptr(p, ptr);
247
248 buf += sz;
249 p += sz;
250 count -= sz;
251 written += sz;
252 }
253
254 *ppos += written;
255 return written;
256}
오늘은 /dev/mem의 읽기와 쓰기 그리고 open에 대해서 알아 보았습니다.
다음은 남은 부분을 해보도록 하겠습니다.
(실습도 만들어 봐야 하는대..; 음 고민입니다 ㅎㅎ)
그럼 다음시간에 만나겠습니다.
http://ms-osek.org/ <- 쫌더 빨리 보고 싶으신분은 여기로 오세요~