Fix red in bootloaders
[maemo-rb.git] / apps / plugins / iriver_flash.c
blob0b3833b9cf86ddf3d9af298dc093baa53da397d1
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * !!! DON'T MESS WITH THIS CODE UNLESS YOU'RE ABSOLUTELY SURE WHAT YOU DO !!!
12 * Copyright (C) 2006 by Miika Pekkarinen
14 * This program is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU General Public License
16 * as published by the Free Software Foundation; either version 2
17 * of the License, or (at your option) any later version.
19 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
20 * KIND, either express or implied.
22 ****************************************************************************/
23 #include "plugin.h"
25 /* All CFI flash routines are copied and ported from firmware_flash.c */
27 unsigned char *audiobuf;
28 ssize_t audiobuf_size;
30 #ifdef IRIVER_H100_SERIES
31 #define PLATFORM_ID ID_IRIVER_H100
32 #else
33 #undef PLATFORM_ID /* this platform is not (yet) flashable */
34 #endif
36 #ifdef PLATFORM_ID
38 PLUGIN_HEADER
40 #if CONFIG_KEYPAD == IRIVER_H100_PAD
41 #define KEY1 BUTTON_OFF
42 #define KEY2 BUTTON_ON
43 #define KEY3 BUTTON_SELECT
44 #define KEYNAME1 "[Stop]"
45 #define KEYNAME2 "[On]"
46 #define KEYNAME3 "[Select]"
47 #endif
49 struct flash_info
51 uint8_t manufacturer;
52 uint8_t id;
53 int size;
54 char name[32];
57 #ifdef IRIVER_H100_SERIES
58 #define SEC_SIZE 4096
59 #define BOOTLOADER_ERASEGUARD (BOOTLOADER_ENTRYPOINT / SEC_SIZE)
60 enum sections {
61 SECT_RAMIMAGE = 1,
62 SECT_ROMIMAGE = 2,
65 static volatile uint16_t* FB = (uint16_t*)0x00000000; /* Flash base address */
66 #endif
68 /* read the manufacturer and device ID */
69 bool cfi_read_id(volatile uint16_t* pBase, uint8_t* pManufacturerID, uint8_t* pDeviceID)
71 uint8_t not_manu, not_id; /* read values before switching to ID mode */
72 uint8_t manu, id; /* read values when in ID mode */
74 pBase = (uint16_t*)((uint32_t)pBase & 0xFFF80000); /* down to 512k align */
76 /* read the normal content */
77 not_manu = pBase[0]; /* should be 'A' (0x41) and 'R' (0x52) */
78 not_id = pBase[1]; /* from the "ARCH" marker */
80 pBase[0x5555] = 0xAA; /* enter command mode */
81 pBase[0x2AAA] = 0x55;
82 pBase[0x5555] = 0x90; /* ID command */
83 rb->sleep(HZ/50); /* Atmel wants 20ms pause here */
85 manu = pBase[0];
86 id = pBase[1];
88 pBase[0] = 0xF0; /* reset flash (back to normal read mode) */
89 rb->sleep(HZ/50); /* Atmel wants 20ms pause here */
91 /* I assume success if the obtained values are different from
92 the normal flash content. This is not perfectly bulletproof, they
93 could theoretically be the same by chance, causing us to fail. */
94 if (not_manu != manu || not_id != id) /* a value has changed */
96 *pManufacturerID = manu; /* return the results */
97 *pDeviceID = id;
98 return true; /* success */
100 return false; /* fail */
104 /* erase the sector which contains the given address */
105 bool cfi_erase_sector(volatile uint16_t* pAddr)
107 unsigned timeout = 430000; /* the timeout loop should be no less than 25ms */
109 FB[0x5555] = 0xAA; /* enter command mode */
110 FB[0x2AAA] = 0x55;
111 FB[0x5555] = 0x80; /* erase command */
112 FB[0x5555] = 0xAA; /* enter command mode */
113 FB[0x2AAA] = 0x55;
114 *pAddr = 0x30; /* erase the sector */
116 /* I counted 7 instructions for this loop -> min. 0.58 us per round */
117 /* Plus memory waitstates it will be much more, gives margin */
118 while (*pAddr != 0xFFFF && --timeout); /* poll for erased */
120 return (timeout != 0);
124 /* address must be in an erased location */
125 inline bool cfi_program_word(volatile uint16_t* pAddr, uint16_t data)
127 unsigned timeout = 85; /* the timeout loop should be no less than 20us */
129 if (~*pAddr & data) /* just a safety feature, not really necessary */
130 return false; /* can't set any bit from 0 to 1 */
132 FB[0x5555] = 0xAA; /* enter command mode */
133 FB[0x2AAA] = 0x55;
134 FB[0x5555] = 0xA0; /* byte program command */
136 *pAddr = data;
138 /* I counted 7 instructions for this loop -> min. 0.58 us per round */
139 /* Plus memory waitstates it will be much more, gives margin */
140 while (*pAddr != data && --timeout); /* poll for programmed */
142 return (timeout != 0);
146 /* this returns true if supported and fills the info struct */
147 bool cfi_get_flash_info(struct flash_info* pInfo)
149 rb->memset(pInfo, 0, sizeof(struct flash_info));
151 if (!cfi_read_id(FB, &pInfo->manufacturer, &pInfo->id))
152 return false;
154 if (pInfo->manufacturer == 0xBF) /* SST */
156 if (pInfo->id == 0xD6)
158 pInfo->size = 256* 1024; /* 256k */
159 rb->strcpy(pInfo->name, "SST39VF020");
160 return true;
162 else if (pInfo->id == 0xD7)
164 pInfo->size = 512* 1024; /* 512k */
165 rb->strcpy(pInfo->name, "SST39VF040");
166 return true;
168 else if (pInfo->id == 0x82)
170 pInfo->size = 2048* 1024; /* 2 MiB */
171 rb->strcpy(pInfo->name, "SST39VF160");
172 return true;
174 else
175 return false;
177 return false;
181 /*********** Utility Functions ************/
184 /* Tool function to calculate a CRC32 across some buffer */
185 /* third argument is either 0xFFFFFFFF to start or value from last piece */
186 unsigned crc_32(const unsigned char* buf, unsigned len, unsigned crc32)
188 /* CCITT standard polynomial 0x04C11DB7 */
189 static const unsigned crc32_lookup[16] =
190 { /* lookup table for 4 bits at a time is affordable */
191 0x00000000, 0x04C11DB7, 0x09823B6E, 0x0D4326D9,
192 0x130476DC, 0x17C56B6B, 0x1A864DB2, 0x1E475005,
193 0x2608EDB8, 0x22C9F00F, 0x2F8AD6D6, 0x2B4BCB61,
194 0x350C9B64, 0x31CD86D3, 0x3C8EA00A, 0x384FBDBD
197 unsigned char byte;
198 unsigned t;
200 while (len--)
202 byte = *buf++; /* get one byte of data */
204 /* upper nibble of our data */
205 t = crc32 >> 28; /* extract the 4 most significant bits */
206 t ^= byte >> 4; /* XOR in 4 bits of data into the extracted bits */
207 crc32 <<= 4; /* shift the CRC register left 4 bits */
208 crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */
210 /* lower nibble of our data */
211 t = crc32 >> 28; /* extract the 4 most significant bits */
212 t ^= byte & 0x0F; /* XOR in 4 bits of data into the extracted bits */
213 crc32 <<= 4; /* shift the CRC register left 4 bits */
214 crc32 ^= crc32_lookup[t]; /* do the table lookup and XOR the result */
217 return crc32;
221 /***************** User Interface Functions *****************/
222 int wait_for_button(void)
224 int button;
228 button = rb->button_get(true);
229 } while (IS_SYSEVENT(button) || (button & BUTTON_REL));
231 return button;
234 /* helper for DoUserDialog() */
235 void ShowFlashInfo(struct flash_info* pInfo)
237 char buf[32];
239 if (!pInfo->manufacturer)
241 rb->lcd_puts(0, 0, "Flash: M=?? D=??");
242 rb->lcd_puts(0, 1, "Impossible to program");
244 else
246 rb->snprintf(buf, sizeof(buf), "Flash: M=%02x D=%02x",
247 pInfo->manufacturer, pInfo->id);
248 rb->lcd_puts(0, 0, buf);
251 if (pInfo->size)
253 rb->lcd_puts(0, 1, pInfo->name);
254 rb->snprintf(buf, sizeof(buf), "Size: %d KB", pInfo->size / 1024);
255 rb->lcd_puts(0, 2, buf);
257 else
259 rb->lcd_puts(0, 1, "Unsupported chip");
264 rb->lcd_update();
267 bool show_info(void)
269 struct flash_info fi;
271 rb->lcd_clear_display();
272 cfi_get_flash_info(&fi);
273 ShowFlashInfo(&fi);
274 if (fi.size == 0) /* no valid chip */
276 rb->splash(HZ*3, "Sorry!");
277 return false; /* exit */
280 return true;
283 bool confirm(const char *msg)
285 char buf[128];
286 bool ret;
288 rb->snprintf(buf, sizeof buf, "%s ([PLAY] to CONFIRM)", msg);
289 rb->splash(0, buf);
291 ret = (wait_for_button() == BUTTON_ON);
292 show_info();
294 return ret;
297 int load_firmware_file(const char *filename, uint32_t *checksum)
299 int fd;
300 int len, rc;
301 int i;
302 uint32_t sum;
304 fd = rb->open(filename, O_RDONLY);
305 if (fd < 0)
306 return -1;
308 len = rb->filesize(fd);
310 if (audiobuf_size < len)
312 rb->splash(HZ*3, "Aborting: Out of memory!");
313 rb->close(fd);
314 return -2;
317 rb->read(fd, checksum, 4);
318 rb->lseek(fd, FIRMWARE_OFFSET_FILE_DATA, SEEK_SET);
319 len -= FIRMWARE_OFFSET_FILE_DATA;
321 rc = rb->read(fd, audiobuf, len);
322 rb->close(fd);
323 if (rc != len)
325 rb->splash(HZ*3, "Aborting: Read failure");
326 return -3;
329 /* Verify the checksum */
330 sum = MODEL_NUMBER;
331 for (i = 0; i < len; i++)
332 sum += audiobuf[i];
334 if (sum != *checksum)
336 rb->splash(HZ*3, "Aborting: Checksums mismatch!");
337 return -4;
340 return len;
343 unsigned long valid_bootloaders[][2] = {
344 /* Size-8 CRC32 */
345 #ifdef IRIVER_H120 /* Iriver H120/H140 checksums */
346 { 63788, 0x08ff01a9 }, /* 7-pre3, improved failsafe functions */
347 { 48764, 0xc674323e }, /* 7-pre4. Fixed audio thump & remote bootup */
348 #endif
349 #ifdef IRIVER_H100
350 { 48760, 0x2efc3323 }, /* 7-pre4 */
351 #endif
352 { 0, 0 }
356 bool detect_valid_bootloader(const unsigned char *addr, int len)
358 int i;
359 unsigned long crc32;
361 /* Try to scan through all valid bootloaders. */
362 for (i = 0; valid_bootloaders[i][0]; i++)
364 if (len > 0 && len != (long)valid_bootloaders[i][0])
365 continue;
367 crc32 = crc_32(addr, valid_bootloaders[i][0], 0xffffffff);
368 if (crc32 == valid_bootloaders[i][1])
369 return true;
372 return false;
375 static int get_section_address(int section)
377 if (section == SECT_RAMIMAGE)
378 return FLASH_RAMIMAGE_ENTRY;
379 else if (section == SECT_ROMIMAGE)
380 return FLASH_ROMIMAGE_ENTRY;
381 else
382 return -1;
385 int flash_rockbox(const char *filename, int section)
387 struct flash_header hdr;
388 char buf[64];
389 int pos, i, len, rc;
390 unsigned long checksum, sum;
391 unsigned char *p8;
392 uint16_t *p16;
394 if (get_section_address(section) < 0)
395 return -1;
397 p8 = (char *)BOOTLOADER_ENTRYPOINT;
398 if (!detect_valid_bootloader(p8, 0))
400 rb->splash(HZ*3, "Incompatible bootloader");
401 return -1;
404 if (!rb->detect_original_firmware())
406 if (!confirm("Update Rockbox flash image?"))
407 return -2;
409 else
411 if (!confirm("Erase original firmware?"))
412 return -3;
415 len = load_firmware_file(filename, &checksum);
416 if (len <= 0)
417 return len * 10;
419 pos = get_section_address(section);
421 /* Check if image relocation seems to be sane. */
422 if (section == SECT_ROMIMAGE)
424 uint32_t *p32 = (uint32_t *)audiobuf;
426 if (pos+sizeof(struct flash_header) != *p32)
428 rb->snprintf(buf, sizeof(buf), "Incorrect relocation: 0x%08lx/0x%08lx",
429 *p32, pos+sizeof(struct flash_header));
430 rb->splash(HZ*10, buf);
431 return -1;
436 /* Erase the program flash. */
437 for (i = 0; i + pos < BOOTLOADER_ENTRYPOINT && i < len + 32; i += SEC_SIZE)
439 /* Additional safety check. */
440 if (i + pos < SEC_SIZE)
441 return -1;
443 rb->snprintf(buf, sizeof(buf), "Erasing... %d%%",
444 (i+SEC_SIZE)*100/len);
445 rb->lcd_puts(0, 3, buf);
446 rb->lcd_update();
448 rc = cfi_erase_sector(FB + (i + pos)/2);
451 /* Write the magic and size. */
452 rb->memset(&hdr, 0, sizeof(struct flash_header));
453 hdr.magic = FLASH_MAGIC;
454 hdr.length = len;
455 // rb->strncpy(hdr.version, APPSVERSION, sizeof(hdr.version)-1);
456 p16 = (uint16_t *)&hdr;
458 rb->snprintf(buf, sizeof(buf), "Programming...");
459 rb->lcd_puts(0, 4, buf);
460 rb->lcd_update();
462 pos = get_section_address(section)/2;
463 for (i = 0; i < (long)sizeof(struct flash_header)/2; i++)
465 cfi_program_word(FB + pos, p16[i]);
466 pos++;
469 p16 = (uint16_t *)audiobuf;
470 for (i = 0; i < len/2 && pos + i < (BOOTLOADER_ENTRYPOINT/2); i++)
472 if (i % SEC_SIZE == 0)
474 rb->snprintf(buf, sizeof(buf), "Programming... %d%%",
475 (i+1)*100/(len/2));
476 rb->lcd_puts(0, 4, buf);
477 rb->lcd_update();
480 cfi_program_word(FB + pos + i, p16[i]);
483 /* Verify */
484 rb->snprintf(buf, sizeof(buf), "Verifying");
485 rb->lcd_puts(0, 5, buf);
486 rb->lcd_update();
488 p8 = (char *)get_section_address(section);
489 p8 += sizeof(struct flash_header);
490 sum = MODEL_NUMBER;
491 for (i = 0; i < len; i++)
492 sum += p8[i];
494 if (sum != checksum)
496 rb->splash(HZ*3, "Verify failed!");
497 /* Erase the magic sector so bootloader does not try to load
498 * rockbox from flash and crash. */
499 if (section == SECT_RAMIMAGE)
500 cfi_erase_sector(FB + FLASH_RAMIMAGE_ENTRY/2);
501 else
502 cfi_erase_sector(FB + FLASH_ROMIMAGE_ENTRY/2);
503 return -5;
506 rb->splash(HZ*2, "Success");
508 return 0;
511 void show_fatal_error(void)
513 rb->splash(HZ*30, "Disable idle poweroff, connect AC power and DON'T TURN PLAYER OFF!!");
514 rb->splash(HZ*30, "Contact Rockbox developers as soon as possible!");
515 rb->splash(HZ*30, "Your device won't be bricked unless you turn off the power");
516 rb->splash(HZ*30, "Don't use the device before further instructions from Rockbox developers");
519 int flash_bootloader(const char *filename)
521 char *bootsector;
522 int pos, i, len, rc;
523 unsigned long checksum, sum;
524 unsigned char *p8;
525 uint16_t *p16;
527 bootsector = audiobuf;
528 audiobuf += SEC_SIZE;
529 audiobuf_size -= SEC_SIZE;
531 if (!confirm("Update bootloader?"))
532 return -2;
534 len = load_firmware_file(filename, &checksum);
535 if (len <= 0)
536 return len * 10;
538 if (len > 0xFFFF)
540 rb->splash(HZ*3, "Too big bootloader");
541 return -1;
544 /* Verify the crc32 checksum also. */
545 if (!detect_valid_bootloader(audiobuf, len))
547 rb->splash(HZ*3, "Incompatible/Untested bootloader");
548 return -1;
551 rb->lcd_puts(0, 3, "Flashing...");
552 rb->lcd_update();
554 /* Backup the bootloader sector first. */
555 p8 = (char *)FB;
556 rb->memcpy(bootsector, p8, SEC_SIZE);
558 /* Erase the boot sector and write a proper reset vector. */
559 cfi_erase_sector(FB);
560 p16 = (uint16_t *)audiobuf;
561 for (i = 0; i < 8/2; i++)
562 cfi_program_word(FB + i, p16[i]);
564 /* And restore original content for original FW to function. */
565 p16 = (uint16_t *)bootsector;
566 for (i = 8/2; i < SEC_SIZE/2; i++)
567 cfi_program_word(FB + i, p16[i]);
569 /* Erase the bootloader flash section. */
570 for (i = BOOTLOADER_ENTRYPOINT/SEC_SIZE; i < 0x200; i++)
571 rc = cfi_erase_sector(FB + (SEC_SIZE/2) * i);
573 pos = BOOTLOADER_ENTRYPOINT/2;
574 p16 = (uint16_t *)audiobuf;
575 for (i = 0; i < len/2; i++)
576 cfi_program_word(FB + pos + i, p16[i]);
578 /* Verify */
579 p8 = (char *)BOOTLOADER_ENTRYPOINT;
580 sum = MODEL_NUMBER;
581 for (i = 0; i < len; i++)
582 sum += p8[i];
584 if (sum != checksum)
586 rb->splash(HZ*3, "Verify failed!");
587 show_fatal_error();
588 return -5;
591 p8 = (char *)FB;
592 for (i = 0; i < 8; i++)
594 if (p8[i] != audiobuf[i])
596 rb->splash(HZ*3, "Bootvector corrupt!");
597 show_fatal_error();
598 return -6;
602 rb->splash(HZ*2, "Success");
604 return 0;
607 int flash_original_fw(int len)
609 unsigned char reset_vector[8];
610 char buf[32];
611 int pos, i, rc;
612 unsigned char *p8;
613 uint16_t *p16;
615 (void)buf;
617 rb->lcd_puts(0, 3, "Critical section...");
618 rb->lcd_update();
620 p8 = (char *)FB;
621 rb->memcpy(reset_vector, p8, sizeof reset_vector);
623 /* Erase the boot sector and write back the reset vector. */
624 cfi_erase_sector(FB);
625 p16 = (uint16_t *)reset_vector;
626 for (i = 0; i < (long)sizeof(reset_vector)/2; i++)
627 cfi_program_word(FB + i, p16[i]);
629 rb->lcd_puts(0, 4, "Flashing orig. FW");
630 rb->lcd_update();
632 /* Erase the program flash. */
633 for (i = 1; i < BOOTLOADER_ERASEGUARD && (i-1)*4096 < len; i++)
635 rc = cfi_erase_sector(FB + (SEC_SIZE/2) * i);
636 rb->snprintf(buf, sizeof(buf), "Erase: 0x%03x (%d)", i, rc);
637 rb->lcd_puts(0, 5, buf);
638 rb->lcd_update();
641 rb->snprintf(buf, sizeof(buf), "Programming");
642 rb->lcd_puts(0, 6, buf);
643 rb->lcd_update();
645 pos = 0x00000008/2;
646 p16 = (uint16_t *)audiobuf;
647 for (i = 0; i < len/2 && pos + i < (BOOTLOADER_ENTRYPOINT/2); i++)
648 cfi_program_word(FB + pos + i, p16[i]);
650 rb->snprintf(buf, sizeof(buf), "Verifying");
651 rb->lcd_puts(0, 7, buf);
652 rb->lcd_update();
654 /* Verify reset vectors. */
655 p8 = (char *)FB;
656 for (i = 0; i < 8; i++)
658 if (p8[i] != reset_vector[i])
660 rb->splash(HZ*3, "Bootvector corrupt!");
661 show_fatal_error();
662 break;
666 /* Verify */
667 p8 = (char *)0x00000008;
668 for (i = 0; i < len; i++)
670 if (p8[i] != audiobuf[i])
672 rb->splash(HZ*3, "Verify failed!");
673 rb->snprintf(buf, sizeof buf, "at: 0x%08x", i);
674 rb->splash(HZ*10, buf);
675 return -5;
679 rb->splash(HZ*2, "Success");
681 return 0;
684 int load_original_bin(const char *filename)
686 unsigned long magic[2];
687 int len, rc;
688 int fd;
690 if (!confirm("Restore original firmware (bootloader will be kept)?"))
691 return -2;
693 fd = rb->open(filename, O_RDONLY);
694 if (fd < 0)
695 return -1;
697 len = rb->filesize(fd) - 0x228;
698 rb->lseek(fd, 0x220, SEEK_SET);
699 rb->read(fd, magic, 8);
700 if (magic[1] != 0x00000008 || len <= 0 || len > audiobuf_size)
702 rb->splash(HZ*2, "Not an original firmware file");
703 rb->close(fd);
704 return -1;
707 rc = rb->read(fd, audiobuf, len);
708 rb->close(fd);
710 if (rc != len)
712 rb->splash(HZ*2, "Read error");
713 return -2;
716 if (len % 2)
717 len++;
719 return flash_original_fw(len);
722 int load_romdump(const char *filename)
724 int len, rc;
725 int fd;
727 if (!confirm("Restore firmware section (bootloader will be kept)?"))
728 return -2;
730 fd = rb->open(filename, O_RDONLY);
731 if (fd < 0)
732 return -1;
734 len = rb->filesize(fd) - 8;
735 if (len <= 0)
736 return -1;
738 rb->lseek(fd, 8, SEEK_SET);
739 rc = rb->read(fd, audiobuf, len);
740 rb->close(fd);
742 if (rc != len)
744 rb->splash(HZ*2, "Read error");
745 return -2;
748 if (len % 2)
749 len++;
751 if (len > BOOTLOADER_ENTRYPOINT - 8)
752 len = BOOTLOADER_ENTRYPOINT - 8;
754 return flash_original_fw(len);
757 /* Kind of our main function, defines the application flow. */
758 void DoUserDialog(char* filename)
760 /* this can only work if Rockbox runs in DRAM, not flash ROM */
761 if ((uint16_t*)rb >= FB && (uint16_t*)rb < FB + 4096*1024) /* 4 MB max */
762 { /* we're running from flash */
763 rb->splash(HZ*3, "Not from ROM");
764 return; /* exit */
767 /* refuse to work if the power may fail meanwhile */
768 if (!rb->battery_level_safe())
770 rb->splash(HZ*3, "Battery too low!");
771 return; /* exit */
774 rb->lcd_setfont(FONT_SYSFIXED);
775 if (!show_info())
776 return ;
778 if (filename == NULL)
780 rb->splash(HZ*3, "Please use this plugin with \"Open with...\"");
781 return ;
784 audiobuf = rb->plugin_get_audio_buffer((size_t *)&audiobuf_size);
786 if (rb->strcasestr(filename, "/rockbox.iriver"))
787 flash_rockbox(filename, SECT_RAMIMAGE);
788 else if (rb->strcasestr(filename, "/rombox.iriver"))
789 flash_rockbox(filename, SECT_ROMIMAGE);
790 else if (rb->strcasestr(filename, "/bootloader.iriver"))
791 flash_bootloader(filename);
792 else if (rb->strcasestr(filename, "/ihp_120.bin"))
793 load_original_bin(filename);
794 else if (rb->strcasestr(filename, "/internal_rom_000000-1FFFFF.bin"))
795 load_romdump(filename);
796 else
797 rb->splash(HZ*3, "Unknown file type");
801 /***************** Plugin Entry Point *****************/
803 enum plugin_status plugin_start(const void* parameter)
805 int oldmode;
807 /* now go ahead and have fun! */
808 oldmode = rb->system_memory_guard(MEMGUARD_NONE); /*disable memory guard */
809 DoUserDialog((char*) parameter);
810 rb->system_memory_guard(oldmode); /* re-enable memory guard */
812 return PLUGIN_OK;
815 #endif /* ifdef PLATFORM_ID */