Move C labels to start-of-line
[coreboot.git] / src / devices / oprom / yabel / pmm.c
1 /****************************************************************************
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>
13 #include "../x86emu/prim_ops.h"
14 #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
22  * have been assigned. MAX_PMM_AREAS defines how many
23  * PMM areas we can assign.
24  * All areas are assigned in PMM_CONV_SEGMENT
25  */
26 typedef 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 */
35 static pmm_allocation_t pmm_allocation_array[MAX_PMM_AREAS];
36
37 /* index into pmm_allocation_array */
38 static u32 curr_pmm_allocation_index = 0;
39
40 /* This function is used to setup the PMM struct in virtual memory
41  * at a certain offset, the length of the PMM struct is returned */
42 u8 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
82 /* handle the selfdefined interrupt, this is executed, when the PMM Entry Point
83  * is executed, it must handle all PMM requests
84  */
85 void 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
139                                  * to allocate, so largest possible is segment size (0xFFFF)
140                                  * 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                         }
154                         /* always align at least to paragraph (16byte) boundary
155                          * 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;
225                 /* rval = 0 means we deallocated the buffer, so set it to 1 in case we dont find it and
226                  * 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         }
270 exit:
271         /* 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. */
290 void 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 }