1 /****************************************************************************
4 * Copyright (c) 2008 Pattrick Hueper <phueper@hueper.net>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
12 * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
15 * Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer
17 * in the documentation and/or other materials provided with the
20 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 ****************************************************************************/
34 #include <x86emu/x86emu.h>
35 #include "../x86emu/prim_ops.h"
43 /* this struct is used to remember which PMM spaces
44 * have been assigned. MAX_PMM_AREAS defines how many
45 * PMM areas we can assign.
46 * All areas are assigned in PMM_CONV_SEGMENT
49 u32 handle
; /* handle that is returned to PMM caller */
50 u32 offset
; /* in PMM_CONV_SEGMENT */
51 u32 length
; /* length of this area */
54 #define MAX_PMM_AREAS 10
56 /* array to store the above structs */
57 static pmm_allocation_t pmm_allocation_array
[MAX_PMM_AREAS
];
59 /* index into pmm_allocation_array */
60 static u32 curr_pmm_allocation_index
= 0;
62 /* This function is used to setup the PMM struct in virtual memory
63 * at a certain offset, the length of the PMM struct is returned */
64 u8
pmm_setup(u16 segment
, u16 offset
)
66 /* setup the PMM structure */
67 pmm_information_t
*pis
=
68 (pmm_information_t
*) (M
.mem_base
+ (((u32
) segment
) << 4) +
70 memset(pis
, 0, sizeof(pmm_information_t
));
71 /* set signature to $PMM */
72 pis
->signature
[0] = '$';
73 pis
->signature
[1] = 'P';
74 pis
->signature
[2] = 'M';
75 pis
->signature
[3] = 'M';
76 /* revision as specified */
77 pis
->struct_rev
= 0x01;
78 /* internal length, excluding code */
79 pis
->length
= ((void *)&(pis
->code
) - (void *)&(pis
->signature
));
80 /* the code to be executed, pointed to by entry_point_offset */
81 pis
->code
[0] = 0xCD; /* INT */
82 pis
->code
[1] = PMM_INT_NUM
; /* my selfdefined PMM INT number */
83 pis
->code
[2] = 0xCB; /* RETF */
84 /* set the entry_point_offset, it should point to pis->code, segment is the segment of
85 * this struct. Since pis->length is the length of the struct excluding code, offset+pis->length
86 * points to the code... it's that simple ;-)
88 out32le(&(pis
->entry_point_offset
),
89 (u32
) segment
<< 16 | (u32
) (offset
+ pis
->length
));
90 /* checksum calculation */
93 for (i
= 0; i
< pis
->length
; i
++) {
94 checksum
+= *(((u8
*) pis
) + i
);
96 pis
->checksum
= ((u8
) 0) - checksum
;
97 CHECK_DBG(DEBUG_PMM
) {
98 DEBUG_PRINTF_PMM("PMM Structure:\n");
99 dump((void *)pis
, sizeof(pmm_information_t
));
101 return sizeof(pmm_information_t
);
104 /* handle the selfdefined interrupt, this is executed, when the PMM Entry Point
105 * is executed, it must handle all PMM requests
107 void pmm_handleInt(void)
114 /* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
115 * according to the PMM Spec "the flags and all registers, except DX and AX
116 * are preserved across calls to PMM"
117 * so we save M.x86 and in :exit label we restore it, however, this means that no
118 * returns must be used in this function, any exit must use goto exit!
119 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
121 X86EMU_regs backup_regs
= M
.x86
;
122 pop_long(); /* pop the return address, this is already saved in INT handler, we don't need
124 function
= pop_word();
127 /* function pmmAllocate */
129 length
*= 16; /* length is passed in "paragraphs" of 16 bytes each */
133 ("%s: pmmAllocate: Length: %x, Handle: %x, Flags: %x\n",
134 __func__
, length
, handle
, flags
);
135 if ((flags
& 0x1) != 0) {
136 /* request to allocate in conventional memory */
137 if (curr_pmm_allocation_index
>= MAX_PMM_AREAS
) {
139 ("%s: pmmAllocate: Maximum Number of allocatable areas reached (%d), cannot allocate more memory!\n",
140 __func__
, MAX_PMM_AREAS
);
144 /* some ROMs seem to be confused by offset 0, so lets start at 0x100 */
145 u32 next_offset
= 0x100;
146 pmm_allocation_t
*pmm_alloc
=
147 &(pmm_allocation_array
[curr_pmm_allocation_index
]);
148 if (curr_pmm_allocation_index
!= 0) {
149 /* we have already allocated... get the new next_offset
150 * from the previous pmm_allocation_t */
153 [curr_pmm_allocation_index
- 1].offset
+
155 [curr_pmm_allocation_index
- 1].length
;
157 DEBUG_PRINTF_PMM("%s: next_offset: 0x%x\n",
158 __func__
, next_offset
);
160 /* largest possible block size requested, we have on segment
161 * to allocate, so largest possible is segment size (0xFFFF)
164 rval
= 0xFFFF - next_offset
;
168 if (((flags
& 0x4) != 0) && (length
> 0)) {
169 /* align to least significant bit set in length param */
171 while (((length
>> lsb
) & 0x1) == 0) {
176 /* always align at least to paragraph (16byte) boundary
177 * hm... since the length is always in paragraphs, we cannot
178 * align outside of paragraphs anyway... so this check might
179 * be unnecessary...*/
183 DEBUG_PRINTF_PMM("%s: align: 0x%x\n", __func__
,
185 if ((next_offset
& (align
- 1)) != 0) {
186 /* not yet aligned... align! */
187 next_offset
+= align
;
188 next_offset
&= ~(align
- 1);
190 if ((next_offset
+ length
) > 0xFFFF) {
193 ("%s: pmmAllocate: Not enough memory available for allocation!\n",
197 curr_pmm_allocation_index
++;
198 /* remember the values in pmm_allocation_array */
199 pmm_alloc
->handle
= handle
;
200 pmm_alloc
->offset
= next_offset
;
201 pmm_alloc
->length
= length
;
202 /* return the 32bit "physical" address, i.e. combination of segment and offset */
203 rval
= ((u32
) (PMM_CONV_SEGMENT
<< 16)) | next_offset
;
205 ("%s: pmmAllocate: allocated memory at %x\n",
210 ("%s: pmmAllocate: allocation in extended memory not supported!\n",
215 /* function pmmFind */
216 handle
= pop_long(); /* the handle to lookup */
217 DEBUG_PRINTF_PMM("%s: pmmFind: Handle: %x\n", __func__
,
220 for (i
= 0; i
< curr_pmm_allocation_index
; i
++) {
221 if (pmm_allocation_array
[i
].handle
== handle
) {
223 ("%s: pmmFind: found allocated memory at %x\n",
225 /* return the 32bit "physical" address, i.e. combination of segment and offset */
227 ((u32
) (PMM_CONV_SEGMENT
<< 16)) |
228 pmm_allocation_array
[i
].offset
;
233 ("%s: pmmFind: handle (%x) not found!\n",
238 /* function pmmDeallocate */
240 /* since argument is the address of the PMM block (including the segment,
241 * we need to remove the segment to get the offset
243 buffer
= buffer
^ ((u32
) PMM_CONV_SEGMENT
<< 16);
244 DEBUG_PRINTF_PMM("%s: pmmDeallocate: PMM segment offset: %x\n",
247 /* rval = 0 means we deallocated the buffer, so set it to 1 in case we don't find it and
248 * thus cannot deallocate
251 for (i
= 0; i
< curr_pmm_allocation_index
; i
++) {
252 DEBUG_PRINTF_PMM("%d: %x\n", i
,
253 pmm_allocation_array
[i
].handle
);
254 if (pmm_allocation_array
[i
].offset
== buffer
) {
255 /* we found the requested buffer, rval = 0 */
258 ("%s: pmmDeallocate: found allocated memory at index: %d\n",
260 /* copy the remaining elements in pmm_allocation_array one position up */
262 for (; j
< curr_pmm_allocation_index
; j
++) {
263 pmm_allocation_array
[j
] =
264 pmm_allocation_array
[j
+ 1];
266 /* move curr_pmm_allocation_index one up, too */
267 curr_pmm_allocation_index
--;
268 /* finally clean last element */
269 pmm_allocation_array
[curr_pmm_allocation_index
].
271 pmm_allocation_array
[curr_pmm_allocation_index
].
273 pmm_allocation_array
[curr_pmm_allocation_index
].
280 ("%s: pmmDeallocate: offset (%x) not found, cannot deallocate!\n",
285 /* invalid/unimplemented function */
286 printf("%s: invalid PMM function (0x%04x) called!\n",
288 /* PMM spec says if function is invalid, return 0xFFFFFFFF */
293 /* exit handler of this function, restore registers, put return value in DX:AX */
295 M
.x86
.R_DX
= (u16
) ((rval
>> 16) & 0xFFFF);
296 M
.x86
.R_AX
= (u16
) (rval
& 0xFFFF);
297 CHECK_DBG(DEBUG_PMM
) {
298 DEBUG_PRINTF_PMM("%s: dump of pmm_allocation_array:\n",
300 for (i
= 0; i
< MAX_PMM_AREAS
; i
++) {
302 ("%d:\n\thandle: %x\n\toffset: %x\n\tlength: %x\n",
303 i
, pmm_allocation_array
[i
].handle
,
304 pmm_allocation_array
[i
].offset
,
305 pmm_allocation_array
[i
].length
);
311 /* This function tests the pmm_handleInt() function above. */
314 u32 handle
, length
, addr
;
316 /*-------------------- Test simple allocation/find/deallocation ----------------------------- */
317 function
= 0; /* pmmAllocate */
319 length
= 16; /* in 16byte paragraphs, so we allocate 256 bytes... */
320 flags
= 0x1; /* conventional memory, unaligned */
321 /* setup stack for call to pmm_handleInt() */
326 push_long(0); /* This is the return address for the ABI, unused in this implementation */
328 addr
= ((u32
) M
.x86
.R_DX
<< 16) | M
.x86
.R_AX
;
329 DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__
,
330 M
.x86
.R_DX
, M
.x86
.R_AX
);
331 function
= 1; /* pmmFind */
334 push_long(0); /* This is the return address for the ABI, unused in this implementation */
336 DEBUG_PRINTF_PMM("%s: found memory at: %04x:%04x (expected: %08x)\n",
337 __func__
, M
.x86
.R_DX
, M
.x86
.R_AX
, addr
);
338 function
= 2; /* pmmDeallocate */
341 push_long(0); /* This is the return address for the ABI, unused in this implementation */
344 ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
345 __func__
, M
.x86
.R_DX
, M
.x86
.R_AX
);
346 /*-------------------- Test aligned allocation/deallocation ----------------------------- */
347 function
= 0; /* pmmAllocate */
349 length
= 257; /* in 16byte paragraphs, so we allocate 4KB + 16 bytes... */
350 flags
= 0x1; /* conventional memory, unaligned */
351 /* setup stack for call to pmm_handleInt() */
356 push_long(0); /* This is the return address for the ABI, unused in this implementation */
358 addr
= ((u32
) M
.x86
.R_DX
<< 16) | M
.x86
.R_AX
;
359 DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__
,
360 M
.x86
.R_DX
, M
.x86
.R_AX
);
361 function
= 0; /* pmmAllocate */
363 length
= 128; /* in 16byte paragraphs, so we allocate 2KB... */
364 flags
= 0x5; /* conventional memory, aligned */
365 /* setup stack for call to pmm_handleInt() */
370 push_long(0); /* This is the return address for the ABI, unused in this implementation */
372 /* the address should be aligned to 0x800, so probably it is at offset 0x1800... */
373 addr
= ((u32
) M
.x86
.R_DX
<< 16) | M
.x86
.R_AX
;
374 DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__
,
375 M
.x86
.R_DX
, M
.x86
.R_AX
);
376 function
= 1; /* pmmFind */
379 push_long(0); /* This is the return address for the ABI, unused in this implementation */
381 addr
= ((u32
) M
.x86
.R_DX
<< 16) | M
.x86
.R_AX
;
382 function
= 2; /* pmmDeallocate */
385 push_long(0); /* This is the return address for the ABI, unused in this implementation */
388 ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
389 __func__
, M
.x86
.R_DX
, M
.x86
.R_AX
);
391 function
= 1; /* pmmFind */
394 push_long(0); /* This is the return address for the ABI, unused in this implementation */
396 addr
= ((u32
) M
.x86
.R_DX
<< 16) | M
.x86
.R_AX
;
397 function
= 2; /* pmmDeallocate */
400 push_long(0); /* This is the return address for the ABI, unused in this implementation */
403 ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
404 __func__
, M
.x86
.R_DX
, M
.x86
.R_AX
);
405 /*-------------------- Test out of memory allocation ----------------------------- */
406 function
= 0; /* pmmAllocate */
408 length
= 0; /* length zero means, give me the largest possible block */
409 flags
= 0x1; /* conventional memory, unaligned */
410 /* setup stack for call to pmm_handleInt() */
415 push_long(0); /* This is the return address for the ABI, unused in this implementation */
417 length
= ((u32
) M
.x86
.R_DX
<< 16) | M
.x86
.R_AX
;
418 length
/= 16; /* length in paragraphs */
419 DEBUG_PRINTF_PMM("%s: largest possible length: %08x\n", __func__
,
421 function
= 0; /* pmmAllocate */
422 flags
= 0x1; /* conventional memory, aligned */
423 /* setup stack for call to pmm_handleInt() */
428 push_long(0); /* This is the return address for the ABI, unused in this implementation */
430 addr
= ((u32
) M
.x86
.R_DX
<< 16) | M
.x86
.R_AX
;
431 DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__
,
432 M
.x86
.R_DX
, M
.x86
.R_AX
);
433 function
= 0; /* pmmAllocate */
436 flags
= 0x1; /* conventional memory, aligned */
437 /* setup stack for call to pmm_handleInt() */
442 push_long(0); /* This is the return address for the ABI, unused in this implementation */
444 /* this should fail, so 0x0 should be returned */
445 addr
= ((u32
) M
.x86
.R_DX
<< 16) | M
.x86
.R_AX
;
447 ("%s: allocated memory at: %04x:%04x expected: 0000:0000\n",
448 __func__
, M
.x86
.R_DX
, M
.x86
.R_AX
);
450 function
= 1; /* pmmFind */
453 push_long(0); /* This is the return address for the ABI, unused in this implementation */
455 addr
= ((u32
) M
.x86
.R_DX
<< 16) | M
.x86
.R_AX
;
456 function
= 2; /* pmmDeallocate */
459 push_long(0); /* This is the return address for the ABI, unused in this implementation */
462 ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
463 __func__
, M
.x86
.R_DX
, M
.x86
.R_AX
);