3.1.7 branch.
[minix.git] / drivers / memory / memory.c
blob3e7a9469cc69773a6cda52f007e8fcc524750cd2
1 /* This file contains the device dependent part of the drivers for the
2 * following special files:
3 * /dev/ram - RAM disk
4 * /dev/mem - absolute memory
5 * /dev/kmem - kernel virtual memory
6 * /dev/null - null device (data sink)
7 * /dev/boot - boot device loaded from boot image
8 * /dev/zero - null byte stream generator
9 * /dev/imgrd - boot image RAM disk
11 * Changes:
12 * Apr 29, 2005 added null byte generator (Jorrit N. Herder)
13 * Apr 09, 2005 added support for boot device (Jorrit N. Herder)
14 * Jul 26, 2004 moved RAM driver to user-space (Jorrit N. Herder)
15 * Apr 20, 1992 device dependent/independent split (Kees J. Bot)
18 #include <minix/drivers.h>
19 #include <minix/driver.h>
20 #include <sys/ioc_memory.h>
21 #include <minix/ds.h>
22 #include <minix/vm.h>
23 #include <sys/mman.h>
24 #include "kernel/const.h"
25 #include "kernel/config.h"
26 #include "kernel/type.h"
28 #include <machine/vm.h>
30 #include "local.h"
32 /* ramdisks (/dev/ram*) */
33 #define RAMDISKS 6
35 #define RAM_DEV_LAST (RAM_DEV_FIRST+RAMDISKS-1)
37 #define NR_DEVS (7+RAMDISKS) /* number of minor devices */
39 PRIVATE struct device m_geom[NR_DEVS]; /* base and size of each device */
40 PRIVATE vir_bytes m_vaddrs[NR_DEVS];
41 PRIVATE int m_device; /* current device */
42 PRIVATE struct kinfo kinfo; /* kernel information */
44 extern int errno; /* error number for PM calls */
46 PRIVATE int openct[NR_DEVS];
48 FORWARD _PROTOTYPE( char *m_name, (void) );
49 FORWARD _PROTOTYPE( struct device *m_prepare, (int device) );
50 FORWARD _PROTOTYPE( int m_transfer, (int proc_nr, int opcode,
51 u64_t position, iovec_t *iov, unsigned nr_req) );
52 FORWARD _PROTOTYPE( int m_do_open, (struct driver *dp, message *m_ptr) );
53 FORWARD _PROTOTYPE( int m_do_close, (struct driver *dp, message *m_ptr) );
54 FORWARD _PROTOTYPE( int m_ioctl, (struct driver *dp, message *m_ptr) );
55 FORWARD _PROTOTYPE( void m_geometry, (struct partition *entry) );
57 /* Entry points to this driver. */
58 PRIVATE struct driver m_dtab = {
59 m_name, /* current device's name */
60 m_do_open, /* open or mount */
61 m_do_close, /* nothing on a close */
62 m_ioctl, /* specify ram disk geometry */
63 m_prepare, /* prepare for I/O on a given minor device */
64 m_transfer, /* do the I/O */
65 nop_cleanup, /* no need to clean up */
66 m_geometry, /* memory device "geometry" */
67 nop_alarm,
68 nop_cancel,
69 nop_select,
70 NULL,
71 NULL
74 /* Buffer for the /dev/zero null byte feed. */
75 #define ZERO_BUF_SIZE 1024
76 PRIVATE char dev_zero[ZERO_BUF_SIZE];
78 #define click_to_round_k(n) \
79 ((unsigned) ((((unsigned long) (n) << CLICK_SHIFT) + 512) / 1024))
81 /* SEF functions and variables. */
82 FORWARD _PROTOTYPE( void sef_local_startup, (void) );
83 FORWARD _PROTOTYPE( int sef_cb_init_fresh, (int type, sef_init_info_t *info) );
85 /*===========================================================================*
86 * main *
87 *===========================================================================*/
88 PUBLIC int main(void)
90 /* SEF local startup. */
91 sef_local_startup();
93 /* Call the generic receive loop. */
94 driver_task(&m_dtab, DRIVER_STD);
96 return(OK);
99 /*===========================================================================*
100 * sef_local_startup *
101 *===========================================================================*/
102 PRIVATE void sef_local_startup()
104 /* Register init callbacks. */
105 sef_setcb_init_fresh(sef_cb_init_fresh);
106 sef_setcb_init_lu(sef_cb_init_fresh);
107 sef_setcb_init_restart(sef_cb_init_fresh);
109 /* Register live update callbacks. */
110 sef_setcb_lu_prepare(sef_cb_lu_prepare_always_ready);
111 sef_setcb_lu_state_isvalid(sef_cb_lu_state_isvalid_standard);
113 /* Let SEF perform startup. */
114 sef_startup();
117 /*===========================================================================*
118 * sef_cb_init_fresh *
119 *===========================================================================*/
120 PRIVATE int sef_cb_init_fresh(int type, sef_init_info_t *info)
122 /* Initialize the memory driver. */
123 u32_t ramdev_size;
124 int i, s;
126 /* Initialize all minor devices one by one. */
127 if (OK != (s=sys_getkinfo(&kinfo))) {
128 panic("Couldn't get kernel information: %d", s);
131 #if 0
132 /* Map in kernel memory for /dev/kmem. */
133 m_geom[KMEM_DEV].dv_base = cvul64(kinfo.kmem_base);
134 m_geom[KMEM_DEV].dv_size = cvul64(kinfo.kmem_size);
135 if((m_vaddrs[KMEM_DEV] = vm_map_phys(SELF, (void *) kinfo.kmem_base,
136 kinfo.kmem_size)) == MAP_FAILED) {
137 printf("MEM: Couldn't map in /dev/kmem.");
139 #endif
141 /* Ramdisk image built into the memory driver */
142 m_geom[IMGRD_DEV].dv_base= cvul64(0);
143 m_geom[IMGRD_DEV].dv_size= cvul64(imgrd_size);
144 m_vaddrs[IMGRD_DEV] = (vir_bytes) imgrd;
146 /* Initialize /dev/zero. Simply write zeros into the buffer. */
147 for (i=0; i<ZERO_BUF_SIZE; i++) {
148 dev_zero[i] = '\0';
151 for(i = 0; i < NR_DEVS; i++)
152 openct[i] = 0;
154 /* Set up memory range for /dev/mem. */
155 m_geom[MEM_DEV].dv_base = cvul64(0);
156 m_geom[MEM_DEV].dv_size = cvul64(0xffffffff);
158 m_vaddrs[MEM_DEV] = (vir_bytes) MAP_FAILED; /* we are not mapping this in. */
160 return(OK);
163 /*===========================================================================*
164 * m_name *
165 *===========================================================================*/
166 PRIVATE char *m_name()
168 /* Return a name for the current device. */
169 static char name[] = "memory";
170 return name;
173 /*===========================================================================*
174 * m_prepare *
175 *===========================================================================*/
176 PRIVATE struct device *m_prepare(device)
177 int device;
179 /* Prepare for I/O on a device: check if the minor device number is ok. */
180 if (device < 0 || device >= NR_DEVS) return(NULL);
181 m_device = device;
183 return(&m_geom[device]);
186 /*===========================================================================*
187 * m_transfer *
188 *===========================================================================*/
189 PRIVATE int m_transfer(proc_nr, opcode, pos64, iov, nr_req)
190 int proc_nr; /* process doing the request */
191 int opcode; /* DEV_GATHER_S or DEV_SCATTER_S */
192 u64_t pos64; /* offset on device to read or write */
193 iovec_t *iov; /* pointer to read or write request vector */
194 unsigned nr_req; /* length of request vector */
196 /* Read or write one the driver's minor devices. */
197 unsigned count, left, chunk;
198 vir_bytes user_vir, vir_offset = 0;
199 struct device *dv;
200 unsigned long dv_size;
201 int s, r;
202 off_t position;
203 vir_bytes dev_vaddr;
205 /* ZERO_DEV and NULL_DEV are infinite in size. */
206 if (m_device != ZERO_DEV && m_device != NULL_DEV && ex64hi(pos64) != 0)
207 return OK; /* Beyond EOF */
208 position= cv64ul(pos64);
210 /* Get minor device number and check for /dev/null. */
211 dv = &m_geom[m_device];
212 dv_size = cv64ul(dv->dv_size);
213 dev_vaddr = m_vaddrs[m_device];
215 while (nr_req > 0) {
217 /* How much to transfer and where to / from. */
218 count = iov->iov_size;
219 user_vir = iov->iov_addr;
221 switch (m_device) {
223 /* No copying; ignore request. */
224 case NULL_DEV:
225 if (opcode == DEV_GATHER_S) return(OK); /* always at EOF */
226 break;
228 /* Virtual copying. For RAM disks, kernel memory and internal FS. */
229 default:
230 case KMEM_DEV:
231 case RAM_DEV_OLD:
232 case IMGRD_DEV:
233 /* Bogus number. */
234 if(m_device < 0 || m_device >= NR_DEVS) {
235 return(EINVAL);
237 if(!dev_vaddr || dev_vaddr == (vir_bytes) MAP_FAILED) {
238 printf("MEM: dev %d not initialized\n", m_device);
239 return EIO;
241 if (position >= dv_size) return(OK); /* check for EOF */
242 if (position + count > dv_size) count = dv_size - position;
243 if (opcode == DEV_GATHER_S) { /* copy actual data */
244 r=sys_safecopyto(proc_nr, user_vir, vir_offset,
245 dev_vaddr + position, count, D);
246 } else {
247 r=sys_safecopyfrom(proc_nr, user_vir, vir_offset,
248 dev_vaddr + position, count, D);
250 if(r != OK) {
251 panic("I/O copy failed: %d", r);
253 break;
255 /* Physical copying. Only used to access entire memory.
256 * Transfer one 'page window' at a time.
258 case MEM_DEV:
260 u32_t pagestart, page_off;
261 static u32_t pagestart_mapped;
262 static int any_mapped = 0;
263 static char *vaddr;
264 int r;
265 u32_t subcount;
266 phys_bytes mem_phys;
268 if (position >= dv_size)
269 return(OK); /* check for EOF */
270 if (position + count > dv_size)
271 count = dv_size - position;
272 mem_phys = position;
274 page_off = mem_phys % I386_PAGE_SIZE;
275 pagestart = mem_phys - page_off;
277 /* All memory to the map call has to be page-aligned.
278 * Don't have to map same page over and over.
280 if(!any_mapped || pagestart_mapped != pagestart) {
281 if(any_mapped) {
282 if(vm_unmap_phys(SELF, vaddr, I386_PAGE_SIZE) != OK)
283 panic("vm_unmap_phys failed");
284 any_mapped = 0;
286 vaddr = vm_map_phys(SELF, (void *) pagestart, I386_PAGE_SIZE);
287 if(vaddr == MAP_FAILED)
288 r = ENOMEM;
289 else
290 r = OK;
291 if(r != OK) {
292 printf("memory: vm_map_phys failed\n");
293 return r;
295 any_mapped = 1;
296 pagestart_mapped = pagestart;
299 /* how much to be done within this page. */
300 subcount = I386_PAGE_SIZE-page_off;
301 if(subcount > count)
302 subcount = count;
304 if (opcode == DEV_GATHER_S) { /* copy data */
305 s=sys_safecopyto(proc_nr, user_vir,
306 vir_offset, (vir_bytes) vaddr+page_off, subcount, D);
307 } else {
308 s=sys_safecopyfrom(proc_nr, user_vir,
309 vir_offset, (vir_bytes) vaddr+page_off, subcount, D);
311 if(s != OK)
312 return s;
313 count = subcount;
314 break;
317 /* Null byte stream generator. */
318 case ZERO_DEV:
319 if (opcode == DEV_GATHER_S) {
320 size_t suboffset = 0;
321 left = count;
322 while (left > 0) {
323 chunk = (left > ZERO_BUF_SIZE) ? ZERO_BUF_SIZE : left;
324 s=sys_safecopyto(proc_nr, user_vir,
325 vir_offset+suboffset, (vir_bytes) dev_zero, chunk, D);
326 if(s != OK)
327 printf("MEM: sys_safecopyto failed: %d\n", s);
328 left -= chunk;
329 suboffset += chunk;
332 break;
336 /* Book the number of bytes transferred. */
337 position += count;
338 vir_offset += count;
339 if ((iov->iov_size -= count) == 0) { iov++; nr_req--; vir_offset = 0; }
342 return(OK);
345 /*===========================================================================*
346 * m_do_open *
347 *===========================================================================*/
348 PRIVATE int m_do_open(dp, m_ptr)
349 struct driver *dp;
350 message *m_ptr;
352 int r;
354 /* Check device number on open. */
355 if (m_prepare(m_ptr->DEVICE) == NULL) return(ENXIO);
356 if (m_device == MEM_DEV)
358 r = sys_enable_iop(m_ptr->IO_ENDPT);
359 if (r != OK)
361 printf("m_do_open: sys_enable_iop failed for %d: %d\n",
362 m_ptr->IO_ENDPT, r);
363 return r;
367 if(m_device < 0 || m_device >= NR_DEVS) {
368 panic("wrong m_device: %d", m_device);
371 openct[m_device]++;
373 return(OK);
376 /*===========================================================================*
377 * m_do_close *
378 *===========================================================================*/
379 PRIVATE int m_do_close(dp, m_ptr)
380 struct driver *dp;
381 message *m_ptr;
383 int r;
385 if (m_prepare(m_ptr->DEVICE) == NULL) return(ENXIO);
387 if(m_device < 0 || m_device >= NR_DEVS) {
388 panic("wrong m_device: %d", m_device);
391 if(openct[m_device] < 1) {
392 panic("closed too often");
394 openct[m_device]--;
396 return(OK);
399 /*===========================================================================*
400 * m_ioctl *
401 *===========================================================================*/
402 PRIVATE int m_ioctl(dp, m_ptr)
403 struct driver *dp; /* pointer to driver structure */
404 message *m_ptr; /* pointer to control message */
406 /* I/O controls for the memory driver. Currently there is one I/O control:
407 * - MIOCRAMSIZE: to set the size of the RAM disk.
409 struct device *dv;
411 switch (m_ptr->REQUEST) {
412 case MIOCRAMSIZE: {
413 /* Someone wants to create a new RAM disk with the given size. */
414 u32_t ramdev_size;
415 int s, dev;
416 void *mem;
418 /* A ramdisk can be created only once, and only on RAM disk device. */
419 dev = m_ptr->DEVICE;
420 if(dev < 0 || dev >= NR_DEVS) {
421 printf("MEM: MIOCRAMSIZE: %d not a valid device\n", dev);
423 if((dev < RAM_DEV_FIRST || dev > RAM_DEV_LAST) && dev != RAM_DEV_OLD) {
424 printf("MEM: MIOCRAMSIZE: %d not a ramdisk\n", dev);
426 if ((dv = m_prepare(dev)) == NULL) return(ENXIO);
428 /* Get request structure */
429 s= sys_safecopyfrom(m_ptr->IO_ENDPT, (vir_bytes)m_ptr->IO_GRANT,
430 0, (vir_bytes)&ramdev_size, sizeof(ramdev_size), D);
431 if (s != OK)
432 return s;
433 if(m_vaddrs[dev] && !cmp64(dv->dv_size, cvul64(ramdev_size))) {
434 return(OK);
436 /* openct is 1 for the ioctl(). */
437 if(openct[dev] != 1) {
438 printf("MEM: MIOCRAMSIZE: %d in use (count %d)\n",
439 dev, openct[dev]);
440 return(EBUSY);
442 if(m_vaddrs[dev]) {
443 u32_t size;
444 if(ex64hi(dv->dv_size)) {
445 panic("huge old ramdisk");
447 size = ex64lo(dv->dv_size);
448 free((void *) m_vaddrs[dev]);
449 m_vaddrs[dev] = (vir_bytes) NULL;
452 #if DEBUG
453 printf("MEM:%d: allocating ramdisk of size 0x%x\n", dev, ramdev_size);
454 #endif
456 /* Try to allocate a piece of memory for the RAM disk. */
457 if(!(mem = malloc(ramdev_size))) {
458 printf("MEM: failed to get memory for ramdisk\n");
459 return(ENOMEM);
461 memset(mem, 0, ramdev_size);
463 m_vaddrs[dev] = (vir_bytes) mem;
465 dv->dv_size = cvul64(ramdev_size);
467 break;
470 default:
471 return(do_diocntl(&m_dtab, m_ptr));
473 return(OK);
476 /*===========================================================================*
477 * m_geometry *
478 *===========================================================================*/
479 PRIVATE void m_geometry(entry)
480 struct partition *entry;
482 /* Memory devices don't have a geometry, but the outside world insists. */
483 entry->cylinders = div64u(m_geom[m_device].dv_size, SECTOR_SIZE) / (64 * 32);
484 entry->heads = 64;
485 entry->sectors = 32;