1 /****************************************************************************
4 * This program and the accompanying materials
5 * are made available under the terms of the BSD License
6 * which accompanies this distribution, and is available at
7 * http://www.opensource.org/licenses/bsd-license.php
9 * Copyright (c) 2008 Pattrick Hueper <phueper@hueper.net>
10 ****************************************************************************/
12 #include <x86emu/x86emu.h>
13 #include "../x86emu/prim_ops.h"
21 /* this struct is used to remember which PMM spaces
22 * have been assigned. MAX_PMM_AREAS defines how many
23 * PMM areas we can assign.
24 * All areas are assigned in PMM_CONV_SEGMENT
27 u32 handle; /* handle that is returned to PMM caller */
28 u32 offset; /* in PMM_CONV_SEGMENT */
29 u32 length; /* length of this area */
32 #define MAX_PMM_AREAS 10
34 /* array to store the above structs */
35 static pmm_allocation_t pmm_allocation_array[MAX_PMM_AREAS];
37 /* index into pmm_allocation_array */
38 static u32 curr_pmm_allocation_index = 0;
40 /* This function is used to setup the PMM struct in virtual memory
41 * at a certain offset, the length of the PMM struct is returned */
42 u8 pmm_setup(u16 segment, u16 offset)
44 /* setup the PMM structure */
45 pmm_information_t *pis =
46 (pmm_information_t *) (M.mem_base + (((u32) segment) << 4) +
48 memset(pis, 0, sizeof(pmm_information_t));
49 /* set signature to $PMM */
50 pis->signature[0] = '$';
51 pis->signature[1] = 'P';
52 pis->signature[2] = 'M';
53 pis->signature[3] = 'M';
54 /* revision as specified */
55 pis->struct_rev = 0x01;
56 /* internal length, excluding code */
57 pis->length = ((void *)&(pis->code) - (void *)&(pis->signature));
58 /* the code to be executed, pointed to by entry_point_offset */
59 pis->code[0] = 0xCD; /* INT */
60 pis->code[1] = PMM_INT_NUM; /* my selfdefined PMM INT number */
61 pis->code[2] = 0xCB; /* RETF */
62 /* set the entry_point_offset, it should point to pis->code, segment is the segment of
63 * this struct. Since pis->length is the length of the struct excluding code, offset+pis->length
64 * points to the code... it's that simple ;-)
66 out32le(&(pis->entry_point_offset),
67 (u32) segment << 16 | (u32) (offset + pis->length));
68 /* checksum calculation */
71 for (i = 0; i < pis->length; i++) {
72 checksum += *(((u8 *) pis) + i);
74 pis->checksum = ((u8) 0) - checksum;
75 CHECK_DBG(DEBUG_PMM) {
76 DEBUG_PRINTF_PMM("PMM Structure:\n");
77 dump((void *)pis, sizeof(pmm_information_t));
79 return sizeof(pmm_information_t);
82 /* handle the selfdefined interrupt, this is executed, when the PMM Entry Point
83 * is executed, it must handle all PMM requests
92 /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
93 * according to the PMM Spec "the flags and all registers, except DX and AX
94 * are preserved across calls to PMM"
95 * so we save M.x86 and in :exit label we restore it, however, this means that no
96 * returns must be used in this function, any exit must use goto exit!
97 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
99 X86EMU_regs backup_regs = M.x86;
100 pop_long(); /* pop the return address, this is already saved in INT handler, we don't need
102 function = pop_word();
105 /* function pmmAllocate */
107 length *= 16; /* length is passed in "paragraphs" of 16 bytes each */
111 ("%s: pmmAllocate: Length: %x, Handle: %x, Flags: %x\n",
112 __func__, length, handle, flags);
113 if ((flags & 0x1) != 0) {
114 /* request to allocate in conventional memory */
115 if (curr_pmm_allocation_index >= MAX_PMM_AREAS) {
117 ("%s: pmmAllocate: Maximum Number of allocatable areas reached (%d), cannot allocate more memory!\n",
118 __func__, MAX_PMM_AREAS);
122 /* some ROMs seem to be confused by offset 0, so lets start at 0x100 */
123 u32 next_offset = 0x100;
124 pmm_allocation_t *pmm_alloc =
125 &(pmm_allocation_array[curr_pmm_allocation_index]);
126 if (curr_pmm_allocation_index != 0) {
127 /* we have already allocated... get the new next_offset
128 * from the previous pmm_allocation_t */
131 [curr_pmm_allocation_index - 1].offset +
133 [curr_pmm_allocation_index - 1].length;
135 DEBUG_PRINTF_PMM("%s: next_offset: 0x%x\n",
136 __func__, next_offset);
138 /* largest possible block size requested, we have on segment
139 * to allocate, so largest possible is segment size (0xFFFF)
142 rval = 0xFFFF - next_offset;
146 if (((flags & 0x4) != 0) && (length > 0)) {
147 /* align to least significant bit set in length param */
149 while (((length >> lsb) & 0x1) == 0) {
154 /* always align at least to paragraph (16byte) boundary
155 * hm... since the length is always in paragraphs, we cannot
156 * align outside of paragraphs anyway... so this check might
157 * be unnecessary...*/
161 DEBUG_PRINTF_PMM("%s: align: 0x%x\n", __func__,
163 if ((next_offset & (align - 1)) != 0) {
164 /* not yet aligned... align! */
165 next_offset += align;
166 next_offset &= ~(align - 1);
168 if ((next_offset + length) > 0xFFFF) {
171 ("%s: pmmAllocate: Not enough memory available for allocation!\n",
175 curr_pmm_allocation_index++;
176 /* remember the values in pmm_allocation_array */
177 pmm_alloc->handle = handle;
178 pmm_alloc->offset = next_offset;
179 pmm_alloc->length = length;
180 /* return the 32bit "physical" address, i.e. combination of segment and offset */
181 rval = ((u32) (PMM_CONV_SEGMENT << 16)) | next_offset;
183 ("%s: pmmAllocate: allocated memory at %x\n",
188 ("%s: pmmAllocate: allocation in extended memory not supported!\n",
193 /* function pmmFind */
194 handle = pop_long(); /* the handle to lookup */
195 DEBUG_PRINTF_PMM("%s: pmmFind: Handle: %x\n", __func__,
198 for (i = 0; i < curr_pmm_allocation_index; i++) {
199 if (pmm_allocation_array[i].handle == handle) {
201 ("%s: pmmFind: found allocated memory at %x\n",
203 /* return the 32bit "physical" address, i.e. combination of segment and offset */
205 ((u32) (PMM_CONV_SEGMENT << 16)) |
206 pmm_allocation_array[i].offset;
211 ("%s: pmmFind: handle (%x) not found!\n",
216 /* function pmmDeallocate */
218 /* since argument is the address of the PMM block (including the segment,
219 * we need to remove the segment to get the offset
221 buffer = buffer ^ ((u32) PMM_CONV_SEGMENT << 16);
222 DEBUG_PRINTF_PMM("%s: pmmDeallocate: PMM segment offset: %x\n",
225 /* rval = 0 means we deallocated the buffer, so set it to 1 in case we dont find it and
226 * thus cannot deallocate
229 for (i = 0; i < curr_pmm_allocation_index; i++) {
230 DEBUG_PRINTF_PMM("%d: %x\n", i,
231 pmm_allocation_array[i].handle);
232 if (pmm_allocation_array[i].offset == buffer) {
233 /* we found the requested buffer, rval = 0 */
236 ("%s: pmmDeallocate: found allocated memory at index: %d\n",
238 /* copy the remaining elements in pmm_allocation_array one position up */
240 for (; j < curr_pmm_allocation_index; j++) {
241 pmm_allocation_array[j] =
242 pmm_allocation_array[j + 1];
244 /* move curr_pmm_allocation_index one up, too */
245 curr_pmm_allocation_index--;
246 /* finally clean last element */
247 pmm_allocation_array[curr_pmm_allocation_index].
249 pmm_allocation_array[curr_pmm_allocation_index].
251 pmm_allocation_array[curr_pmm_allocation_index].
258 ("%s: pmmDeallocate: offset (%x) not found, cannot deallocate!\n",
263 /* invalid/unimplemented function */
264 printf("%s: invalid PMM function (0x%04x) called!\n",
266 /* PMM spec says if function is invalid, return 0xFFFFFFFF */
271 /* exit handler of this function, restore registers, put return value in DX:AX */
273 M.x86.R_DX = (u16) ((rval >> 16) & 0xFFFF);
274 M.x86.R_AX = (u16) (rval & 0xFFFF);
275 CHECK_DBG(DEBUG_PMM) {
276 DEBUG_PRINTF_PMM("%s: dump of pmm_allocation_array:\n",
278 for (i = 0; i < MAX_PMM_AREAS; i++) {
280 ("%d:\n\thandle: %x\n\toffset: %x\n\tlength: %x\n",
281 i, pmm_allocation_array[i].handle,
282 pmm_allocation_array[i].offset,
283 pmm_allocation_array[i].length);
289 /* This function tests the pmm_handleInt() function above. */
292 u32 handle, length, addr;
294 /*-------------------- Test simple allocation/find/deallocation ----------------------------- */
295 function = 0; /* pmmAllocate */
297 length = 16; /* in 16byte paragraphs, so we allocate 256 bytes... */
298 flags = 0x1; /* conventional memory, unaligned */
299 /* setup stack for call to pmm_handleInt() */
304 push_long(0); /* This is the return address for the ABI, unused in this implementation */
306 addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
307 DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__,
308 M.x86.R_DX, M.x86.R_AX);
309 function = 1; /* pmmFind */
312 push_long(0); /* This is the return address for the ABI, unused in this implementation */
314 DEBUG_PRINTF_PMM("%s: found memory at: %04x:%04x (expected: %08x)\n",
315 __func__, M.x86.R_DX, M.x86.R_AX, addr);
316 function = 2; /* pmmDeallocate */
319 push_long(0); /* This is the return address for the ABI, unused in this implementation */
322 ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
323 __func__, M.x86.R_DX, M.x86.R_AX);
324 /*-------------------- Test aligned allocation/deallocation ----------------------------- */
325 function = 0; /* pmmAllocate */
327 length = 257; /* in 16byte paragraphs, so we allocate 4KB + 16 bytes... */
328 flags = 0x1; /* conventional memory, unaligned */
329 /* setup stack for call to pmm_handleInt() */
334 push_long(0); /* This is the return address for the ABI, unused in this implementation */
336 addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
337 DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__,
338 M.x86.R_DX, M.x86.R_AX);
339 function = 0; /* pmmAllocate */
341 length = 128; /* in 16byte paragraphs, so we allocate 2KB... */
342 flags = 0x5; /* conventional memory, aligned */
343 /* setup stack for call to pmm_handleInt() */
348 push_long(0); /* This is the return address for the ABI, unused in this implementation */
350 /* the address should be aligned to 0x800, so probably it is at offset 0x1800... */
351 addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
352 DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__,
353 M.x86.R_DX, M.x86.R_AX);
354 function = 1; /* pmmFind */
357 push_long(0); /* This is the return address for the ABI, unused in this implementation */
359 addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
360 function = 2; /* pmmDeallocate */
363 push_long(0); /* This is the return address for the ABI, unused in this implementation */
366 ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
367 __func__, M.x86.R_DX, M.x86.R_AX);
369 function = 1; /* pmmFind */
372 push_long(0); /* This is the return address for the ABI, unused in this implementation */
374 addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
375 function = 2; /* pmmDeallocate */
378 push_long(0); /* This is the return address for the ABI, unused in this implementation */
381 ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
382 __func__, M.x86.R_DX, M.x86.R_AX);
383 /*-------------------- Test out of memory allocation ----------------------------- */
384 function = 0; /* pmmAllocate */
386 length = 0; /* length zero means, give me the largest possible block */
387 flags = 0x1; /* conventional memory, unaligned */
388 /* setup stack for call to pmm_handleInt() */
393 push_long(0); /* This is the return address for the ABI, unused in this implementation */
395 length = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
396 length /= 16; /* length in paragraphs */
397 DEBUG_PRINTF_PMM("%s: largest possible length: %08x\n", __func__,
399 function = 0; /* pmmAllocate */
400 flags = 0x1; /* conventional memory, aligned */
401 /* setup stack for call to pmm_handleInt() */
406 push_long(0); /* This is the return address for the ABI, unused in this implementation */
408 addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
409 DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__,
410 M.x86.R_DX, M.x86.R_AX);
411 function = 0; /* pmmAllocate */
414 flags = 0x1; /* conventional memory, aligned */
415 /* setup stack for call to pmm_handleInt() */
420 push_long(0); /* This is the return address for the ABI, unused in this implementation */
422 /* this should fail, so 0x0 should be returned */
423 addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
425 ("%s: allocated memory at: %04x:%04x expected: 0000:0000\n",
426 __func__, M.x86.R_DX, M.x86.R_AX);
428 function = 1; /* pmmFind */
431 push_long(0); /* This is the return address for the ABI, unused in this implementation */
433 addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
434 function = 2; /* pmmDeallocate */
437 push_long(0); /* This is the return address for the ABI, unused in this implementation */
440 ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
441 __func__, M.x86.R_DX, M.x86.R_AX);