blob: d7e1f7beba455a59c117f5ccb1bfbe17fc97e323 [file] [log] [blame]
Stefan Reinauer38cd29e2009-08-11 21:28:25 +00001/****************************************************************************
2 * YABEL BIOS Emulator
3 *
Stefan Reinauer38cd29e2009-08-11 21:28:25 +00004 * Copyright (c) 2008 Pattrick Hueper <phueper@hueper.net>
Martin Rotha9e1a222016-01-14 14:15:24 -07005 *
6 * All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions are
10 * met:
11 *
12 * Redistributions of source code must retain the above copyright
13 * notice, this list of conditions and the following disclaimer.
14 *
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
18 * distribution.
19 *
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.
31 *
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000032 ****************************************************************************/
33
34#include <x86emu/x86emu.h>
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000035#include "../x86emu/prim_ops.h"
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000036#include <string.h>
37
38#include "biosemu.h"
39#include "pmm.h"
40#include "debug.h"
41#include "device.h"
42
43/* this struct is used to remember which PMM spaces
Stefan Reinauer14e22772010-04-27 06:56:47 +000044 * have been assigned. MAX_PMM_AREAS defines how many
45 * PMM areas we can assign.
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000046 * All areas are assigned in PMM_CONV_SEGMENT
47 */
48typedef struct {
49 u32 handle; /* handle that is returned to PMM caller */
50 u32 offset; /* in PMM_CONV_SEGMENT */
51 u32 length; /* length of this area */
52} pmm_allocation_t;
53
54#define MAX_PMM_AREAS 10
55
56/* array to store the above structs */
57static pmm_allocation_t pmm_allocation_array[MAX_PMM_AREAS];
58
59/* index into pmm_allocation_array */
60static u32 curr_pmm_allocation_index = 0;
61
Stefan Reinauer14e22772010-04-27 06:56:47 +000062/* This function is used to setup the PMM struct in virtual memory
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000063 * at a certain offset, the length of the PMM struct is returned */
64u8 pmm_setup(u16 segment, u16 offset)
65{
66 /* setup the PMM structure */
67 pmm_information_t *pis =
68 (pmm_information_t *) (M.mem_base + (((u32) segment) << 4) +
69 offset);
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 ;-)
87 */
88 out32le(&(pis->entry_point_offset),
89 (u32) segment << 16 | (u32) (offset + pis->length));
90 /* checksum calculation */
91 u8 i;
92 u8 checksum = 0;
93 for (i = 0; i < pis->length; i++) {
94 checksum += *(((u8 *) pis) + i);
95 }
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));
100 }
101 return sizeof(pmm_information_t);
102}
103
Stefan Reinauer14e22772010-04-27 06:56:47 +0000104/* handle the selfdefined interrupt, this is executed, when the PMM Entry Point
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000105 * is executed, it must handle all PMM requests
106 */
107void pmm_handleInt()
108{
109 u32 rval = 0;
110 u16 function, flags;
111 u32 handle, length;
112 u32 i, j;
113 u32 buffer;
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 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
120 */
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
123 to remember this. */
124 function = pop_word();
125 switch (function) {
126 case 0:
127 /* function pmmAllocate */
128 length = pop_long();
129 length *= 16; /* length is passed in "paragraphs" of 16 bytes each */
130 handle = pop_long();
131 flags = pop_word();
132 DEBUG_PRINTF_PMM
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) {
138 printf
139 ("%s: pmmAllocate: Maximum Number of allocatable areas reached (%d), cannot allocate more memory!\n",
140 __func__, MAX_PMM_AREAS);
141 rval = 0;
142 goto exit;
143 }
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 */
151 next_offset =
152 pmm_allocation_array
153 [curr_pmm_allocation_index - 1].offset +
154 pmm_allocation_array
155 [curr_pmm_allocation_index - 1].length;
156 }
157 DEBUG_PRINTF_PMM("%s: next_offset: 0x%x\n",
158 __func__, next_offset);
159 if (length == 0) {
160 /* largest possible block size requested, we have on segment
Stefan Reinauer14e22772010-04-27 06:56:47 +0000161 * to allocate, so largest possible is segment size (0xFFFF)
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000162 * minus next_offset
163 */
164 rval = 0xFFFF - next_offset;
165 goto exit;
166 }
167 u32 align = 0;
168 if (((flags & 0x4) != 0) && (length > 0)) {
169 /* align to least significant bit set in length param */
170 u8 lsb = 0;
171 while (((length >> lsb) & 0x1) == 0) {
172 lsb++;
173 }
174 align = 1 << lsb;
175 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000176 /* always align at least to paragraph (16byte) boundary
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000177 * hm... since the length is always in paragraphs, we cannot
178 * align outside of paragraphs anyway... so this check might
179 * be unnecessary...*/
180 if (align < 0x10) {
181 align = 0x10;
182 }
183 DEBUG_PRINTF_PMM("%s: align: 0x%x\n", __func__,
184 align);
185 if ((next_offset & (align - 1)) != 0) {
186 /* not yet aligned... align! */
187 next_offset += align;
188 next_offset &= ~(align - 1);
189 }
190 if ((next_offset + length) > 0xFFFF) {
191 rval = 0;
192 printf
193 ("%s: pmmAllocate: Not enough memory available for allocation!\n",
194 __func__);
195 goto exit;
196 }
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;
204 DEBUG_PRINTF_PMM
205 ("%s: pmmAllocate: allocated memory at %x\n",
206 __func__, rval);
207 } else {
208 rval = 0;
209 printf
210 ("%s: pmmAllocate: allocation in extended memory not supported!\n",
211 __func__);
212 }
213 goto exit;
214 case 1:
215 /* function pmmFind */
216 handle = pop_long(); /* the handle to lookup */
217 DEBUG_PRINTF_PMM("%s: pmmFind: Handle: %x\n", __func__,
218 handle);
219 i = 0;
220 for (i = 0; i < curr_pmm_allocation_index; i++) {
221 if (pmm_allocation_array[i].handle == handle) {
222 DEBUG_PRINTF_PMM
223 ("%s: pmmFind: found allocated memory at %x\n",
224 __func__, rval);
225 /* return the 32bit "physical" address, i.e. combination of segment and offset */
226 rval =
227 ((u32) (PMM_CONV_SEGMENT << 16)) |
228 pmm_allocation_array[i].offset;
229 }
230 }
231 if (rval == 0) {
232 DEBUG_PRINTF_PMM
233 ("%s: pmmFind: handle (%x) not found!\n",
234 __func__, handle);
235 }
236 goto exit;
237 case 2:
238 /* function pmmDeallocate */
239 buffer = pop_long();
240 /* since argument is the address of the PMM block (including the segment,
241 * we need to remove the segment to get the offset
242 */
243 buffer = buffer ^ ((u32) PMM_CONV_SEGMENT << 16);
244 DEBUG_PRINTF_PMM("%s: pmmDeallocate: PMM segment offset: %x\n",
245 __func__, buffer);
246 i = 0;
Martin Roth63373ed2013-07-08 16:24:19 -0600247 /* rval = 0 means we deallocated the buffer, so set it to 1 in case we don't find it and
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000248 * thus cannot deallocate
249 */
250 rval = 1;
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 */
256 rval = 0;
257 DEBUG_PRINTF_PMM
258 ("%s: pmmDeallocate: found allocated memory at index: %d\n",
259 __func__, i);
260 /* copy the remaining elements in pmm_allocation_array one position up */
261 j = i;
262 for (; j < curr_pmm_allocation_index; j++) {
263 pmm_allocation_array[j] =
264 pmm_allocation_array[j + 1];
265 }
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].
270 handle = 0;
271 pmm_allocation_array[curr_pmm_allocation_index].
272 offset = 0;
273 pmm_allocation_array[curr_pmm_allocation_index].
274 length = 0;
275 break;
276 }
277 }
278 if (rval != 0) {
279 DEBUG_PRINTF_PMM
280 ("%s: pmmDeallocate: offset (%x) not found, cannot deallocate!\n",
281 __func__, buffer);
282 }
283 goto exit;
284 default:
285 /* invalid/unimplemented function */
286 printf("%s: invalid PMM function (0x%04x) called!\n",
287 __func__, function);
288 /* PMM spec says if function is invalid, return 0xFFFFFFFF */
289 rval = 0xFFFFFFFF;
290 goto exit;
291 }
Patrick Georgic5fc7db2012-03-07 15:55:47 +0100292exit:
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000293 /* exit handler of this function, restore registers, put return value in DX:AX */
294 M.x86 = backup_regs;
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",
299 __func__);
300 for (i = 0; i < MAX_PMM_AREAS; i++) {
301 DEBUG_PRINTF_PMM
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);
306 }
307 }
308 return;
309}
310
311/* This function tests the pmm_handleInt() function above. */
312void pmm_test(void)
313{
314 u32 handle, length, addr;
315 u16 function, flags;
316 /*-------------------- Test simple allocation/find/deallocation ----------------------------- */
317 function = 0; /* pmmAllocate */
318 handle = 0xdeadbeef;
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() */
322 push_word(flags);
323 push_long(handle);
324 push_long(length);
325 push_word(function);
326 push_long(0); /* This is the return address for the ABI, unused in this implementation */
327 pmm_handleInt();
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 */
332 push_long(handle);
333 push_word(function);
334 push_long(0); /* This is the return address for the ABI, unused in this implementation */
335 pmm_handleInt();
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 */
339 push_long(addr);
340 push_word(function);
341 push_long(0); /* This is the return address for the ABI, unused in this implementation */
342 pmm_handleInt();
343 DEBUG_PRINTF_PMM
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 */
348 handle = 0xdeadbeef;
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() */
352 push_word(flags);
353 push_long(handle);
354 push_long(length);
355 push_word(function);
356 push_long(0); /* This is the return address for the ABI, unused in this implementation */
357 pmm_handleInt();
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 */
362 handle = 0xf00d4b0b;
363 length = 128; /* in 16byte paragraphs, so we allocate 2KB... */
364 flags = 0x5; /* conventional memory, aligned */
365 /* setup stack for call to pmm_handleInt() */
366 push_word(flags);
367 push_long(handle);
368 push_long(length);
369 push_word(function);
370 push_long(0); /* This is the return address for the ABI, unused in this implementation */
371 pmm_handleInt();
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 */
377 push_long(handle);
378 push_word(function);
379 push_long(0); /* This is the return address for the ABI, unused in this implementation */
380 pmm_handleInt();
381 addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
382 function = 2; /* pmmDeallocate */
383 push_long(addr);
384 push_word(function);
385 push_long(0); /* This is the return address for the ABI, unused in this implementation */
386 pmm_handleInt();
387 DEBUG_PRINTF_PMM
388 ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
389 __func__, M.x86.R_DX, M.x86.R_AX);
390 handle = 0xdeadbeef;
391 function = 1; /* pmmFind */
392 push_long(handle);
393 push_word(function);
394 push_long(0); /* This is the return address for the ABI, unused in this implementation */
395 pmm_handleInt();
396 addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
397 function = 2; /* pmmDeallocate */
398 push_long(addr);
399 push_word(function);
400 push_long(0); /* This is the return address for the ABI, unused in this implementation */
401 pmm_handleInt();
402 DEBUG_PRINTF_PMM
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 */
407 handle = 0xdeadbeef;
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() */
411 push_word(flags);
412 push_long(handle);
413 push_long(length);
414 push_word(function);
415 push_long(0); /* This is the return address for the ABI, unused in this implementation */
416 pmm_handleInt();
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__,
420 length);
421 function = 0; /* pmmAllocate */
422 flags = 0x1; /* conventional memory, aligned */
423 /* setup stack for call to pmm_handleInt() */
424 push_word(flags);
425 push_long(handle);
426 push_long(length);
427 push_word(function);
428 push_long(0); /* This is the return address for the ABI, unused in this implementation */
429 pmm_handleInt();
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 */
434 length = 1;
435 handle = 0xf00d4b0b;
436 flags = 0x1; /* conventional memory, aligned */
437 /* setup stack for call to pmm_handleInt() */
438 push_word(flags);
439 push_long(handle);
440 push_long(length);
441 push_word(function);
442 push_long(0); /* This is the return address for the ABI, unused in this implementation */
443 pmm_handleInt();
444 /* this should fail, so 0x0 should be returned */
445 addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
446 DEBUG_PRINTF_PMM
447 ("%s: allocated memory at: %04x:%04x expected: 0000:0000\n",
448 __func__, M.x86.R_DX, M.x86.R_AX);
449 handle = 0xdeadbeef;
450 function = 1; /* pmmFind */
451 push_long(handle);
452 push_word(function);
453 push_long(0); /* This is the return address for the ABI, unused in this implementation */
454 pmm_handleInt();
455 addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
456 function = 2; /* pmmDeallocate */
457 push_long(addr);
458 push_word(function);
459 push_long(0); /* This is the return address for the ABI, unused in this implementation */
460 pmm_handleInt();
461 DEBUG_PRINTF_PMM
462 ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
463 __func__, M.x86.R_DX, M.x86.R_AX);
464}