1 // Post memory manager (PMM) calls
2 //
3 // Copyright (C) 2009-2013  Kevin O'Connor <kevin@koconnor.net>
4 //
5 // This file may be distributed under the terms of the GNU LGPLv3 license.
6 
7 #include "biosvar.h" // FUNC16
8 #include "config.h" // CONFIG_*
9 #include "malloc.h" // _malloc
10 #include "output.h" // dprintf
11 #include "e820map.h" // struct e820entry
12 #include "std/pmm.h" // PMM_SIGNATURE
13 #include "string.h" // checksum
14 #include "util.h" // pmm_init
15 #include "x86.h" // __ffs
16 
17 extern struct pmmheader PMMHEADER;
18 
19 #if CONFIG_PMM
20 struct pmmheader PMMHEADER __aligned(16) VARFSEG = {
21     .signature = PMM_SIGNATURE,
22     .version = 0x01,
23     .length = sizeof(PMMHEADER),
24 };
25 #endif
26 
27 // PMM - allocate
28 static u32
handle_pmm00(u16 * args)29 handle_pmm00(u16 *args)
30 {
31     u32 length = *(u32*)&args[1], handle = *(u32*)&args[3];
32     u16 flags = args[5];
33     dprintf(3, "pmm00: length=%x handle=%x flags=%x\n"
34             , length, handle, flags);
35     struct zone_s *lowzone = &ZoneTmpLow, *highzone = &ZoneTmpHigh;
36     if (flags & 8) {
37         // Permanent memory request.
38         lowzone = &ZoneLow;
39         highzone = &ZoneHigh;
40     }
41     if (!length) {
42         // Memory size request
43         switch (flags & 3) {
44         default:
45         case 0:
46             return 0;
47         case 1:
48             return malloc_getspace(lowzone);
49         case 2:
50             return malloc_getspace(highzone);
51         case 3: {
52             u32 spacelow = malloc_getspace(lowzone);
53             u32 spacehigh = malloc_getspace(highzone);
54             if (spacelow > spacehigh)
55                 return spacelow;
56             return spacehigh;
57         }
58         }
59     }
60     u32 size = length * 16;
61     if ((s32)size <= 0)
62         return 0;
63     u32 align = MALLOC_MIN_ALIGN;
64     if (flags & 4) {
65         align = 1<<__ffs(size);
66         if (align < MALLOC_MIN_ALIGN)
67             align = MALLOC_MIN_ALIGN;
68     }
69     u32 data;
70     switch (flags & 3) {
71     default:
72     case 0:
73         return 0;
74     case 1:
75         data = malloc_palloc(lowzone, size, align);
76         break;
77     case 2:
78         data = malloc_palloc(highzone, size, align);
79         if (!data && (flags & 8)) {
80             /*
81              * We are out of meory.  So go allocate from the (big)
82              * ZoneTmpHigh instead and reserve the block in the e820
83              * map so the OS will not override it.  That way we can
84              * handle big permanent allocations without needing a big
85              * ZoneHigh.
86              */
87             data = malloc_palloc(&ZoneTmpHigh, size, align);
88             if (data)
89                 e820_add(data, size, E820_RESERVED);
90         }
91         break;
92     case 3: {
93         data = malloc_palloc(lowzone, size, align);
94         if (!data)
95             data = malloc_palloc(highzone, size, align);
96     }
97     }
98     if (data && handle != MALLOC_DEFAULT_HANDLE)
99         malloc_sethandle(data, handle);
100     return data;
101 }
102 
103 // PMM - find
104 static u32
handle_pmm01(u16 * args)105 handle_pmm01(u16 *args)
106 {
107     u32 handle = *(u32*)&args[1];
108     dprintf(3, "pmm01: handle=%x\n", handle);
109     if (handle == MALLOC_DEFAULT_HANDLE)
110         return 0;
111     return malloc_findhandle(handle);
112 }
113 
114 // PMM - deallocate
115 static u32
handle_pmm02(u16 * args)116 handle_pmm02(u16 *args)
117 {
118     u32 buffer = *(u32*)&args[1];
119     dprintf(3, "pmm02: buffer=%x\n", buffer);
120     int ret = malloc_pfree(buffer);
121     if (ret)
122         // Error
123         return 1;
124     return 0;
125 }
126 
127 static u32
handle_pmmXX(u16 * args)128 handle_pmmXX(u16 *args)
129 {
130     return PMM_FUNCTION_NOT_SUPPORTED;
131 }
132 
133 u32 VISIBLE32INIT
handle_pmm(u16 * args)134 handle_pmm(u16 *args)
135 {
136     ASSERT32FLAT();
137     if (! CONFIG_PMM)
138         return PMM_FUNCTION_NOT_SUPPORTED;
139 
140     u16 arg1 = args[0];
141     dprintf(DEBUG_HDL_pmm, "pmm call arg1=%x\n", arg1);
142 
143     u32 ret;
144     switch (arg1) {
145     case 0x00: ret = handle_pmm00(args); break;
146     case 0x01: ret = handle_pmm01(args); break;
147     case 0x02: ret = handle_pmm02(args); break;
148     default:   ret = handle_pmmXX(args); break;
149     }
150 
151     return ret;
152 }
153 
154 void
pmm_init(void)155 pmm_init(void)
156 {
157     if (! CONFIG_PMM)
158         return;
159 
160     dprintf(3, "init PMM\n");
161 
162     PMMHEADER.entry = FUNC16(entry_pmm);
163     PMMHEADER.checksum -= checksum(&PMMHEADER, sizeof(PMMHEADER));
164 }
165 
166 void
pmm_prepboot(void)167 pmm_prepboot(void)
168 {
169     if (! CONFIG_PMM)
170         return;
171 
172     dprintf(3, "finalize PMM\n");
173 
174     PMMHEADER.signature = 0;
175     PMMHEADER.entry.segoff = 0;
176 }
177