Branch data Line data Source code
1 : : // SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
2 : : /* Copyright 2017 IBM Corp. */
3 : :
4 : : #include <stdio.h>
5 : : #include <stdlib.h>
6 : : #include <stdint.h>
7 : : #include <string.h>
8 : : #include <stdarg.h>
9 : : #include <inttypes.h>
10 : :
11 : : #include <sys/mman.h> /* for mprotect() */
12 : :
13 : : #define pr_fmt(fmt) "MBOX-SERVER: " fmt
14 : : #include "skiboot.h"
15 : : #include "opal-api.h"
16 : :
17 : : #include "mbox-server.h"
18 : : #include "stubs.h"
19 : :
20 : : #define ERASE_GRANULE 0x100
21 : :
22 : : #define LPC_BLOCKS 256
23 : :
24 : : #define __unused __attribute__((unused))
25 : :
26 : : enum win_type {
27 : : WIN_CLOSED,
28 : : WIN_READ,
29 : : WIN_WRITE
30 : : };
31 : :
32 : : typedef void (*mbox_data_cb)(struct bmc_mbox_msg *msg, void *priv);
33 : : typedef void (*mbox_attn_cb)(uint8_t reg, void *priv);
34 : :
35 : : struct {
36 : : mbox_data_cb fn;
37 : : void *cb_data;
38 : : struct bmc_mbox_msg *msg;
39 : : mbox_attn_cb attn;
40 : : void *cb_attn;
41 : : } mbox_data;
42 : :
43 : : static struct {
44 : : int api;
45 : : bool reset;
46 : :
47 : : void *lpc_base;
48 : : size_t lpc_size;
49 : :
50 : : uint8_t attn_reg;
51 : :
52 : : uint32_t block_shift;
53 : : uint32_t erase_granule;
54 : :
55 : : uint16_t def_read_win; /* default window size in blocks */
56 : : uint16_t def_write_win;
57 : :
58 : : uint16_t max_read_win; /* max window size in blocks */
59 : : uint16_t max_write_win;
60 : :
61 : : enum win_type win_type;
62 : : uint32_t win_base;
63 : : uint32_t win_size;
64 : : bool win_dirty;
65 : : } server_state;
66 : :
67 : :
68 : 28670397 : static bool check_window(uint32_t pos, uint32_t size)
69 : : {
70 : : /* If size is zero then all is well */
71 : 28670397 : if (size == 0)
72 : 24 : return true;
73 : :
74 : 28670373 : if (server_state.api == 1) {
75 : : /*
76 : : * Can actually be stricter in v1 because pos is relative to
77 : : * flash not window
78 : : */
79 : 104244 : if (pos < server_state.win_base ||
80 : 104244 : pos + size > server_state.win_base + server_state.win_size) {
81 : 0 : fprintf(stderr, "pos: 0x%08x size: 0x%08x aren't in active window\n",
82 : : pos, size);
83 : 0 : fprintf(stderr, "window pos: 0x%08x window size: 0x%08x\n",
84 : : server_state.win_base, server_state.win_size);
85 : 0 : return false;
86 : : }
87 : : } else {
88 : 28566129 : if (pos + size > server_state.win_base + server_state.win_size)
89 : 0 : return false;
90 : : }
91 : 28670373 : return true;
92 : : }
93 : :
94 : : /* skiboot test stubs */
95 : : int64_t lpc_read(enum OpalLPCAddressType __unused addr_type, uint32_t addr,
96 : : uint32_t *data, uint32_t sz);
97 : 22434105 : int64_t lpc_read(enum OpalLPCAddressType __unused addr_type, uint32_t addr,
98 : : uint32_t *data, uint32_t sz)
99 : : {
100 : : /* Let it read from a write window... Spec says it ok! */
101 : 22434105 : if (!check_window(addr, sz) || server_state.win_type == WIN_CLOSED)
102 : 0 : return 1;
103 : :
104 : 22434105 : switch (sz) {
105 : 308 : case 1:
106 : 308 : *(uint8_t *)data = *(uint8_t *)(server_state.lpc_base + addr);
107 : 308 : break;
108 : 0 : case 2:
109 : 0 : *(uint16_t *)data = be16_to_cpu(*(uint16_t *)(server_state.lpc_base + addr));
110 : 0 : break;
111 : 22433797 : case 4:
112 : 22433797 : *(uint32_t *)data = be32_to_cpu(*(uint32_t *)(server_state.lpc_base + addr));
113 : 22433797 : break;
114 : 0 : default:
115 : 0 : prerror("Invalid data size %d\n", sz);
116 : 0 : return 1;
117 : : }
118 : 22434105 : return 0;
119 : : }
120 : :
121 : : int64_t lpc_write(enum OpalLPCAddressType __unused addr_type, uint32_t addr,
122 : : uint32_t data, uint32_t sz);
123 : 6236172 : int64_t lpc_write(enum OpalLPCAddressType __unused addr_type, uint32_t addr,
124 : : uint32_t data, uint32_t sz)
125 : : {
126 : 6236172 : if (!check_window(addr, sz) || server_state.win_type != WIN_WRITE)
127 : 0 : return 1;
128 : 6236172 : switch (sz) {
129 : 0 : case 1:
130 : 0 : *(uint8_t *)(server_state.lpc_base + addr) = data;
131 : 0 : break;
132 : 0 : case 2:
133 : 0 : *(uint16_t *)(server_state.lpc_base + addr) = cpu_to_be16(data);
134 : 0 : break;
135 : 6236172 : case 4:
136 : 6236172 : *(uint32_t *)(server_state.lpc_base + addr) = cpu_to_be32(data);
137 : 6236172 : break;
138 : 0 : default:
139 : 0 : prerror("Invalid data size %d\n", sz);
140 : 0 : return 1;
141 : : }
142 : 6236172 : return 0;
143 : : }
144 : :
145 : : int64_t lpc_fw_read(uint32_t off, void *buf, uint32_t len);
146 : 659 : int64_t lpc_fw_read(uint32_t off, void *buf, uint32_t len)
147 : : {
148 : : int rc;
149 : :
150 : 22434764 : while (len) {
151 : : uint32_t chunk;
152 : : uint32_t dat;
153 : :
154 : : /* XXX: make this read until it's aligned */
155 : 22434105 : if (len > 3 && !(off & 3)) {
156 : 22433797 : rc = lpc_read(OPAL_LPC_FW, off, &dat, 4);
157 : 22433797 : if (!rc) {
158 : : /*
159 : : * lpc_read swaps to CPU endian but it's not
160 : : * really a 32-bit value, so convert back.
161 : : */
162 : 22433797 : *(__be32 *)buf = cpu_to_be32(dat);
163 : : }
164 : 22433797 : chunk = 4;
165 : : } else {
166 : 308 : rc = lpc_read(OPAL_LPC_FW, off, &dat, 1);
167 : 308 : if (!rc)
168 : 308 : *(uint8_t *)buf = dat;
169 : 308 : chunk = 1;
170 : : }
171 : 22434105 : if (rc)
172 : 0 : return rc;
173 : :
174 : 22434105 : len -= chunk;
175 : 22434105 : off += chunk;
176 : 22434105 : buf += chunk;
177 : : }
178 : :
179 : 659 : return 0;
180 : : }
181 : :
182 : : int64_t lpc_fw_write(uint32_t off, const void *buf, uint32_t len);
183 : 96 : int64_t lpc_fw_write(uint32_t off, const void *buf, uint32_t len)
184 : : {
185 : : int rc;
186 : :
187 : 6236268 : while (len) {
188 : : uint32_t chunk;
189 : :
190 : 6236172 : if (len > 3 && !(off & 3)) {
191 : : /* endian swap: see lpc_window_write */
192 : 6236172 : uint32_t dat = be32_to_cpu(*(__be32 *)buf);
193 : :
194 : 6236172 : rc = lpc_write(OPAL_LPC_FW, off, dat, 4);
195 : 6236172 : chunk = 4;
196 : : } else {
197 : 0 : uint8_t dat = *(uint8_t *)buf;
198 : :
199 : 0 : rc = lpc_write(OPAL_LPC_FW, off, dat, 1);
200 : 0 : chunk = 1;
201 : : }
202 : 6236172 : if (rc)
203 : 0 : return rc;
204 : :
205 : 6236172 : len -= chunk;
206 : 6236172 : off += chunk;
207 : 6236172 : buf += chunk;
208 : : }
209 : :
210 : 96 : return 0;
211 : : }
212 : :
213 : 4 : int bmc_mbox_register_attn(mbox_attn_cb handler, void *drv_data)
214 : : {
215 : 4 : mbox_data.attn = handler;
216 : 4 : mbox_data.cb_attn = drv_data;
217 : :
218 : 4 : return 0;
219 : : }
220 : :
221 : 7 : uint8_t bmc_mbox_get_attn_reg(void)
222 : : {
223 : 7 : return server_state.attn_reg;
224 : : }
225 : :
226 : 4 : int bmc_mbox_register_callback(mbox_data_cb handler, void *drv_data)
227 : : {
228 : 4 : mbox_data.fn = handler;
229 : 4 : mbox_data.cb_data = drv_data;
230 : :
231 : 4 : return 0;
232 : : }
233 : :
234 : 734 : static int close_window(bool check)
235 : : {
236 : : /*
237 : : * This isn't strictly prohibited and some daemons let you close
238 : : * windows even if none are open.
239 : : * I've made the test fail because closing with no windows open is
240 : : * a sign that something 'interesting' has happened.
241 : : * You should investigate why
242 : : *
243 : : * If check is false it is because we just want to do the logic
244 : : * because open window has been called - you can open a window
245 : : * over a closed window obviously
246 : : */
247 : 734 : if (check && server_state.win_type == WIN_CLOSED)
248 : 0 : return MBOX_R_PARAM_ERROR;
249 : :
250 : 734 : server_state.win_type = WIN_CLOSED;
251 : 734 : mprotect(server_state.lpc_base, server_state.lpc_size, PROT_NONE);
252 : :
253 : 734 : return MBOX_R_SUCCESS;
254 : : }
255 : :
256 : 120 : static int do_dirty(uint32_t pos, uint32_t size)
257 : : {
258 : 120 : pos <<= server_state.block_shift;
259 : 120 : if (server_state.api > 1)
260 : 72 : size <<= server_state.block_shift;
261 : 120 : if (!check_window(pos, size)) {
262 : 0 : prlog(PR_ERR, "Trying to dirty not in open window range\n");
263 : 0 : return MBOX_R_PARAM_ERROR;
264 : : }
265 : 120 : if (server_state.win_type != WIN_WRITE) {
266 : 0 : prlog(PR_ERR, "Trying to dirty not write window\n");
267 : 0 : return MBOX_R_PARAM_ERROR;
268 : : }
269 : :
270 : : /* Thats about all actually */
271 : 120 : return MBOX_R_SUCCESS;
272 : : }
273 : :
274 : 944 : void check_timers(bool __unused unused)
275 : : {
276 : : /* now that we've handled the message, holla-back */
277 : 944 : if (mbox_data.msg) {
278 : 944 : mbox_data.fn(mbox_data.msg, mbox_data.cb_data);
279 : 944 : mbox_data.msg = NULL;
280 : : }
281 : 944 : }
282 : :
283 : 735 : static int open_window(struct bmc_mbox_msg *msg, bool write, u32 offset, u32 size)
284 : : {
285 : 735 : int max_size = server_state.max_read_win << server_state.block_shift;
286 : : //int win_size = server_state.def_read_win;
287 : 735 : enum win_type type = WIN_READ;
288 : 735 : int prot = PROT_READ;
289 : :
290 : 735 : assert(server_state.win_type == WIN_CLOSED);
291 : :
292 : : /* Shift params up */
293 : 735 : offset <<= server_state.block_shift;
294 : 735 : size <<= server_state.block_shift;
295 : :
296 : 735 : if (!size || server_state.api == 1)
297 : 734 : size = server_state.def_read_win << server_state.block_shift;
298 : :
299 : 735 : if (write) {
300 : 95 : max_size = server_state.max_write_win << server_state.block_shift;
301 : : //win_size = server_state.def_write_win;
302 : 95 : prot |= PROT_WRITE;
303 : 95 : type = WIN_WRITE;
304 : : /* Use the default size if zero size is set */
305 : 95 : if (!size || server_state.api == 1)
306 : 23 : size = server_state.def_write_win << server_state.block_shift;
307 : : }
308 : :
309 : :
310 : 735 : prlog(PR_INFO, "Opening range %#.8x, %#.8x for %s\n",
311 : : offset, offset + size - 1, write ? "writing" : "reading");
312 : :
313 : : /* XXX: Document this behaviour */
314 : 735 : if ((size + offset) > server_state.lpc_size) {
315 : 4 : prlog(PR_INFO, "tried to open beyond end of flash\n");
316 : 4 : return MBOX_R_PARAM_ERROR;
317 : : }
318 : :
319 : : /* XXX: should we do this before or after checking for errors?
320 : : * Doing it afterwards ensures consistency between
321 : : * implementations
322 : : */
323 : 731 : if (server_state.api == 2)
324 : 365 : size = MIN(size, max_size);
325 : :
326 : 731 : mprotect(server_state.lpc_base + offset, size, prot);
327 : 731 : server_state.win_type = type;
328 : 731 : server_state.win_base = offset;
329 : 731 : server_state.win_size = size;
330 : :
331 : 731 : memset(msg->args, 0, sizeof(msg->args));
332 : 731 : bmc_put_u16(msg, 0, offset >> server_state.block_shift);
333 : 731 : if (server_state.api == 1) {
334 : : /*
335 : : * Put nonsense in here because v1 mbox-flash shouldn't know about it.
336 : : * If v1 mbox-flash does read this, 0xffff should trigger a big mistake.
337 : : */
338 : 187 : bmc_put_u16(msg, 2, 0xffff >> server_state.block_shift);
339 : 187 : bmc_put_u16(msg, 4, 0xffff >> server_state.block_shift);
340 : : } else {
341 : 544 : bmc_put_u16(msg, 2, size >> server_state.block_shift);
342 : 544 : bmc_put_u16(msg, 4, offset >> server_state.block_shift);
343 : : }
344 : 731 : return MBOX_R_SUCCESS;
345 : : }
346 : :
347 : 944 : int bmc_mbox_enqueue(struct bmc_mbox_msg *msg,
348 : : unsigned int __unused timeout_sec)
349 : : {
350 : : /*
351 : : * FIXME: should we be using the same storage for message
352 : : * and response?
353 : : */
354 : 944 : int rc = MBOX_R_SUCCESS;
355 : : uint32_t start, size;
356 : :
357 : 944 : if (server_state.reset && msg->command != MBOX_C_GET_MBOX_INFO &&
358 : 3 : msg->command != MBOX_C_BMC_EVENT_ACK) {
359 : : /*
360 : : * Real daemons should return an error, but for testing we'll
361 : : * be a bit more strict
362 : : */
363 : 0 : prlog(PR_EMERG, "Server was in reset state - illegal command %d\n",
364 : : msg->command);
365 : 0 : exit(1);
366 : : }
367 : :
368 : 944 : switch (msg->command) {
369 : 1 : case MBOX_C_RESET_STATE:
370 : 1 : prlog(PR_INFO, "RESET_STATE\n");
371 : 1 : server_state.win_type = WIN_CLOSED;
372 : 1 : rc = open_window(msg, false, 0, LPC_BLOCKS);
373 : 1 : memset(msg->args, 0, sizeof(msg->args));
374 : 1 : break;
375 : :
376 : 4 : case MBOX_C_GET_MBOX_INFO:
377 : 4 : prlog(PR_INFO, "GET_MBOX_INFO version = %d, block_shift = %d\n",
378 : : server_state.api, server_state.block_shift);
379 : 4 : msg->args[0] = server_state.api;
380 : 4 : if (server_state.api == 1) {
381 : 1 : prlog(PR_INFO, "\tread_size = 0x%08x, write_size = 0x%08x\n",
382 : : server_state.def_read_win, server_state.def_write_win);
383 : 1 : bmc_put_u16(msg, 1, server_state.def_read_win);
384 : 1 : bmc_put_u16(msg, 3, server_state.def_write_win);
385 : 1 : msg->args[5] = 0xff; /* If v1 reads this, 0xff will force the mistake */
386 : : } else {
387 : 3 : msg->args[5] = server_state.block_shift;
388 : : }
389 : 4 : server_state.reset = false;
390 : 4 : break;
391 : :
392 : 4 : case MBOX_C_GET_FLASH_INFO:
393 : 4 : prlog(PR_INFO, "GET_FLASH_INFO: size: 0x%" PRIu64 ", erase: 0x%08x\n",
394 : : server_state.lpc_size, server_state.erase_granule);
395 : 4 : if (server_state.api == 1) {
396 : 1 : bmc_put_u32(msg, 0, server_state.lpc_size);
397 : 1 : bmc_put_u32(msg, 4, server_state.erase_granule);
398 : : } else {
399 : 3 : bmc_put_u16(msg, 0, server_state.lpc_size >> server_state.block_shift);
400 : 3 : bmc_put_u16(msg, 2, server_state.erase_granule >> server_state.block_shift);
401 : : }
402 : 4 : break;
403 : :
404 : 639 : case MBOX_C_CREATE_READ_WINDOW:
405 : 639 : start = bmc_get_u16(msg, 0);
406 : 639 : size = bmc_get_u16(msg, 2);
407 : 639 : prlog(PR_INFO, "CREATE_READ_WINDOW: pos: 0x%08x, len: 0x%08x\n", start, size);
408 : 639 : rc = close_window(false);
409 : 639 : if (rc != MBOX_R_SUCCESS)
410 : 0 : break;
411 : 639 : rc = open_window(msg, false, start, size);
412 : 639 : break;
413 : :
414 : 0 : case MBOX_C_CLOSE_WINDOW:
415 : 0 : rc = close_window(true);
416 : 0 : break;
417 : :
418 : 95 : case MBOX_C_CREATE_WRITE_WINDOW:
419 : 95 : start = bmc_get_u16(msg, 0);
420 : 95 : size = bmc_get_u16(msg, 2);
421 : 95 : prlog(PR_INFO, "CREATE_WRITE_WINDOW: pos: 0x%08x, len: 0x%08x\n", start, size);
422 : 95 : rc = close_window(false);
423 : 95 : if (rc != MBOX_R_SUCCESS)
424 : 0 : break;
425 : 95 : rc = open_window(msg, true, start, size);
426 : 95 : break;
427 : :
428 : : /* TODO: make these do something */
429 : 99 : case MBOX_C_WRITE_FLUSH:
430 : 99 : prlog(PR_INFO, "WRITE_FLUSH\n");
431 : : /*
432 : : * This behaviour isn't strictly illegal however it could
433 : : * be a sign of bad behaviour
434 : : */
435 : 99 : if (server_state.api > 1 && !server_state.win_dirty) {
436 : 0 : prlog(PR_EMERG, "Version >1 called FLUSH without a previous DIRTY\n");
437 : 0 : exit (1);
438 : : }
439 : 99 : server_state.win_dirty = false;
440 : 99 : if (server_state.api > 1)
441 : 75 : break;
442 : :
443 : : /* This is only done on V1 */
444 : 24 : start = bmc_get_u16(msg, 0);
445 : 24 : if (server_state.api == 1)
446 : 24 : size = bmc_get_u32(msg, 2);
447 : : else
448 : 0 : size = bmc_get_u16(msg, 2);
449 : 24 : prlog(PR_INFO, "\tpos: 0x%08x len: 0x%08x\n", start, size);
450 : 24 : rc = do_dirty(start, size);
451 : 24 : break;
452 : 96 : case MBOX_C_MARK_WRITE_DIRTY:
453 : 96 : start = bmc_get_u16(msg, 0);
454 : 96 : if (server_state.api == 1)
455 : 24 : size = bmc_get_u32(msg, 2);
456 : : else
457 : 72 : size = bmc_get_u16(msg, 2);
458 : 96 : prlog(PR_INFO, "MARK_WRITE_DIRTY: pos: 0x%08x, len: %08x\n", start, size);
459 : 96 : server_state.win_dirty = true;
460 : 96 : rc = do_dirty(start, size);
461 : 96 : break;
462 : 3 : case MBOX_C_BMC_EVENT_ACK:
463 : : /*
464 : : * Clear any BMC notifier flags. Don't clear the server
465 : : * reset state here, it is a permitted command but only
466 : : * GET_INFO should clear it.
467 : : *
468 : : * Make sure that msg->args[0] is only acking bits we told
469 : : * it about, in server_state.attn_reg. The caveat is that
470 : : * it could NOT ack some bits...
471 : : */
472 : 3 : prlog(PR_INFO, "BMC_EVENT_ACK 0x%02x\n", msg->args[0]);
473 : 3 : if ((msg->args[0] | server_state.attn_reg) != server_state.attn_reg) {
474 : 0 : prlog(PR_EMERG, "Tried to ack bits we didn't say!\n");
475 : 0 : exit(1);
476 : : }
477 : 3 : msg->bmc &= ~msg->args[0];
478 : 3 : server_state.attn_reg &= ~msg->args[0];
479 : 3 : break;
480 : 3 : case MBOX_C_MARK_WRITE_ERASED:
481 : 3 : start = bmc_get_u16(msg, 0) << server_state.block_shift;
482 : 3 : size = bmc_get_u16(msg, 2) << server_state.block_shift;
483 : : /* If we've negotiated v1 this should never be called */
484 : 3 : if (server_state.api == 1) {
485 : 0 : prlog(PR_EMERG, "Version 1 protocol called a V2 only command\n");
486 : 0 : exit(1);
487 : : }
488 : : /*
489 : : * This will likely result in flush (but not
490 : : * dirty) being called. This is the point.
491 : : */
492 : 3 : server_state.win_dirty = true;
493 : : /* This should really be done when they call flush */
494 : 3 : memset(server_state.lpc_base + server_state.win_base + start, 0xff, size);
495 : 3 : break;
496 : 0 : default:
497 : 0 : prlog(PR_EMERG, "Got unknown command code from mbox: %d\n", msg->command);
498 : : }
499 : :
500 : 944 : prerror("command response = %d\n", rc);
501 : 944 : msg->response = rc;
502 : :
503 : 944 : mbox_data.msg = msg;
504 : :
505 : 944 : return 0;
506 : : }
507 : :
508 : 23 : int mbox_server_memcmp(int off, const void *buf, size_t len)
509 : : {
510 : 23 : return memcmp(server_state.lpc_base + off, buf, len);
511 : : }
512 : :
513 : 4 : void mbox_server_memset(int c)
514 : : {
515 : 4 : memset(server_state.lpc_base, c, server_state.lpc_size);
516 : 4 : }
517 : :
518 : 4 : uint32_t mbox_server_total_size(void)
519 : : {
520 : : /* Not actually but for this server we don't differentiate */
521 : 4 : return server_state.lpc_size;
522 : : }
523 : :
524 : 4 : uint32_t mbox_server_erase_granule(void)
525 : : {
526 : 4 : return server_state.erase_granule;
527 : : }
528 : :
529 : 8 : int mbox_server_version(void)
530 : : {
531 : 8 : return server_state.api;
532 : : }
533 : :
534 : 3 : int mbox_server_reset(unsigned int version, uint8_t block_shift)
535 : : {
536 : 3 : if (version > 3)
537 : 0 : return 1;
538 : :
539 : 3 : server_state.api = version;
540 : 3 : if (block_shift)
541 : 3 : server_state.block_shift = block_shift;
542 : 3 : if (server_state.erase_granule < (1 << server_state.block_shift))
543 : 2 : server_state.erase_granule = 1 << server_state.block_shift;
544 : 3 : server_state.lpc_size = LPC_BLOCKS * (1 << server_state.block_shift);
545 : 3 : free(server_state.lpc_base);
546 : 3 : server_state.lpc_base = malloc(server_state.lpc_size);
547 : 3 : server_state.attn_reg = MBOX_ATTN_BMC_REBOOT | MBOX_ATTN_BMC_DAEMON_READY;
548 : 3 : server_state.win_type = WIN_CLOSED;
549 : 3 : server_state.reset = true;
550 : 3 : mbox_data.attn(MBOX_ATTN_BMC_REBOOT, mbox_data.cb_attn);
551 : :
552 : 3 : return 0;
553 : : }
554 : :
555 : 1 : int mbox_server_init(void)
556 : : {
557 : 1 : server_state.api = 1;
558 : 1 : server_state.reset = true;
559 : :
560 : : /* We're always ready! */
561 : 1 : server_state.attn_reg = MBOX_ATTN_BMC_DAEMON_READY;
562 : :
563 : : /* setup server */
564 : 1 : server_state.block_shift = 12;
565 : 1 : server_state.erase_granule = 0x1000;
566 : 1 : server_state.lpc_size = LPC_BLOCKS * (1 << server_state.block_shift);
567 : 1 : server_state.lpc_base = malloc(server_state.lpc_size);
568 : :
569 : 1 : server_state.def_read_win = 1; /* These are in units of block shift "= 1 is 4K" */
570 : 1 : server_state.def_write_win = 1; /* These are in units of block shift "= 1 is 4K" */
571 : :
572 : 1 : server_state.max_read_win = LPC_BLOCKS;
573 : 1 : server_state.max_write_win = LPC_BLOCKS;
574 : 1 : server_state.win_type = WIN_CLOSED;
575 : :
576 : 1 : return 0;
577 : : }
578 : :
579 : 1 : void mbox_server_destroy(void)
580 : : {
581 : 1 : free(server_state.lpc_base);
582 : 1 : }
|