blob: d6c528d1dc072a98eba2af17b06b7cf8ff089845 [file] [log] [blame]
Stefan Reinauer38cd29e2009-08-11 21:28:25 +00001/****************************************************************************
2 * YABEL BIOS Emulator
3 *
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
8 *
9 * Copyright (c) 2008 Pattrick Hueper <phueper@hueper.net>
10 ****************************************************************************/
11
12#include <x86emu/x86emu.h>
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000013#include "../x86emu/prim_ops.h"
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000014#include <string.h>
15
16#include "biosemu.h"
17#include "pmm.h"
18#include "debug.h"
19#include "device.h"
20
21/* this struct is used to remember which PMM spaces
Stefan Reinauer14e22772010-04-27 06:56:47 +000022 * have been assigned. MAX_PMM_AREAS defines how many
23 * PMM areas we can assign.
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000024 * All areas are assigned in PMM_CONV_SEGMENT
25 */
26typedef struct {
27 u32 handle; /* handle that is returned to PMM caller */
28 u32 offset; /* in PMM_CONV_SEGMENT */
29 u32 length; /* length of this area */
30} pmm_allocation_t;
31
32#define MAX_PMM_AREAS 10
33
34/* array to store the above structs */
35static pmm_allocation_t pmm_allocation_array[MAX_PMM_AREAS];
36
37/* index into pmm_allocation_array */
38static u32 curr_pmm_allocation_index = 0;
39
Stefan Reinauer14e22772010-04-27 06:56:47 +000040/* This function is used to setup the PMM struct in virtual memory
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000041 * at a certain offset, the length of the PMM struct is returned */
42u8 pmm_setup(u16 segment, u16 offset)
43{
44 /* setup the PMM structure */
45 pmm_information_t *pis =
46 (pmm_information_t *) (M.mem_base + (((u32) segment) << 4) +
47 offset);
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 ;-)
65 */
66 out32le(&(pis->entry_point_offset),
67 (u32) segment << 16 | (u32) (offset + pis->length));
68 /* checksum calculation */
69 u8 i;
70 u8 checksum = 0;
71 for (i = 0; i < pis->length; i++) {
72 checksum += *(((u8 *) pis) + i);
73 }
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));
78 }
79 return sizeof(pmm_information_t);
80}
81
Stefan Reinauer14e22772010-04-27 06:56:47 +000082/* handle the selfdefined interrupt, this is executed, when the PMM Entry Point
Stefan Reinauer38cd29e2009-08-11 21:28:25 +000083 * is executed, it must handle all PMM requests
84 */
85void pmm_handleInt()
86{
87 u32 rval = 0;
88 u16 function, flags;
89 u32 handle, length;
90 u32 i, j;
91 u32 buffer;
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 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
98 */
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
101 to remember this. */
102 function = pop_word();
103 switch (function) {
104 case 0:
105 /* function pmmAllocate */
106 length = pop_long();
107 length *= 16; /* length is passed in "paragraphs" of 16 bytes each */
108 handle = pop_long();
109 flags = pop_word();
110 DEBUG_PRINTF_PMM
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) {
116 printf
117 ("%s: pmmAllocate: Maximum Number of allocatable areas reached (%d), cannot allocate more memory!\n",
118 __func__, MAX_PMM_AREAS);
119 rval = 0;
120 goto exit;
121 }
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 */
129 next_offset =
130 pmm_allocation_array
131 [curr_pmm_allocation_index - 1].offset +
132 pmm_allocation_array
133 [curr_pmm_allocation_index - 1].length;
134 }
135 DEBUG_PRINTF_PMM("%s: next_offset: 0x%x\n",
136 __func__, next_offset);
137 if (length == 0) {
138 /* largest possible block size requested, we have on segment
Stefan Reinauer14e22772010-04-27 06:56:47 +0000139 * to allocate, so largest possible is segment size (0xFFFF)
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000140 * minus next_offset
141 */
142 rval = 0xFFFF - next_offset;
143 goto exit;
144 }
145 u32 align = 0;
146 if (((flags & 0x4) != 0) && (length > 0)) {
147 /* align to least significant bit set in length param */
148 u8 lsb = 0;
149 while (((length >> lsb) & 0x1) == 0) {
150 lsb++;
151 }
152 align = 1 << lsb;
153 }
Stefan Reinauer14e22772010-04-27 06:56:47 +0000154 /* always align at least to paragraph (16byte) boundary
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000155 * hm... since the length is always in paragraphs, we cannot
156 * align outside of paragraphs anyway... so this check might
157 * be unnecessary...*/
158 if (align < 0x10) {
159 align = 0x10;
160 }
161 DEBUG_PRINTF_PMM("%s: align: 0x%x\n", __func__,
162 align);
163 if ((next_offset & (align - 1)) != 0) {
164 /* not yet aligned... align! */
165 next_offset += align;
166 next_offset &= ~(align - 1);
167 }
168 if ((next_offset + length) > 0xFFFF) {
169 rval = 0;
170 printf
171 ("%s: pmmAllocate: Not enough memory available for allocation!\n",
172 __func__);
173 goto exit;
174 }
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;
182 DEBUG_PRINTF_PMM
183 ("%s: pmmAllocate: allocated memory at %x\n",
184 __func__, rval);
185 } else {
186 rval = 0;
187 printf
188 ("%s: pmmAllocate: allocation in extended memory not supported!\n",
189 __func__);
190 }
191 goto exit;
192 case 1:
193 /* function pmmFind */
194 handle = pop_long(); /* the handle to lookup */
195 DEBUG_PRINTF_PMM("%s: pmmFind: Handle: %x\n", __func__,
196 handle);
197 i = 0;
198 for (i = 0; i < curr_pmm_allocation_index; i++) {
199 if (pmm_allocation_array[i].handle == handle) {
200 DEBUG_PRINTF_PMM
201 ("%s: pmmFind: found allocated memory at %x\n",
202 __func__, rval);
203 /* return the 32bit "physical" address, i.e. combination of segment and offset */
204 rval =
205 ((u32) (PMM_CONV_SEGMENT << 16)) |
206 pmm_allocation_array[i].offset;
207 }
208 }
209 if (rval == 0) {
210 DEBUG_PRINTF_PMM
211 ("%s: pmmFind: handle (%x) not found!\n",
212 __func__, handle);
213 }
214 goto exit;
215 case 2:
216 /* function pmmDeallocate */
217 buffer = pop_long();
218 /* since argument is the address of the PMM block (including the segment,
219 * we need to remove the segment to get the offset
220 */
221 buffer = buffer ^ ((u32) PMM_CONV_SEGMENT << 16);
222 DEBUG_PRINTF_PMM("%s: pmmDeallocate: PMM segment offset: %x\n",
223 __func__, buffer);
224 i = 0;
Martin Roth63373ed2013-07-08 16:24:19 -0600225 /* 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 +0000226 * thus cannot deallocate
227 */
228 rval = 1;
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 */
234 rval = 0;
235 DEBUG_PRINTF_PMM
236 ("%s: pmmDeallocate: found allocated memory at index: %d\n",
237 __func__, i);
238 /* copy the remaining elements in pmm_allocation_array one position up */
239 j = i;
240 for (; j < curr_pmm_allocation_index; j++) {
241 pmm_allocation_array[j] =
242 pmm_allocation_array[j + 1];
243 }
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].
248 handle = 0;
249 pmm_allocation_array[curr_pmm_allocation_index].
250 offset = 0;
251 pmm_allocation_array[curr_pmm_allocation_index].
252 length = 0;
253 break;
254 }
255 }
256 if (rval != 0) {
257 DEBUG_PRINTF_PMM
258 ("%s: pmmDeallocate: offset (%x) not found, cannot deallocate!\n",
259 __func__, buffer);
260 }
261 goto exit;
262 default:
263 /* invalid/unimplemented function */
264 printf("%s: invalid PMM function (0x%04x) called!\n",
265 __func__, function);
266 /* PMM spec says if function is invalid, return 0xFFFFFFFF */
267 rval = 0xFFFFFFFF;
268 goto exit;
269 }
Patrick Georgic5fc7db2012-03-07 15:55:47 +0100270exit:
Stefan Reinauer38cd29e2009-08-11 21:28:25 +0000271 /* exit handler of this function, restore registers, put return value in DX:AX */
272 M.x86 = backup_regs;
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",
277 __func__);
278 for (i = 0; i < MAX_PMM_AREAS; i++) {
279 DEBUG_PRINTF_PMM
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);
284 }
285 }
286 return;
287}
288
289/* This function tests the pmm_handleInt() function above. */
290void pmm_test(void)
291{
292 u32 handle, length, addr;
293 u16 function, flags;
294 /*-------------------- Test simple allocation/find/deallocation ----------------------------- */
295 function = 0; /* pmmAllocate */
296 handle = 0xdeadbeef;
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() */
300 push_word(flags);
301 push_long(handle);
302 push_long(length);
303 push_word(function);
304 push_long(0); /* This is the return address for the ABI, unused in this implementation */
305 pmm_handleInt();
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 */
310 push_long(handle);
311 push_word(function);
312 push_long(0); /* This is the return address for the ABI, unused in this implementation */
313 pmm_handleInt();
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 */
317 push_long(addr);
318 push_word(function);
319 push_long(0); /* This is the return address for the ABI, unused in this implementation */
320 pmm_handleInt();
321 DEBUG_PRINTF_PMM
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 */
326 handle = 0xdeadbeef;
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() */
330 push_word(flags);
331 push_long(handle);
332 push_long(length);
333 push_word(function);
334 push_long(0); /* This is the return address for the ABI, unused in this implementation */
335 pmm_handleInt();
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 */
340 handle = 0xf00d4b0b;
341 length = 128; /* in 16byte paragraphs, so we allocate 2KB... */
342 flags = 0x5; /* conventional memory, aligned */
343 /* setup stack for call to pmm_handleInt() */
344 push_word(flags);
345 push_long(handle);
346 push_long(length);
347 push_word(function);
348 push_long(0); /* This is the return address for the ABI, unused in this implementation */
349 pmm_handleInt();
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 */
355 push_long(handle);
356 push_word(function);
357 push_long(0); /* This is the return address for the ABI, unused in this implementation */
358 pmm_handleInt();
359 addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
360 function = 2; /* pmmDeallocate */
361 push_long(addr);
362 push_word(function);
363 push_long(0); /* This is the return address for the ABI, unused in this implementation */
364 pmm_handleInt();
365 DEBUG_PRINTF_PMM
366 ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
367 __func__, M.x86.R_DX, M.x86.R_AX);
368 handle = 0xdeadbeef;
369 function = 1; /* pmmFind */
370 push_long(handle);
371 push_word(function);
372 push_long(0); /* This is the return address for the ABI, unused in this implementation */
373 pmm_handleInt();
374 addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
375 function = 2; /* pmmDeallocate */
376 push_long(addr);
377 push_word(function);
378 push_long(0); /* This is the return address for the ABI, unused in this implementation */
379 pmm_handleInt();
380 DEBUG_PRINTF_PMM
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 */
385 handle = 0xdeadbeef;
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() */
389 push_word(flags);
390 push_long(handle);
391 push_long(length);
392 push_word(function);
393 push_long(0); /* This is the return address for the ABI, unused in this implementation */
394 pmm_handleInt();
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__,
398 length);
399 function = 0; /* pmmAllocate */
400 flags = 0x1; /* conventional memory, aligned */
401 /* setup stack for call to pmm_handleInt() */
402 push_word(flags);
403 push_long(handle);
404 push_long(length);
405 push_word(function);
406 push_long(0); /* This is the return address for the ABI, unused in this implementation */
407 pmm_handleInt();
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 */
412 length = 1;
413 handle = 0xf00d4b0b;
414 flags = 0x1; /* conventional memory, aligned */
415 /* setup stack for call to pmm_handleInt() */
416 push_word(flags);
417 push_long(handle);
418 push_long(length);
419 push_word(function);
420 push_long(0); /* This is the return address for the ABI, unused in this implementation */
421 pmm_handleInt();
422 /* this should fail, so 0x0 should be returned */
423 addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
424 DEBUG_PRINTF_PMM
425 ("%s: allocated memory at: %04x:%04x expected: 0000:0000\n",
426 __func__, M.x86.R_DX, M.x86.R_AX);
427 handle = 0xdeadbeef;
428 function = 1; /* pmmFind */
429 push_long(handle);
430 push_word(function);
431 push_long(0); /* This is the return address for the ABI, unused in this implementation */
432 pmm_handleInt();
433 addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
434 function = 2; /* pmmDeallocate */
435 push_long(addr);
436 push_word(function);
437 push_long(0); /* This is the return address for the ABI, unused in this implementation */
438 pmm_handleInt();
439 DEBUG_PRINTF_PMM
440 ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
441 __func__, M.x86.R_DX, M.x86.R_AX);
442}