|
|
1.1 root 1: #include "ds_oskit.h"
2:
3: #include <oskit/machine/physmem.h>
4: #include <vm/vm_map.h>
5: #include <string.h>
6:
7:
8: /* For device_get_status, we look like a block device. */
9: io_return_t
10: ds_mem_get_status (device_t dev, dev_flavor_t flavor, dev_status_t status,
11: mach_msg_type_number_t *status_count)
12: {
13: switch (flavor)
14: {
15: case DEV_GET_SIZE:
16: status[DEV_GET_SIZE_RECORD_SIZE] = dev->com.mem.recsize;
17: status[DEV_GET_SIZE_DEVICE_SIZE] = dev->com.mem.size;
18: *status_count = 2;
19: return D_SUCCESS;
20: }
21: INVALOP;
22: }
23:
24: /* This is the main use for these pseudo-devices, to map physical pages
25: into user tasks. This is called from the device pager to get a physical
26: page address that will be inserted into a task's page tables. So we can
27: safely use any physical address at all, regardless of whether it is
28: mapped into the kernel address space. */
29: io_return_t
30: ds_mem_map (device_t dev, vm_prot_t prot,
31: vm_offset_t offset, vm_size_t size, oskit_addr_t *pa)
32: {
33: if (offset == 0 && size == 0)
34: /* Special case for compatibility with the old Mach driver behavior. */
35: size = dev->com.mem.size;
36:
37: if (offset % dev->com.mem.recsize || !page_aligned (offset))
38: INVALREC;
39: if (trunc_page (offset) > trunc_page (dev->com.mem.size))
40: INVALREC;
41: if (trunc_page (offset + size - 1) > trunc_page (dev->com.mem.size - 1))
42: INVALSZ;
43: if (round_page (size) % dev->com.mem.recsize)
44: INVALSZ;
45:
46: if (pa)
47: *pa = dev->com.mem.pa + offset;
48: return 0;
49: }
50:
51:
52: /* Real physical memory is already direct-mapped into the kernel address
53: space, but i/o memory may not be. To copy into or out of i/o memory
54: addresses, we need to map pages into the kernel address space
55: temporarily. */
56: static int
57: direct_mapped (vm_offset_t pa)
58: {
59: return (trunc_page (pa) >= PAGE_SIZE &&
60: trunc_page (pa) >= round_page (phys_mem_min) &&
61: round_page (pa) <= trunc_page (phys_mem_max));
62: }
63:
64: static int
65: map_phys (vm_offset_t pa, unsigned *count, vm_prot_t prot,
66: vm_offset_t *kva, vm_size_t *mapsz, vm_offset_t *ofs)
67: {
68: vm_offset_t start = trunc_page (pa), end = round_page (pa + *count);
69: kern_return_t kr;
70: vm_offset_t addr;
71:
72: /* Find some address space to use.
73: If we can't get it all, get a smaller amount. */
74: addr = vm_map_min (device_io_map);
75: while ((kr = vm_map_enter (device_io_map, &addr, end - start, 0, TRUE,
76: 0, 0, FALSE, prot, prot, VM_INHERIT_NONE))
77: == KERN_NO_SPACE)
78: end = start + (end - start) / 2;
79: if (kr != KERN_SUCCESS)
80: return kr;
81:
82: *kva = addr;
83: *ofs = pa - start;
84: if (end < pa + *count)
85: *count = end - pa;
86: *mapsz = end - start;
87:
88: /* Now map those physical pages in. */
89: do
90: {
91: pmap_enter (vm_map_pmap (device_io_map), addr, start, prot, TRUE);
92: start += PAGE_SIZE;
93: addr += PAGE_SIZE;
94: } while (start < end);
95:
96: return 0;
97: }
98:
99: static void
100: unmap_phys (vm_offset_t addr, vm_size_t size)
101: {
102: vm_map_remove (device_io_map, addr, addr + size);
103: }
104:
105:
106: io_return_t
107: ds_mem_read_inband (device_t dev, ipc_port_t reply_port,
108: mach_msg_type_name_t reply_port_type, dev_mode_t mode,
109: recnum_t recnum, int count, char *data,
110: unsigned *bytes_read)
111: {
112: vm_offset_t pa;
113:
114: if (count == 0)
115: {
116: *bytes_read = 0;
117: return 0;
118: }
119: recnum *= dev->com.mem.recsize;
120: if ((oskit_size_t) recnum > dev->com.mem.size)
121: INVALREC;
122: if ((oskit_size_t) recnum + count > dev->com.mem.size)
123: INVALSZ;
124:
125: pa = dev->com.mem.pa + recnum;
126: *bytes_read = count;
127: if (direct_mapped (pa) && direct_mapped (pa + recnum - 1))
128: memcpy (data, (char *) phystokv (pa), count);
129: else
130: {
131: vm_offset_t kva, ofs;
132: vm_size_t mapsz;
133: if (! map_phys (pa, bytes_read, VM_PROT_READ,
134: &kva, &mapsz, &ofs))
135: return D_NO_MEMORY;
136: memcpy (data, (char *) kva + ofs, *bytes_read);
137: unmap_phys (kva, mapsz);
138: }
139:
140: return 0;
141: }
142:
143: io_return_t
144: ds_mem_write_inband (device_t dev, ipc_port_t reply_port,
145: mach_msg_type_name_t reply_port_type, dev_mode_t mode,
146: recnum_t recnum,
147: io_buf_ptr_t data, unsigned int count,
148: int *bytes_written)
149: {
150: vm_offset_t pa;
151:
152: if (count == 0)
153: {
154: *bytes_written = 0;
155: return 0;
156: }
157: recnum *= dev->com.mem.recsize;
158: if ((oskit_size_t) recnum > dev->com.mem.size)
159: INVALREC;
160: if ((oskit_size_t) recnum + count > dev->com.mem.size)
161: INVALSZ;
162:
163: pa = dev->com.mem.pa + recnum;
164: *bytes_written = count;
165: if (direct_mapped (pa) && direct_mapped (pa + recnum - 1))
166: memcpy ((char *) phystokv (pa), data, count);
167: else
168: {
169: vm_offset_t kva, ofs;
170: vm_size_t mapsz;
171: if (! map_phys (pa, (unsigned *) bytes_written,
172: VM_PROT_READ | VM_PROT_WRITE,
173: &kva, &mapsz, &ofs))
174: return D_NO_MEMORY;
175: memcpy ((char *) kva + ofs, data, *bytes_written);
176: unmap_phys (kva, mapsz);
177: }
178:
179: return 0;
180: }
181:
182:
183: const struct device_ops mem_device_ops =
184: {
185: read_inband: ds_mem_read_inband,
186: write_inband: ds_mem_write_inband,
187: get_status: ds_mem_get_status,
188: map: ds_mem_map,
189: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.