summaryrefslogtreecommitdiff
path: root/src/devices/oprom/yabel/pmm.c
blob: ad4dc6834c4c403a4bb3df0c036464bea76c096d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
/****************************************************************************
 * YABEL BIOS Emulator
 *
 * This program and the accompanying materials
 * are made available under the terms of the BSD License
 * which accompanies this distribution, and is available at
 * http://www.opensource.org/licenses/bsd-license.php
 *
 * Copyright (c) 2008 Pattrick Hueper <phueper@hueper.net>
 ****************************************************************************/

#include <x86emu/x86emu.h>
#include "../x86emu/prim_ops.h"
#include <string.h>

#include "biosemu.h"
#include "pmm.h"
#include "debug.h"
#include "device.h"

/* this struct is used to remember which PMM spaces
 * have been assigned. MAX_PMM_AREAS defines how many 
 * PMM areas we can assign. 
 * All areas are assigned in PMM_CONV_SEGMENT
 */
typedef struct {
	u32 handle;		/* handle that is returned to PMM caller */
	u32 offset;		/* in PMM_CONV_SEGMENT */
	u32 length;		/* length of this area */
} pmm_allocation_t;

#define MAX_PMM_AREAS 10

/* array to store the above structs */
static pmm_allocation_t pmm_allocation_array[MAX_PMM_AREAS];

/* index into pmm_allocation_array */
static u32 curr_pmm_allocation_index = 0;

/* This function is used to setup the PMM struct in virtual memory 
 * at a certain offset, the length of the PMM struct is returned */
u8 pmm_setup(u16 segment, u16 offset)
{
	/* setup the PMM structure */
	pmm_information_t *pis =
	    (pmm_information_t *) (M.mem_base + (((u32) segment) << 4) +
				   offset);
	memset(pis, 0, sizeof(pmm_information_t));
	/* set signature to $PMM */
	pis->signature[0] = '$';
	pis->signature[1] = 'P';
	pis->signature[2] = 'M';
	pis->signature[3] = 'M';
	/* revision as specified */
	pis->struct_rev = 0x01;
	/* internal length, excluding code */
	pis->length = ((void *)&(pis->code) - (void *)&(pis->signature));
	/* the code to be executed, pointed to by entry_point_offset */
	pis->code[0] = 0xCD;	/* INT */
	pis->code[1] = PMM_INT_NUM;	/* my selfdefined PMM INT number */
	pis->code[2] = 0xCB;	/* RETF */
	/* set the entry_point_offset, it should point to pis->code, segment is the segment of
	 * this struct. Since pis->length is the length of the struct excluding code, offset+pis->length
	 * points to the code... it's that simple ;-)
	 */
	out32le(&(pis->entry_point_offset),
		(u32) segment << 16 | (u32) (offset + pis->length));
	/* checksum calculation */
	u8 i;
	u8 checksum = 0;
	for (i = 0; i < pis->length; i++) {
		checksum += *(((u8 *) pis) + i);
	}
	pis->checksum = ((u8) 0) - checksum;
	CHECK_DBG(DEBUG_PMM) {
		DEBUG_PRINTF_PMM("PMM Structure:\n");
		dump((void *)pis, sizeof(pmm_information_t));
	}
	return sizeof(pmm_information_t);
}

/* handle the selfdefined interrupt, this is executed, when the PMM Entry Point 
 * is executed, it must handle all PMM requests
 */
void pmm_handleInt()
{
	u32 rval = 0;
	u16 function, flags;
	u32 handle, length;
	u32 i, j;
	u32 buffer;
	/* !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	 * according to the PMM Spec "the flags and all registers, except DX and AX
	 * are preserved across calls to PMM"
	 * so we save M.x86 and in :exit label we restore it, however, this means that no
	 * returns must be used in this function, any exit must use goto exit!
	 * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
	 */
	X86EMU_regs backup_regs = M.x86;
	pop_long();		/* pop the return address, this is already saved in INT handler, we don't need
				   to remember this. */
	function = pop_word();
	switch (function) {
	case 0:
		/* function pmmAllocate */
		length = pop_long();
		length *= 16;	/* length is passed in "paragraphs" of 16 bytes each */
		handle = pop_long();
		flags = pop_word();
		DEBUG_PRINTF_PMM
		    ("%s: pmmAllocate: Length: %x, Handle: %x, Flags: %x\n",
		     __func__, length, handle, flags);
		if ((flags & 0x1) != 0) {
			/* request to allocate in  conventional memory */
			if (curr_pmm_allocation_index >= MAX_PMM_AREAS) {
				printf
				    ("%s: pmmAllocate: Maximum Number of allocatable areas reached (%d), cannot allocate more memory!\n",
				     __func__, MAX_PMM_AREAS);
				rval = 0;
				goto exit;
			}
			/* some ROMs seem to be confused by offset 0, so lets start at 0x100 */
			u32 next_offset = 0x100;
			pmm_allocation_t *pmm_alloc =
			    &(pmm_allocation_array[curr_pmm_allocation_index]);
			if (curr_pmm_allocation_index != 0) {
				/* we have already allocated... get the new next_offset
				 * from the previous pmm_allocation_t */
				next_offset =
				    pmm_allocation_array
				    [curr_pmm_allocation_index - 1].offset +
				    pmm_allocation_array
				    [curr_pmm_allocation_index - 1].length;
			}
			DEBUG_PRINTF_PMM("%s: next_offset: 0x%x\n",
					 __func__, next_offset);
			if (length == 0) {
				/* largest possible block size requested, we have on segment
				 * to allocate, so largest possible is segment size (0xFFFF) 
				 * minus next_offset
				 */
				rval = 0xFFFF - next_offset;
				goto exit;
			}
			u32 align = 0;
			if (((flags & 0x4) != 0) && (length > 0)) {
				/* align to least significant bit set in length param */
				u8 lsb = 0;
				while (((length >> lsb) & 0x1) == 0) {
					lsb++;
				}
				align = 1 << lsb;
			}
			/* always align at least to paragraph (16byte) boundary 
			 * hm... since the length is always in paragraphs, we cannot
			 * align outside of paragraphs anyway... so this check might
			 * be unnecessary...*/
			if (align < 0x10) {
				align = 0x10;
			}
			DEBUG_PRINTF_PMM("%s: align: 0x%x\n", __func__,
					 align);
			if ((next_offset & (align - 1)) != 0) {
				/* not yet aligned... align! */
				next_offset += align;
				next_offset &= ~(align - 1);
			}
			if ((next_offset + length) > 0xFFFF) {
				rval = 0;
				printf
				    ("%s: pmmAllocate: Not enough memory available for allocation!\n",
				     __func__);
				goto exit;
			}
			curr_pmm_allocation_index++;
			/* remember the values in pmm_allocation_array */
			pmm_alloc->handle = handle;
			pmm_alloc->offset = next_offset;
			pmm_alloc->length = length;
			/* return the 32bit "physical" address, i.e. combination of segment and offset */
			rval = ((u32) (PMM_CONV_SEGMENT << 16)) | next_offset;
			DEBUG_PRINTF_PMM
			    ("%s: pmmAllocate: allocated memory at %x\n",
			     __func__, rval);
		} else {
			rval = 0;
			printf
			    ("%s: pmmAllocate: allocation in extended memory not supported!\n",
			     __func__);
		}
		goto exit;
	case 1:
		/* function pmmFind */
		handle = pop_long();	/* the handle to lookup */
		DEBUG_PRINTF_PMM("%s: pmmFind: Handle: %x\n", __func__,
				 handle);
		i = 0;
		for (i = 0; i < curr_pmm_allocation_index; i++) {
			if (pmm_allocation_array[i].handle == handle) {
				DEBUG_PRINTF_PMM
				    ("%s: pmmFind: found allocated memory at %x\n",
				     __func__, rval);
				/* return the 32bit "physical" address, i.e. combination of segment and offset */
				rval =
				    ((u32) (PMM_CONV_SEGMENT << 16)) |
				    pmm_allocation_array[i].offset;
			}
		}
		if (rval == 0) {
			DEBUG_PRINTF_PMM
			    ("%s: pmmFind: handle (%x) not found!\n",
			     __func__, handle);
		}
		goto exit;
	case 2:
		/* function pmmDeallocate */
		buffer = pop_long();
		/* since argument is the address of the PMM block (including the segment,
		 * we need to remove the segment to get the offset
		 */
		buffer = buffer ^ ((u32) PMM_CONV_SEGMENT << 16);
		DEBUG_PRINTF_PMM("%s: pmmDeallocate: PMM segment offset: %x\n",
				 __func__, buffer);
		i = 0;
		/* rval = 0 means we deallocated the buffer, so set it to 1 in case we dont find it and
		 * thus cannot deallocate
		 */
		rval = 1;
		for (i = 0; i < curr_pmm_allocation_index; i++) {
			DEBUG_PRINTF_PMM("%d: %x\n", i,
					 pmm_allocation_array[i].handle);
			if (pmm_allocation_array[i].offset == buffer) {
				/* we found the requested buffer, rval = 0 */
				rval = 0;
				DEBUG_PRINTF_PMM
				    ("%s: pmmDeallocate: found allocated memory at index: %d\n",
				     __func__, i);
				/* copy the remaining elements in pmm_allocation_array one position up */
				j = i;
				for (; j < curr_pmm_allocation_index; j++) {
					pmm_allocation_array[j] =
					    pmm_allocation_array[j + 1];
				}
				/* move curr_pmm_allocation_index one up, too */
				curr_pmm_allocation_index--;
				/* finally clean last element */
				pmm_allocation_array[curr_pmm_allocation_index].
				    handle = 0;
				pmm_allocation_array[curr_pmm_allocation_index].
				    offset = 0;
				pmm_allocation_array[curr_pmm_allocation_index].
				    length = 0;
				break;
			}
		}
		if (rval != 0) {
			DEBUG_PRINTF_PMM
			    ("%s: pmmDeallocate: offset (%x) not found, cannot deallocate!\n",
			     __func__, buffer);
		}
		goto exit;
	default:
		/* invalid/unimplemented function */
		printf("%s: invalid PMM function (0x%04x) called!\n",
		       __func__, function);
		/* PMM spec says if function is invalid, return 0xFFFFFFFF */
		rval = 0xFFFFFFFF;
		goto exit;
	}
      exit:
	/* exit handler of this function, restore registers, put return value in DX:AX */
	M.x86 = backup_regs;
	M.x86.R_DX = (u16) ((rval >> 16) & 0xFFFF);
	M.x86.R_AX = (u16) (rval & 0xFFFF);
	CHECK_DBG(DEBUG_PMM) {
		DEBUG_PRINTF_PMM("%s: dump of pmm_allocation_array:\n",
				 __func__);
		for (i = 0; i < MAX_PMM_AREAS; i++) {
			DEBUG_PRINTF_PMM
			    ("%d:\n\thandle: %x\n\toffset: %x\n\tlength: %x\n",
			     i, pmm_allocation_array[i].handle,
			     pmm_allocation_array[i].offset,
			     pmm_allocation_array[i].length);
		}
	}
	return;
}

/* This function tests the pmm_handleInt() function above. */
void pmm_test(void)
{
	u32 handle, length, addr;
	u16 function, flags;
	/*-------------------- Test simple allocation/find/deallocation ----------------------------- */
	function = 0;		/* pmmAllocate */
	handle = 0xdeadbeef;
	length = 16;		/* in 16byte paragraphs, so we allocate 256 bytes... */
	flags = 0x1;		/* conventional memory, unaligned */
	/* setup stack for call to pmm_handleInt() */
	push_word(flags);
	push_long(handle);
	push_long(length);
	push_word(function);
	push_long(0);		/* This is the return address for the ABI, unused in this implementation */
	pmm_handleInt();
	addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
	DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__,
			 M.x86.R_DX, M.x86.R_AX);
	function = 1;		/* pmmFind */
	push_long(handle);
	push_word(function);
	push_long(0);		/* This is the return address for the ABI, unused in this implementation */
	pmm_handleInt();
	DEBUG_PRINTF_PMM("%s: found memory at: %04x:%04x (expected: %08x)\n",
			 __func__, M.x86.R_DX, M.x86.R_AX, addr);
	function = 2;		/* pmmDeallocate */
	push_long(addr);
	push_word(function);
	push_long(0);		/* This is the return address for the ABI, unused in this implementation */
	pmm_handleInt();
	DEBUG_PRINTF_PMM
	    ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
	     __func__, M.x86.R_DX, M.x86.R_AX);
	/*-------------------- Test aligned allocation/deallocation ----------------------------- */
	function = 0;		/* pmmAllocate */
	handle = 0xdeadbeef;
	length = 257;		/* in 16byte paragraphs, so we allocate 4KB + 16 bytes... */
	flags = 0x1;		/* conventional memory, unaligned */
	/* setup stack for call to pmm_handleInt() */
	push_word(flags);
	push_long(handle);
	push_long(length);
	push_word(function);
	push_long(0);		/* This is the return address for the ABI, unused in this implementation */
	pmm_handleInt();
	addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
	DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__,
			 M.x86.R_DX, M.x86.R_AX);
	function = 0;		/* pmmAllocate */
	handle = 0xf00d4b0b;
	length = 128;		/* in 16byte paragraphs, so we allocate 2KB... */
	flags = 0x5;		/* conventional memory, aligned */
	/* setup stack for call to pmm_handleInt() */
	push_word(flags);
	push_long(handle);
	push_long(length);
	push_word(function);
	push_long(0);		/* This is the return address for the ABI, unused in this implementation */
	pmm_handleInt();
	/* the address should be aligned to 0x800, so probably it is at offset 0x1800... */
	addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
	DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__,
			 M.x86.R_DX, M.x86.R_AX);
	function = 1;		/* pmmFind */
	push_long(handle);
	push_word(function);
	push_long(0);		/* This is the return address for the ABI, unused in this implementation */
	pmm_handleInt();
	addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
	function = 2;		/* pmmDeallocate */
	push_long(addr);
	push_word(function);
	push_long(0);		/* This is the return address for the ABI, unused in this implementation */
	pmm_handleInt();
	DEBUG_PRINTF_PMM
	    ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
	     __func__, M.x86.R_DX, M.x86.R_AX);
	handle = 0xdeadbeef;
	function = 1;		/* pmmFind */
	push_long(handle);
	push_word(function);
	push_long(0);		/* This is the return address for the ABI, unused in this implementation */
	pmm_handleInt();
	addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
	function = 2;		/* pmmDeallocate */
	push_long(addr);
	push_word(function);
	push_long(0);		/* This is the return address for the ABI, unused in this implementation */
	pmm_handleInt();
	DEBUG_PRINTF_PMM
	    ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
	     __func__, M.x86.R_DX, M.x86.R_AX);
	/*-------------------- Test out of memory allocation ----------------------------- */
	function = 0;		/* pmmAllocate */
	handle = 0xdeadbeef;
	length = 0;		/* length zero means, give me the largest possible block */
	flags = 0x1;		/* conventional memory, unaligned */
	/* setup stack for call to pmm_handleInt() */
	push_word(flags);
	push_long(handle);
	push_long(length);
	push_word(function);
	push_long(0);		/* This is the return address for the ABI, unused in this implementation */
	pmm_handleInt();
	length = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
	length /= 16;		/* length in paragraphs */
	DEBUG_PRINTF_PMM("%s: largest possible length: %08x\n", __func__,
			 length);
	function = 0;		/* pmmAllocate */
	flags = 0x1;		/* conventional memory, aligned */
	/* setup stack for call to pmm_handleInt() */
	push_word(flags);
	push_long(handle);
	push_long(length);
	push_word(function);
	push_long(0);		/* This is the return address for the ABI, unused in this implementation */
	pmm_handleInt();
	addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
	DEBUG_PRINTF_PMM("%s: allocated memory at: %04x:%04x\n", __func__,
			 M.x86.R_DX, M.x86.R_AX);
	function = 0;		/* pmmAllocate */
	length = 1;
	handle = 0xf00d4b0b;
	flags = 0x1;		/* conventional memory, aligned */
	/* setup stack for call to pmm_handleInt() */
	push_word(flags);
	push_long(handle);
	push_long(length);
	push_word(function);
	push_long(0);		/* This is the return address for the ABI, unused in this implementation */
	pmm_handleInt();
	/* this should fail, so 0x0 should be returned */
	addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
	DEBUG_PRINTF_PMM
	    ("%s: allocated memory at: %04x:%04x expected: 0000:0000\n",
	     __func__, M.x86.R_DX, M.x86.R_AX);
	handle = 0xdeadbeef;
	function = 1;		/* pmmFind */
	push_long(handle);
	push_word(function);
	push_long(0);		/* This is the return address for the ABI, unused in this implementation */
	pmm_handleInt();
	addr = ((u32) M.x86.R_DX << 16) | M.x86.R_AX;
	function = 2;		/* pmmDeallocate */
	push_long(addr);
	push_word(function);
	push_long(0);		/* This is the return address for the ABI, unused in this implementation */
	pmm_handleInt();
	DEBUG_PRINTF_PMM
	    ("%s: freed memory rval: %04x:%04x (expected: 0000:0000)\n",
	     __func__, M.x86.R_DX, M.x86.R_AX);
}