xref: /aosp_15_r20/external/coreboot/src/device/oprom/yabel/pmm.c (revision b9411a12aaaa7e1e6a6fb7c5e057f44ee179a49c)
1 /****************************************************************************
2  * YABEL BIOS Emulator
3  *
4  * Copyright (c) 2008 Pattrick Hueper <[email protected]>
5  *
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  *
32  ****************************************************************************/
33 
34 #include <x86emu/x86emu.h>
35 #include "../x86emu/prim_ops.h"
36 #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
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
47  */
48 typedef 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 */
57 static pmm_allocation_t pmm_allocation_array[MAX_PMM_AREAS];
58 
59 /* index into pmm_allocation_array */
60 static u32 curr_pmm_allocation_index = 0;
61 
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 */
pmm_setup(u16 segment,u16 offset)64 u8 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 
104 /* handle the selfdefined interrupt, this is executed, when the PMM Entry Point
105  * is executed, it must handle all PMM requests
106  */
pmm_handleInt(void)107 void pmm_handleInt(void)
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
161 				 * to allocate, so largest possible is segment size (0xFFFF)
162 				 * 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 			}
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...*/
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;
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
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 	}
292 exit:
293 	/* 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. */
pmm_test(void)312 void 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 }
465