From 4d0451847a77d420284c7fb0f50b1f167c1118ee Mon Sep 17 00:00:00 2001 From: notaz Date: Thu, 29 Jul 2010 19:26:04 +0300 Subject: [PATCH] wiz port. Lots of refactoring, some bugfixes --- .gitignore | 3 + common/cmn.c | 29 +- common/fbdev.c | 2 +- common/host_fb.c | 133 ++++++++- common/host_fb.h | 3 + common/warm.c | 254 ++++++++++++++++++ common/warm.h | 100 +++++++ common/wiz_video.c | 20 +- dist/ginge.gpe | 14 + ginge.pxml => dist/ginge.pxml | 0 dist/ginge.sh | 14 + dist/ginge32.png | Bin 0 -> 1825 bytes ginge.png => dist/ginge60.png | Bin loader/ginge_dyn.sh => dist/ginge_dyn_eabi.sh | 0 dist/ginge_dyn_oabi.sh | 11 + dist/make_cmn.sh | 18 ++ loader/.gitignore | 1 - loader/Makefile | 12 +- loader/dl.c | 16 +- loader/emu.c | 89 +++--- loader/ginge_dyn.symver | 16 ++ loader/header.h | 1 + loader/host_pnd.c | 11 +- loader/host_wiz.c | 83 ++++++ loader/loader.c | 4 +- loader/override.c | 10 +- loader/realfuncs.h | 2 +- make_pnd.sh | 20 +- make_wiz.sh | 12 + prep/Makefile | 8 +- prep/main.c | 50 ++-- 31 files changed, 808 insertions(+), 128 deletions(-) create mode 100644 .gitignore create mode 100644 common/warm.c create mode 100644 common/warm.h create mode 100755 dist/ginge.gpe rename ginge.pxml => dist/ginge.pxml (100%) create mode 100755 dist/ginge.sh create mode 100644 dist/ginge32.png rename ginge.png => dist/ginge60.png (100%) rename loader/ginge_dyn.sh => dist/ginge_dyn_eabi.sh (100%) create mode 100755 dist/ginge_dyn_oabi.sh create mode 100755 dist/make_cmn.sh delete mode 100644 loader/.gitignore create mode 100644 loader/ginge_dyn.symver create mode 100644 loader/host_wiz.c create mode 100755 make_wiz.sh diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2c222af --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +*.o +cscope.out +tags diff --git a/common/cmn.c b/common/cmn.c index 9f802ba..7886c7c 100644 --- a/common/cmn.c +++ b/common/cmn.c @@ -1,3 +1,4 @@ +// vim:shiftwidth=2:expandtab #ifdef LOADER #include "../loader/realfuncs.h" #endif @@ -12,24 +13,24 @@ int make_local_path(char *buf, size_t size, const char *file) ssize_t ret; char *p; - ret = readlink("/proc/self/exe", buf, size - 1); - if (ret < 0) { - perror("readlink"); - goto err; + p = getenv("GINGE_ROOT"); + if (p != NULL) { + strncpy(buf, p, size); + buf[size - 1] = 0; + p = buf + strlen(buf); } - buf[ret] = 0; - - p = strrchr(buf, '/'); - if (p == NULL) - goto err; - p++; + else { + ret = readlink("/proc/self/exe", buf, size - 1); + if (ret < 0) { + perror("readlink"); + goto err; + } + buf[ret] = 0; - if (strstr(p, ".so")) { - p = getenv("GINGE_ROOT"); + p = strrchr(buf, '/'); if (p == NULL) goto err; - strcpy(buf, p); - p = buf + strlen(buf); + p++; } snprintf(p, size - (p - buf), "%s", file); diff --git a/common/fbdev.c b/common/fbdev.c index 069dc6e..afcd529 100644 --- a/common/fbdev.c +++ b/common/fbdev.c @@ -64,7 +64,7 @@ struct vout_fbdev *vout_fbdev_init(const char *fbdev_name, int *w, int *h, int n if (fbdev == NULL) return NULL; - fbdev->fd = open(fbdev_name, O_RDWR, 0); + fbdev->fd = open(fbdev_name, O_RDWR); if (fbdev->fd == -1) { fprintf(stderr, "%s: ", fbdev_name); perror("open"); diff --git a/common/host_fb.c b/common/host_fb.c index 92cde57..46467d4 100644 --- a/common/host_fb.c +++ b/common/host_fb.c @@ -1,27 +1,138 @@ +// vim:shiftwidth=2:expandtab +#include #ifdef LOADER #include "../loader/realfuncs.h" #endif -#include "fbdev.c" #include "host_fb.h" +static void *host_screen; +static int host_stride; + +#if defined(PND) + +#include "fbdev.c" + static struct vout_fbdev *fbdev; +void *host_video_flip(void) +{ + host_screen = vout_fbdev_flip(fbdev); + return host_screen; +} + int host_video_init(int *stride, int no_dblbuf) { - const char *fbdev_name; - int w, h; + const char *fbdev_name; + int w, h; + + fbdev_name = getenv("FBDEV"); + if (fbdev_name == NULL) + fbdev_name = "/dev/fb1"; + + fbdev = vout_fbdev_init(fbdev_name, &w, &h, no_dblbuf); + if (fbdev == NULL) + return -1; + + host_stride = w * 2; + if (stride != 0) + *stride = host_stride; + host_video_flip(); - fbdev_name = getenv("FBDEV"); - if (fbdev_name == NULL) - fbdev_name = "/dev/fb1"; - - fbdev = vout_fbdev_init(fbdev_name, &w, &h, no_dblbuf); - *stride = w * 2; - return (fbdev != 0) ? 0 : -1; + return 0; } +#elif defined(WIZ) + +#include "warm.c" +#include "wiz_video.c" + void *host_video_flip(void) { - return vout_fbdev_flip(fbdev); + vout_gp2x_flip(); + host_screen = g_screen_ptr; + return host_screen; +} + +int host_video_init(int *stride, int no_dblbuf) +{ + int ret; + + host_stride = 320 * 2; + if (stride != 0) + *stride = host_stride; + + ret = vout_gp2x_init(no_dblbuf); + if (ret != 0) + return ret; + + host_video_flip(); + return 0; +} + +#endif + +static unsigned short host_pal[256]; + +static void host_update_pal(unsigned int *pal) +{ + unsigned short *dstp = host_pal; + int i; + + for (i = 0; i < 256; i++, pal++, dstp++) { + unsigned int t = *pal; + *dstp = ((t >> 8) & 0xf800) | ((t >> 5) & 0x07e0) | ((t >> 3) & 0x001f); + } +} + +void host_video_blit4(const unsigned char *src, int w, int h, unsigned int *pal) +{ + unsigned short *dst = host_screen; + unsigned short *hpal = host_pal; + int i, u; + + if (pal != NULL) + host_update_pal(pal); + + for (i = 0; i < 240; i++, dst += host_stride / 2 - 320) { + for (u = 320 / 2; u > 0; u--, src++) { + *dst++ = hpal[*src >> 4]; + *dst++ = hpal[*src & 0x0f]; + } + } + + host_video_flip(); } + +void host_video_blit8(const unsigned char *src, int w, int h, unsigned int *pal) +{ + unsigned short *dst = host_screen; + unsigned short *hpal = host_pal; + int i, u; + + if (pal != NULL) + host_update_pal(pal); + + for (i = 0; i < 240; i++, dst += host_stride / 2 - 320) { + for (u = 320 / 4; u > 0; u--) { + *dst++ = hpal[*src++]; + *dst++ = hpal[*src++]; + *dst++ = hpal[*src++]; + *dst++ = hpal[*src++]; + } + } + + host_video_flip(); +} + +void host_video_blit16(const unsigned short *src, int w, int h) +{ + unsigned short *dst = host_screen; + int i; + + for (i = 0; i < 240; i++, dst += host_stride / 2, src += 320) + memcpy(dst, src, 320*2); + + host_video_flip(); +} + diff --git a/common/host_fb.h b/common/host_fb.h index bd739d4..edb931b 100644 --- a/common/host_fb.h +++ b/common/host_fb.h @@ -1,3 +1,6 @@ int host_video_init(int *stride, int no_dblbuf); void *host_video_flip(void); +void host_video_blit4(const unsigned char *src, int w, int h, unsigned int *pal); +void host_video_blit8(const unsigned char *src, int w, int h, unsigned int *pal); +void host_video_blit16(const unsigned short *src, int w, int h); diff --git a/common/warm.c b/common/warm.c new file mode 100644 index 0000000..7cf22ac --- /dev/null +++ b/common/warm.c @@ -0,0 +1,254 @@ +/* + * wARM - exporting ARM processor specific privileged services to userspace + * userspace part + * + * Copyright (c) Gražvydas "notaz" Ignotas, 2009 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the organization nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define WARM_CODE +#include "warm.h" + +/* provided by glibc */ +extern long init_module(void *, unsigned long, const char *); +extern long delete_module(const char *, unsigned int); + +static int warm_fd = -1; +static int kernel_version; + +static void sys_cacheflush(void *start, void *end) +{ +#ifdef __ARM_EABI__ + /* EABI version */ + int num = __ARM_NR_cacheflush; + __asm__("mov r0, %0 ;" + "mov r1, %1 ;" + "mov r2, #0 ;" + "mov r7, %2 ;" + "swi 0" : : "r" (start), "r" (end), "r" (num) + : "r0", "r1", "r2", "r3", "r7"); +#else + /* OABI */ + __asm__("mov r0, %0 ;" + "mov r1, %1 ;" + "mov r2, #0 ;" + "swi %2" : : "r" (start), "r" (end), "i" __ARM_NR_cacheflush + : "r0", "r1", "r2", "r3"); +#endif +} + +/* Those are here because system() occasionaly fails on Wiz + * with errno 12 for some unknown reason */ +static int manual_insmod_26(const char *fname, const char *opts) +{ + unsigned long len, read_len; + int ret = -1; + void *buff; + FILE *f; + + f = fopen(fname, "rb"); + if (f == NULL) + return -1; + + fseek(f, 0, SEEK_END); + len = ftell(f); + fseek(f, 0, SEEK_SET); + + buff = malloc(len); + if (buff == NULL) + goto fail0; + + read_len = fread(buff, 1, len, f); + if (read_len != len) { + fprintf(stderr, "failed to read module\n"); + goto fail1; + } + + ret = init_module(buff, len, opts); + +fail1: + free(buff); +fail0: + fclose(f); + return ret; +} + +static int manual_rmmod(const char *name) +{ + return delete_module(name, O_NONBLOCK|O_EXCL); +} + +int warm_init(void) +{ + struct utsname unm; + char buff1[32], buff2[128]; + int ret; + + memset(&unm, 0, sizeof(unm)); + uname(&unm); + + if (strlen(unm.release) < 3 || unm.release[1] != '.') { + fprintf(stderr, "unexpected version string: %s\n", unm.release); + goto fail; + } + kernel_version = ((unm.release[0] - '0') << 4) | (unm.release[2] - '0'); + + warm_fd = open("/proc/warm", O_RDWR); + if (warm_fd >= 0) + return 0; + + snprintf(buff1, sizeof(buff1), "warm_%s.%s", unm.release, kernel_version >= 0x26 ? "ko" : "o"); + snprintf(buff2, sizeof(buff2), "/sbin/insmod %s verbose=1", buff1); + + /* try to insmod */ + ret = system(buff2); + if (ret != 0) { + fprintf(stderr, "system/insmod failed: %d %d\n", ret, errno); + if (kernel_version >= 0x26) { + ret = manual_insmod_26(buff1, "verbose=1"); + if (ret != 0) + fprintf(stderr, "manual insmod also failed: %d\n", ret); + } + } + + warm_fd = open("/proc/warm", O_RDWR); + if (warm_fd >= 0) + return 0; + +fail: + fprintf(stderr, "wARM: can't init, acting as sys_cacheflush wrapper\n"); + return -1; +} + +void warm_finish(void) +{ + char name[32], cmd[64]; + int ret; + + if (warm_fd < 0) + return; + + close(warm_fd); + warm_fd = -1; + + if (kernel_version < 0x26) { + struct utsname unm; + memset(&unm, 0, sizeof(unm)); + uname(&unm); + snprintf(name, sizeof(name), "warm_%s", unm.release); + } + else + strcpy(name, "warm"); + + snprintf(cmd, sizeof(cmd), "/sbin/rmmod %s", name); + ret = system(cmd); + if (ret != 0) { + fprintf(stderr, "system/rmmod failed: %d %d\n", ret, errno); + manual_rmmod(name); + } +} + +int warm_cache_op_range(int op, void *addr, unsigned long size) +{ + struct warm_cache_op wop; + int ret; + + if (warm_fd < 0) { + /* note that this won't work for warm_cache_op_all */ + sys_cacheflush(addr, (char *)addr + size); + return -1; + } + + wop.ops = op; + wop.addr = (unsigned long)addr; + wop.size = size; + + ret = ioctl(warm_fd, WARMC_CACHE_OP, &wop); + if (ret != 0) { + perror("WARMC_CACHE_OP failed"); + return -1; + } + + return 0; +} + +int warm_cache_op_all(int op) +{ + return warm_cache_op_range(op, NULL, (unsigned long)-1); +} + +int warm_change_cb_range(int cb, int is_set, void *addr, unsigned long size) +{ + struct warm_change_cb ccb; + int ret; + + if (warm_fd < 0) + return -1; + + ccb.addr = (unsigned long)addr; + ccb.size = size; + ccb.cb = cb; + ccb.is_set = is_set; + + ret = ioctl(warm_fd, WARMC_CHANGE_CB, &ccb); + if (ret != 0) { + perror("WARMC_CHANGE_CB failed"); + return -1; + } + + return 0; +} + +int warm_change_cb_upper(int cb, int is_set) +{ + return warm_change_cb_range(cb, is_set, 0, 0); +} + +unsigned long warm_virt2phys(const void *ptr) +{ + unsigned long ptrio; + int ret; + + ptrio = (unsigned long)ptr; + ret = ioctl(warm_fd, WARMC_VIRT2PHYS, &ptrio); + if (ret != 0) { + perror("WARMC_VIRT2PHYS failed"); + return (unsigned long)-1; + } + + return ptrio; +} + diff --git a/common/warm.h b/common/warm.h new file mode 100644 index 0000000..a3fdd6b --- /dev/null +++ b/common/warm.h @@ -0,0 +1,100 @@ +/* + * wARM - exporting ARM processor specific privileged services to userspace + * library functions + * + * Copyright (c) Gražvydas "notaz" Ignotas, 2009 + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * * Neither the name of the organization nor the + * names of its contributors may be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#ifndef __WARM_H__ +#define __WARM_H__ 1 + +/* cache operations (warm_cache_op_*): + * o clean - write dirty data to memory, but also leave in cache. + * o invalidate - throw away everything in cache, losing dirty data. + * + * Write buffer is always drained, no ops will only drain WB + */ +#define WOP_D_CLEAN (1 << 0) +#define WOP_D_INVALIDATE (1 << 1) +#define WOP_I_INVALIDATE (1 << 2) + +/* change C and B bits (warm_change_cb_*) + * if is_set in not zero, bits are set, else cleared. + * the address for range function is virtual address. + */ +#define WCB_C_BIT (1 << 0) +#define WCB_B_BIT (1 << 1) + +#ifndef __ASSEMBLER__ + +#ifdef __cplusplus +extern "C" +{ +#endif + +int warm_init(void); + +int warm_cache_op_range(int ops, void *virt_addr, unsigned long size); +int warm_cache_op_all(int ops); + +int warm_change_cb_upper(int cb, int is_set); +int warm_change_cb_range(int cb, int is_set, void *virt_addr, unsigned long size); + +unsigned long warm_virt2phys(const void *ptr); + +void warm_finish(void); + +#ifdef __cplusplus +} +#endif + +/* internal */ +#ifdef WARM_CODE + +#include + +#define WARM_IOCTL_BASE 'A' + +struct warm_cache_op +{ + unsigned long addr; + unsigned long size; + int ops; +}; + +struct warm_change_cb +{ + unsigned long addr; + unsigned long size; + int cb; + int is_set; +}; + +#define WARMC_CACHE_OP _IOW(WARM_IOCTL_BASE, 0, struct warm_cache_op) +#define WARMC_CHANGE_CB _IOW(WARM_IOCTL_BASE, 1, struct warm_change_cb) +#define WARMC_VIRT2PHYS _IOWR(WARM_IOCTL_BASE, 2, unsigned long) + +#endif /* WARM_CODE */ +#endif /* !__ASSEMBLER__ */ +#endif /* __WARM_H__ */ diff --git a/common/wiz_video.c b/common/wiz_video.c index 4395683..7996ee3 100644 --- a/common/wiz_video.c +++ b/common/wiz_video.c @@ -8,13 +8,15 @@ #include #include #include +#include "warm.h" static volatile unsigned short *memregs; static volatile unsigned long *memregl; -static int memdev = -1; +int memdev = -1; #define FB_BUF_COUNT 4 static unsigned int fb_paddr[FB_BUF_COUNT]; +static int fb_buf_count = FB_BUF_COUNT; static int fb_work_buf; static int fbdev = -1; @@ -28,12 +30,12 @@ static void vout_gp2x_flip(void) memregl[0x4058>>2] |= 0x10; fb_work_buf++; - if (fb_work_buf >= FB_BUF_COUNT) + if (fb_work_buf >= fb_buf_count) fb_work_buf = 0; g_screen_ptr = gp2x_screens[fb_work_buf]; } -static int vout_gp2x_init(void) +static int vout_gp2x_init(int no_dblbuf) { struct fb_fix_screeninfo fbfix; int i, ret; @@ -75,6 +77,13 @@ static int vout_gp2x_init(void) } memset(gp2x_screens[0], 0, 320*240*2*FB_BUF_COUNT); + if (!no_dblbuf) { + warm_init(); + ret = warm_change_cb_range(WCB_B_BIT, 1, gp2x_screens[0], 320*240*2*FB_BUF_COUNT); + if (ret != 0) + fprintf(stderr, "could not make fb buferable.\n"); + } + printf(" %p -> %08x\n", gp2x_screens[0], fb_paddr[0]); for (i = 1; i < FB_BUF_COUNT; i++) { @@ -85,6 +94,9 @@ static int vout_gp2x_init(void) fb_work_buf = 0; g_screen_ptr = gp2x_screens[0]; + if (no_dblbuf) + fb_buf_count = 1; + return 0; } @@ -96,5 +108,7 @@ void vout_gp2x_finish(void) munmap((void *)memregs, 0x20000); close(memdev); + + warm_finish(); } diff --git a/dist/ginge.gpe b/dist/ginge.gpe new file mode 100755 index 0000000..27521e5 --- /dev/null +++ b/dist/ginge.gpe @@ -0,0 +1,14 @@ +#!/bin/sh + +/sbin/rmmod warm 2> /dev/null +/sbin/insmod ./warm_2.6.24.ko + +# theoretically GP2X apps can make use of more RAM, because +# Wiz has 2.6 kernel (larger memory requirements) and larger +# reserved areas, so we mount some swap here just in case. +mkswap swapfile +swapon swapfile + +./gp2xmenu + +swapoff swapfile diff --git a/ginge.pxml b/dist/ginge.pxml similarity index 100% rename from ginge.pxml rename to dist/ginge.pxml diff --git a/dist/ginge.sh b/dist/ginge.sh new file mode 100755 index 0000000..66a99e1 --- /dev/null +++ b/dist/ginge.sh @@ -0,0 +1,14 @@ +#!/bin/sh + +export FBDEV=/dev/fb1 +ofbset -fb $FBDEV -pos 80 0 -size 640 480 -mem 614400 -en 1 +fbset -fb $FBDEV -g 320 240 320 480 16 + +# make ir runnable from ssh +if [ -z "$DISPLAY" ]; then +export DISPLAY=:0 +fi + +./gp2xmenu + +ofbset -fb $FBDEV -pos 0 0 -size 0 0 -mem 0 -en 0 diff --git a/dist/ginge32.png b/dist/ginge32.png new file mode 100644 index 0000000000000000000000000000000000000000..5bc2a4f39e2381d7d6f9c36621303015a0384fd4 GIT binary patch literal 1825 zcmV++2j2LJP)QVB&uh>xnJQdI<~{J{@=AX_ntOyhudCIj?9^2M+zj>%@JXNdDyJ3G5ryk&sO+7%{we^a9SFo9@*80P%RZ ziyIQyO}w+-_WkURQ9SV@2MYJdZ&>geWmHVy&-1(S&rgnY8YX_UxqU{JgL;Q5T(Srj680-q@ zq%z`)1XWWIMB~Wz#E^{m+>s~-gSm}-{5Il+wFI^i&u%nQ(^RN&6-dO89^VeH>2WOy zB(5KT=QiPa61UOaOrSkQjIAW_R46p-whI^MP_lJ6O&?9mhab3*G#QP;BFY!)fa>9r zoZk!qaRk3vCXgf^-OMPJVbgN+2+<=P zZy)iCje5soF(h;y)0Zk38_rRE0W+K*Q!`J%}%WOg5Q3nhYa#uI*QR=w!(SF#z1XIzm z;6!m~M#YDRkD-GS-X(r&ki_69lqr-v{9 zr4m;cg+lAL%@xV>_~m|y6H5qIsd>=yXHE0)&Odw@&ItP$NT<^{Q;ZMxLvUFSKOql~~Kb$&!2L9+Mx02$bDD(>jp6#Gs+rzV8YN;Vb zfMgi|N)D1c2RnHkHRA2dy3R3<*VJRK|titUG2Uu6pI)d8p6cHji}Zg z_zsPodzps0^41nCZ-4lgd?Z()A0$XTKzDaHrl+UZ3&jON;Hyq~-=hOJ9gF5;QucwviYekitBDn#i{!(A(RK zo}QjoDlQf-K@(@bG7d$23O21@54My*t~cN6Q7V-%KR@3|gsQ}~%QO9f_*EyG;!K58 z2@-y={*q+&5kczm`0DlgdR_lY>|Z7zijb3pzOu*7H|#>aQldB8_<>r3rfaRZ33#pn zhGq`Q;vCqdV~2 z&?%(4sA$X^n!c3=e8^F+<(x1pIC`-Ui)t44WLWM+)YdWhu0h{qrBp4$sRt-Gi%9Lg z1^+s@Dir?|E_hW%^g?*4xSXmO_slztxD2#R3QB~eeHX4lucm#YV&m^~IlTEr36%!F zu@3PK<^`b-j?8f?BB=;^^D*R;t4hyfD~q3%1Z?6lQFn-k)6=t<_WGb$OAILo4y83v znfXlqpD*Bua~&p+p1{oP1$HQ*%h<2C-dTgljhm z4=&5K`KFB5=EiXR)VHErAuK|^*v!RbdsrdwjmP7-cE>Ps8NC%wi{%Es`R*K(lf7zy zh>{z2kD)KC^S&UVVmSC_Y6gWu5#e26zfkzAxuJ==jk*itD(-}9!AynML?r(&m(%Ns zKMLKx3d7>8-ykM7nq0aHB3r_%`M!`;--=4Q9ugD*|54~i7dJEMe*_o+(dpKSUj)ge P00000NkvXXu0mjf3NmiL literal 0 HcmV?d00001 diff --git a/ginge.png b/dist/ginge60.png similarity index 100% rename from ginge.png rename to dist/ginge60.png diff --git a/loader/ginge_dyn.sh b/dist/ginge_dyn_eabi.sh similarity index 100% rename from loader/ginge_dyn.sh rename to dist/ginge_dyn_eabi.sh diff --git a/dist/ginge_dyn_oabi.sh b/dist/ginge_dyn_oabi.sh new file mode 100755 index 0000000..180d0da --- /dev/null +++ b/dist/ginge_dyn_oabi.sh @@ -0,0 +1,11 @@ +#!/bin/sh + +root=$1 +shift + +#export LD_LIBRARY_PATH="$LD_LIBRARY_PATH:${root}lib" +export LD_PRELOAD="${root}ginge_dyn" + +export GINGE_ROOT="${root}" + +exec "$@" diff --git a/dist/make_cmn.sh b/dist/make_cmn.sh new file mode 100755 index 0000000..486f949 --- /dev/null +++ b/dist/make_cmn.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +set -e + +if [ -z "$1" ]; then + echo "usage: $0 " + exit 1 +fi + +rm -rf $1 +mkdir $1 +cp gp2xmenu/gp2xmenu $1/ +cp -r gp2xmenu/gp2xmenu_data $1/ +cp prep/ginge_prep $1/ +cp loader/ginge_dyn $1/ +cp loader/ginge_sloader $1/ +cp readme.txt $1/ + diff --git a/loader/.gitignore b/loader/.gitignore deleted file mode 100644 index 5761abc..0000000 --- a/loader/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.o diff --git a/loader/Makefile b/loader/Makefile index ed9d113..0a28670 100644 --- a/loader/Makefile +++ b/loader/Makefile @@ -22,20 +22,28 @@ ifeq "$(ARCH)" "arm" ASFLAGS += -mfloat-abi=soft OBJ += sys_cacheflush.o emu_arm.o endif +ifdef PND +CFLAGS += -DPND +OBJ += host_pnd.o +endif +ifdef WIZ +CFLAGS += -DWIZ +OBJ += host_wiz.o +endif vpath %.c = ../common/ TARGET_S = ginge_sloader TARGET_D = ginge_dyn -OBJ += emu.o host_fb.o host_pnd.o cmn.o +OBJ += emu.o host_fb.o cmn.o OBJ_S += $(OBJ) loader.o loader_$(ARCH).o patches.o OBJ_D += $(OBJ) dl.o all: $(TARGET_S) $(TARGET_D) $(TARGET_S): LDFLAGS += -Wl,-T script_$(ARCH).lds -$(TARGET_D): LDFLAGS += -ldl +$(TARGET_D): LDFLAGS += -ldl -Wl,--version-script=$(TARGET_D).symver $(TARGET_S): $(OBJ_S) $(CC) -o $@ $^ -static $(LDFLAGS) diff --git a/loader/dl.c b/loader/dl.c index 154e012..468a2e0 100644 --- a/loader/dl.c +++ b/loader/dl.c @@ -9,6 +9,14 @@ #define DL #include "override.c" +static void next_line(FILE *f) +{ + int c; + do { + c = fgetc(f); + } while (c != EOF && c != '\n'); +} + __attribute__((constructor)) static void ginge_init(void) { @@ -32,7 +40,7 @@ static void ginge_init(void) exit(1); } - ret = fscanf(f, "%x-%x %*s %*s %*s %*s %*s\n", &start, &end); + ret = fscanf(f, "%x-%x ", &start, &end); if (ret != 2) { perror("parse maps"); exit(1); @@ -46,6 +54,8 @@ static void ginge_init(void) perror("warning: mprotect"); while (1) { + next_line(f); + ret = fscanf(f, "%x-%*s %*s %*s %*s %*s %*s\n", &start); if (ret <= 0) break; @@ -62,6 +72,10 @@ static void ginge_init(void) #endif fclose(f); + // remove self from preload, further commands (from system() and such) + // will be handled by ginge_prep. + unsetenv("LD_PRELOAD"); + emu_init((void *)lowest_segment); } diff --git a/loader/emu.c b/loader/emu.c index f161795..3ab4b34 100644 --- a/loader/emu.c +++ b/loader/emu.c @@ -103,15 +103,11 @@ static struct { // state void *umem; - u16 host_pal[256]; u32 old_mlc_stl_adr; u32 btn_state; // as seen through /dev/GPIO u32 dirty_pal:1; } mmsp2; -static u16 *host_screen; -static int host_stride; - #if defined(LOG_IO) || defined(LOG_IO_UNK) static void log_io(const char *pfx, u32 a, u32 d, int size) @@ -270,56 +266,34 @@ bad_blit: dump_blitter(); } -// TODO: hw scaler stuff -static void mlc_flip(u8 *src, int bpp) +// FIXME: pass real dimensions to blitters +static void mlc_flip(void *src, int bpp) { - u16 *dst = host_screen; - u16 *hpal = mmsp2.host_pal; - int i, u; + u32 *srcp = NULL; + // only pass pal to host if it's dirty if (bpp <= 8 && mmsp2.dirty_pal) { - u32 *srcp = mmsp2.mlc_stl_pallt_d32; - u16 *dstp = hpal; - - for (i = 0; i < 256; i++, srcp++, dstp++) { - u32 t = *srcp; - *dstp = ((t >> 8) & 0xf800) | ((t >> 5) & 0x07e0) | ((t >> 3) & 0x001f); - } + srcp = mmsp2.mlc_stl_pallt_d32; mmsp2.dirty_pal = 0; } switch (bpp) { case 4: - for (i = 0; i < 240; i++, dst += host_stride / 2 - 320) { - for (u = 320 / 2; u > 0; u--, src++) { - *dst++ = hpal[*src >> 4]; - *dst++ = hpal[*src & 0x0f]; - } - } + host_video_blit4(src, 320, 240, srcp); break; case 8: - for (i = 0; i < 240; i++, dst += host_stride / 2 - 320) { - for (u = 320 / 4; u > 0; u--) { - *dst++ = hpal[*src++]; - *dst++ = hpal[*src++]; - *dst++ = hpal[*src++]; - *dst++ = hpal[*src++]; - } - } + host_video_blit8(src, 320, 240, srcp); break; case 16: - for (i = 0; i < 240; i++, dst += host_stride / 2, src += 320*2) - memcpy(dst, src, 320*2); + host_video_blit16(src, 320, 240); break; case 24: // TODO break; } - - host_screen = host_video_flip(); } #define ts_add_nsec(ts, ns) { \ @@ -372,11 +346,11 @@ static void *fb_sync_thread(void *arg) ts_add_nsec(ts, 50000000); manual_refresh++; if (manual_refresh == 2) - log("fb_thread: switch to manual refresh\n"); + dbg("fb_thread: switch to manual refresh\n"); } else { ts_add_nsec(ts, 16666667); if (manual_refresh > 1) - log("fb_thread: switch to auto refresh\n"); + dbg("fb_thread: switch to auto refresh\n"); manual_refresh = 0; } @@ -747,7 +721,7 @@ static void segv_sigaction(int num, siginfo_t *info, void *ctx) // real crash - time to die err("segv %d %p @ %08x\n", info->si_code, info->si_addr, regs[15]); for (i = 0; i < 8; i++) - err(" r%d=%08x r%2d=%08x\n", i, regs[i], i+8, regs[i+8]); + dbg(" r%d=%08x r%2d=%08x\n", i, regs[i], i+8, regs[i+8]); signal(num, SIG_DFL); raise(num); return; @@ -804,7 +778,7 @@ void emu_init(void *map_bottom) g_linkpage = (void *)(((u32)map_bottom - LINKPAGE_ALLOC) & ~0xfff); pret = mmap(g_linkpage, LINKPAGE_ALLOC, PROT_READ|PROT_WRITE, - MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); + MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0); if (pret != g_linkpage) { perror(PFX "mmap linkpage"); exit(1); @@ -812,20 +786,31 @@ void emu_init(void *map_bottom) log("linkpages @ %p\n", g_linkpage); init_linkpage(); - mmsp2.umem = mmap(NULL, 0x2000000, PROT_READ|PROT_WRITE|PROT_EXEC, - MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); - if (mmsp2.umem == MAP_FAILED) { - perror(PFX "mmap upper mem"); + // host stuff + ret = host_init(); + if (ret != 0) { + err("can't init host\n"); exit(1); } - // host stuff - ret = host_video_init(&host_stride, 0); + ret = host_video_init(NULL, 0); if (ret != 0) { - err("can't init video\n"); + err("can't init host video\n"); + exit(1); + } + +#ifdef WIZ + // we are short on memmory on Wiz, need special handling + extern void *host_mmap_upper(void); + mmsp2.umem = host_mmap_upper(); +#else + mmsp2.umem = mmap(NULL, 0x2000000, PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_PRIVATE|MAP_ANONYMOUS, -1, 0); +#endif + if (mmsp2.umem == MAP_FAILED) { + perror(PFX "mmap upper mem"); exit(1); } - host_screen = host_video_flip(); ret = pthread_create(&tid, NULL, fb_sync_thread, NULL); if (ret != 0) { @@ -844,16 +829,16 @@ void emu_init(void *map_bottom) int emu_read_gpiodev(void *buf, int count) { - unsigned int btns; - - if (count < 4) { + if (count <= 0) { err("gpiodev read %d?\n", count); return -1; } + if (count > 4) + count = 4; - btns = host_read_btns(); - memcpy(buf, &btns, 4); - return 4; + mmsp2.btn_state = host_read_btns(); + memcpy(buf, &mmsp2.btn_state, count); + return count; } struct dev_fd_t emu_interesting_fds[] = { diff --git a/loader/ginge_dyn.symver b/loader/ginge_dyn.symver new file mode 100644 index 0000000..c0c70c1 --- /dev/null +++ b/loader/ginge_dyn.symver @@ -0,0 +1,16 @@ +{ +global: + open; + fopen; + mmap; + mmap2; + read; + ioctl; + sigaction; + tcgetattr; + tcsetattr; + system; + +local: + *; +}; diff --git a/loader/header.h b/loader/header.h index 34db74f..752dd03 100644 --- a/loader/header.h +++ b/loader/header.h @@ -46,6 +46,7 @@ int emu_read_gpiodev(void *buf, int count); void *emu_do_fopen(const char *path, const char *mode); int emu_do_system(const char *command); +int host_init(void); int host_read_btns(void); enum { GP2X_UP = 0, GP2X_LEFT = 2, GP2X_DOWN = 4, GP2X_RIGHT = 6, diff --git a/loader/host_pnd.c b/loader/host_pnd.c index 5c79b91..9064047 100644 --- a/loader/host_pnd.c +++ b/loader/host_pnd.c @@ -14,17 +14,16 @@ #include "realfuncs.h" static int ifds[2] = { -1, -1 }; -static int init_done; static int keystate; -static void init(void) +int host_init(void) { char buff[64]; int i, ifd, ret; for (ifd = -1, i = 0; ifds[0] == -1 || ifds[1] == -1; i++) { snprintf(buff, sizeof(buff), "/dev/input/event%i", i); - ifd = open(buff, O_RDONLY | O_NONBLOCK, 0); + ifd = open(buff, O_RDONLY | O_NONBLOCK); if (ifd == -1) break; @@ -47,7 +46,8 @@ static void init(void) fprintf(stderr, PFX "missing buttons\n"); if (ifds[1] < 0) fprintf(stderr, PFX "missing keypad\n"); - init_done = 1; + + return 0; } static const struct { @@ -84,9 +84,6 @@ int host_read_btns(void) struct input_event ev; int i, ret; - if (!init_done) - init(); - while (1) { ret = read(ifds[0], &ev, sizeof(ev)); diff --git a/loader/host_wiz.c b/loader/host_wiz.c new file mode 100644 index 0000000..dccd841 --- /dev/null +++ b/loader/host_wiz.c @@ -0,0 +1,83 @@ +// vim:shiftwidth=2:expandtab +#include +#include +#include +#include +#include + +#include "header.h" +#include "../common/warm.h" +#include "realfuncs.h" + +static int gpiodev = -1; +extern int memdev; // leasing from wiz_video + +int host_init(void) +{ + gpiodev = open("/dev/GPIO", O_RDONLY); + if (gpiodev < 0) + perror(PFX "couldn't open /dev/GPIO"); + + return 0; +} + +int host_read_btns(void) +{ + int r, value = 0; + + r = read(gpiodev, &value, 4); + if (value & 0x02) + value |= 0x05; + if (value & 0x08) + value |= 0x14; + if (value & 0x20) + value |= 0x50; + if (value & 0x80) + value |= 0x41; + + return value; +} + +void *host_mmap_upper(void) +{ + void *ret; + int r; + + // Wiz GP2X + // 03460000-03ffffff 00ba0000 + // 02aa0000-02dfffff 03100000-0345ffff 00360000 + // 03000000-030fffff 00100000 + // 03000000-03ffffff 02000000-02ffffff 01000000 + ret = mmap((void *)0x82000000, 0x1000000, PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_SHARED|MAP_FIXED, memdev, 0x3000000); + if (ret != (void *)0x82000000) + goto fail; + + ret = mmap((void *)0x83000000, 0x100000, PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0); + if (ret != (void *)0x83000000) + goto fail; + + ret = mmap((void *)0x83100000, 0x360000, PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_SHARED|MAP_FIXED, memdev, 0x2aa0000); + if (ret != (void *)0x83100000) + goto fail; + + ret = mmap((void *)0x83460000, 0xba0000, PROT_READ|PROT_WRITE|PROT_EXEC, + MAP_PRIVATE|MAP_ANONYMOUS|MAP_FIXED, -1, 0); + if (ret != (void *)0x83460000) + goto fail; + + r = warm_change_cb_range(WCB_B_BIT|WCB_C_BIT, 1, (void *)0x82000000, 0x1000000); + r |= warm_change_cb_range(WCB_B_BIT|WCB_C_BIT, 1, (void *)0x83100000, 0x360000); + if (r != 0) + err("could not make upper mem cacheable.\n"); + + return (void *)0x82000000; + +fail: + err("mmap %p: ", ret); + perror(NULL); + exit(1); +} + diff --git a/loader/loader.c b/loader/loader.c index 28bdf8e..fea8bb6 100644 --- a/loader/loader.c +++ b/loader/loader.c @@ -125,7 +125,7 @@ int main(int argc, char *argv[]) return 1; } - printf("load %d %08x-%08x from %08x\n", phdr[i].p_type, + log("load %d %08x-%08x from %08x\n", phdr[i].p_type, phdr[i].p_vaddr, end_addr, phdr[i].p_offset); align = phdr[i].p_vaddr & 0xfff; @@ -170,7 +170,7 @@ int main(int argc, char *argv[]) stack_frame[sfp++] = (long)environ[i]; stack_frame[sfp++] = 0; - printf("entering %08x, %d stack entries\n", hdr.e_entry, sfp); + log("entering %08x, %d stack entries\n", hdr.e_entry, sfp); do_entry(hdr.e_entry, stack_frame, sfp, NULL); fprintf(stderr, "do_entry failed!\n"); diff --git a/loader/override.c b/loader/override.c index 5a10193..42b52ab 100644 --- a/loader/override.c +++ b/loader/override.c @@ -1,6 +1,7 @@ // vim:shiftwidth=2:expandtab #include #include +#include #include #include #include @@ -167,6 +168,7 @@ static UNUSED int w_system(const char *command) /* wrapper to real functions, to be set up on load */ \ static typeof(sym) *p_real_##sym +// note: update symver too MAKE_WRAP_SYM(open); MAKE_WRAP_SYM(fopen); MAKE_WRAP_SYM(mmap); @@ -212,8 +214,14 @@ static const struct { #endif // just call real funcs for static, pointer for dynamic -int real_open(const char *pathname, int flags, mode_t mode) +int real_open(const char *pathname, int flags, ...) { + va_list ap; + mode_t mode; + + va_start(ap, flags); + mode = va_arg(ap, mode_t); + va_end(ap); return open(pathname, flags, mode); } diff --git a/loader/realfuncs.h b/loader/realfuncs.h index 4bca950..bda599a 100644 --- a/loader/realfuncs.h +++ b/loader/realfuncs.h @@ -10,7 +10,7 @@ #include -int real_open(const char *pathname, int flags, mode_t mode); +int real_open(const char *pathname, int flags, ...); FILE *real_fopen(const char *path, const char *mode); void *real_mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); int real_read(int fd, void *buf, size_t count); diff --git a/make_pnd.sh b/make_pnd.sh index 8970d38..8a0fab0 100755 --- a/make_pnd.sh +++ b/make_pnd.sh @@ -4,15 +4,11 @@ pnd_make=$HOME/dev/pnd/src/pandora-libraries/testdata/scripts/pnd_make.sh set -e -rm -rf out -mkdir out -cp gp2xmenu/run.sh out/ginge.sh -cp gp2xmenu/gp2xmenu out/ -cp -r gp2xmenu/gp2xmenu_data out/ -cp prep/ginge_prep out/ -cp loader/ginge_* out/ -cp -r tools out/ -cp -r lib out/ -cp readme.txt out/ - -$pnd_make -p ginge.pnd -d out -x ginge.pxml -c -i ginge.png +dist/make_cmn.sh out_pnd +mkdir -p out_pnd/tools +cp dist/ginge.sh out_pnd/ +cp dist/ginge_dyn_eabi.sh out_pnd/ginge_dyn.sh +cp tools/cramfsck_eabi out_pnd/tools/cramfsck +cp -r lib out_pnd/ + +$pnd_make -p ginge.pnd -d out_pnd -x dist/ginge.pxml -c -i dist/ginge60.png diff --git a/make_wiz.sh b/make_wiz.sh new file mode 100755 index 0000000..9176fc2 --- /dev/null +++ b/make_wiz.sh @@ -0,0 +1,12 @@ +#!/bin/sh + +set -e + +dist/make_cmn.sh out_wiz +mkdir -p out_wiz/tools +cp dist/ginge.gpe out_wiz/ +cp dist/ginge32.png out_wiz/ginge.png +cp dist/ginge_dyn_oabi.sh out_wiz/ginge_dyn.sh +cp tools/cramfsck_oabi out_wiz/tools/cramfsck + +dd if=/dev/zero of=out_wiz/swapfile bs=1M count=16 diff --git a/prep/Makefile b/prep/Makefile index 368dc0e..bf9c95c 100644 --- a/prep/Makefile +++ b/prep/Makefile @@ -1,11 +1,17 @@ CC = $(CROSS_COMPILE)gcc CFLAGS += -Wall -O2 LDFLAGS = -s -O2 +ifdef WIZ +CFLAGS += -DWIZ +endif +ifdef PND +CFLAGS += -DPND +endif vpath %.c = ../common/ TARGET = ginge_prep -OBJS = main.o host_fb.o cmn.o +OBJS += main.o host_fb.o cmn.o all: $(TARGET) diff --git a/prep/main.c b/prep/main.c index 7cc4337..8acbef7 100644 --- a/prep/main.c +++ b/prep/main.c @@ -16,11 +16,18 @@ #define LOADER_DYNAMIC "ginge_dyn.sh" #define LAUNCHER "gp2xmenu" +#ifdef PND +#define WRAP_APP "op_runfbapp " +#else +#define WRAP_APP "" +#endif + #include "font.c" static void *fb_mem; static int fb_stride; static int fb_x, fb_y; +static int init_done; static char *sskip(char *p) { @@ -36,6 +43,16 @@ static char *cskip(char *p) return p; } +static void fb_text_init(void) +{ + int ret = host_video_init(&fb_stride, 1); + if (ret == 0) + fb_mem = host_video_flip(); + fb_x = 4; + fb_y = 4; + init_done = 1; +} + static void fb_syms_out(void *fbi, int x, int y, int dotsz, int stride, const char *text, int count) { int v = -1, n = 0, *p; @@ -72,6 +89,9 @@ static void fb_text_out(char *text) char *p, *pe; int l; + if (!init_done) + fb_text_init(); + if (fb_mem == NULL) return; @@ -98,15 +118,6 @@ static void fb_text_out(char *text) } } -static void fb_text_init(void) -{ - int ret = host_video_init(&fb_stride, 1); - if (ret == 0) - fb_mem = host_video_flip(); - fb_x = 4; - fb_y = 4; -} - static void fbprintf(int is_err, const char *format, ...) { va_list ap; @@ -242,8 +253,6 @@ int main(int argc, char *argv[]) FILE *fin, *fout; int ret; - fb_text_init(); - if (argc < 2) { err("usage: %s [args]\n", argv[0]); return 1; @@ -281,13 +290,13 @@ int main(int argc, char *argv[]) break; case 1: - fprintf(fout, "op_runfbapp %s%s ", root_path, LOADER_STATIC); + fprintf(fout, WRAP_APP "%s%s ", root_path, LOADER_STATIC); dump_args(fout, argc - 1, &argv[1]); fprintf(fout, "\n"); goto no_in_script; case 2: - fprintf(fout, "op_runfbapp %s%s \"%s\" ", root_path, LOADER_DYNAMIC, root_path); + fprintf(fout, WRAP_APP "%s%s \"%s\" ", root_path, LOADER_DYNAMIC, root_path); dump_args(fout, argc - 1, &argv[1]); fprintf(fout, "\n"); goto no_in_script; @@ -385,12 +394,12 @@ int main(int argc, char *argv[]) switch (ret) { case 1: printf(PFX "prefixing as static: %s", p); - fprintf(fout, "op_runfbapp %s%s ", root_path, LOADER_STATIC); + fprintf(fout, WRAP_APP "%s%s ", root_path, LOADER_STATIC); break; case 2: printf(PFX "prefixing as dynamic: %s", p); - fprintf(fout, "op_runfbapp %s%s \"%s\" ", root_path, LOADER_DYNAMIC, root_path); + fprintf(fout, WRAP_APP "%s%s \"%s\" ", root_path, LOADER_DYNAMIC, root_path); break; default: @@ -412,10 +421,13 @@ no_in_script: fclose(fout); - msg("starting script..\n"); - if (have_cramfs) - msg("\nsome files need to be unpacked, this may tike a few minutes.\n" - "Please wait at least while SD LED is active.\n"); + //msg("starting script..\n"); + if (have_cramfs) { + msg("\nsome files need to be unpacked, this may tike a few minutes.\n"); +#ifdef PND + msg("Please wait at least while SD LED is active.\n"); +#endif + } system("echo ---; cat /tmp/ginge_conv.sh; echo ---"); chmod(out_script, S_IRWXU|S_IRWXG|S_IRWXO); chdir(cwd); -- 2.39.5