[LINUX] Let's analyze a Chinese retro game machine

Overview

I bought a Chinese retro game machine, which has become a hot topic in some areas, so I analyzed it. It's like a memo for me who is very forgetful.

I am wondering what the goal of the analysis should be, but I would like to show you how to build an application development environment for Chinese retro game machines.

Then, I think that a lot of people with a lot of ideas will make something good.

Image file

obtain

When the power was turned on, the RetroFW screen was displayed. If you google this as a keyword, you will find RetroFW immediately.

Download the release file RetroFW_v1.2.zip and take a look at the contents.

Contents of the handout

When you unzip the zip file, RetroFW.img, batch file, dd.exe, and various uBoot.bin and uImage.bin appear in the subfolders.

What you can see from the batch file

If you look inside the batch file, you can see that the process of writing uBoot.bin and uImage.bin to a specific location in RetroFW.img using dd.exe is written. For example, RetroGame_v1.0_S_B.bat is as follows.

bat:RetroGame_v1.0_S_B.bat


dd if=kernel/RetroGame_v1.0_S_B.uBoot.bin  of=RetroFW.img conv=notrunc bs=512 seek=1 && dd if=kernel/RetroGame_v1.0_S_B.uImage.bin of=RetroFW.img conv=notrunc bs=1024 seek=4096

It can be seen that uBoot.bin is written from the position offset by 512 bytes from the beginning of the file, and uImage.bin is written from the position offset by 4 Mbyte from the beginning of the file.

U-Boot starts operating at the initial stage immediately after the power is turned on, and boots the OS after initializing the hardware. It is a so-called boot loader that performs.

Since this is written at a position offset by 512 bytes from the beginning of the file, the chip used in this Chinese retro game machine reads from the position offset by 512 bytes from the beginning of the microSD (temporarily set as LBA 1) into RAM and jumps. You can imagine that it has a mechanism.

Furthermore, it can be imagined that this U-Boot is written to read uImage.bin, which is located at a position offset by 4 Mbytes from the beginning of the microSD, into RAM and jump.

What you can see from the image file

Looking at RetroFW.img, it looks like the first 512 bytes are the MBR.

Extracting only the easy-to-understand part of the partition table and making it a table is as follows.

Partition No. First sector(LBA) Number of sectors Partition type
#1 0x0000_4000 0x0004_0000 Linux
#2 0x0004_4000 0x0008_0000 Linux swap
#3 0x000c_4000 0x0013_c000 FAT32
#4 - - Free

If you look at RetroFW.img with a binary editor according to the above, you can see that the first partition (from offset 0x0080_0000) is rootfs and the second partition (from offset 0x0800_0000) is swap.

However, the third partition (from offset 0x1800_0000) is filled with 0s for some time. In this case, if you try to mount from Linux, it will probably fail, and it is thought that the operation in case of failure is pre-loaded.

Specifically, if FAT32 fails to mount, mkfs is performed. If you have a slightly more nifty design, it is possible to extend the FAT32 area to the end of the microSD and then mkfs (raspbian. ) Because something is so). You can actually check the movement of this.

Execution code is supposed to be placed in the loader part from offset 0 of MBR, and something is actually written, but it is difficult to judge whether it is the same with this chip.

For the time being, try disassemble the first 4 bytes, FA B8 00 10 ...

00000000 1000b8fa    b loc_fffee000

So, it is a relative jump command and the jump destination seems to be in the minus direction. Don't feel like this isn't done. Instead of digging in any further, you should try writing a random value and see what happens.

Find out more using Linux

I've searched with a binary editor so far, but I found that it's easier to check with a Linux machine.

$ fdisk -l ./RetroFW.img
Disk ./RetroFW.img: 1 GiB, 1073741824 bytes, 2097152 sectors
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 512 bytes
I/O size (minimum/optimal): 512 bytes / 512 bytes
Disklabel type: dos
Disk identifier: 0xbb005712

Device         Boot  Start     End Sectors  Size Id Type
./RetroFW.img1       16384  278527  262144  128M 83 Linux
./RetroFW.img2      278528  802815  524288  256M 82 Linux swap / Solaris
./RetroFW.img3      802816 2097151 1294336  632M  c W95 FAT32 (LBA)

As I checked. It's a big deal, so I'll go a little further.

$ sudo mount -t ext4 -o ro,loop,offset=8388608 RetroFW.img /mnt

$ file /mnt/bin/busybox 
/mnt/bin/busybox: setuid ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), dynamically linked, interpreter /lib/ld-uClibc.so.0, stripped

$ readelf -a /mnt/bin/busybox
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           MIPS R3000
  Version:                           0x1
  Entry point address:               0x402fb0
  Start of program headers:          52 (bytes into file)
  Start of section headers:          800132 (bytes into file)
  Flags:                             0x50001007, noreorder, pic, cpic, o32, mips32
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         9
  Size of section headers:           40 (bytes)
  Number of section headers:         26
  Section header string table index: 25
      :
Attribute Section: gnu
File Attributes
  Tag_GNU_MIPS_ABI_FP: Hard float (double precision)

It is a binary of MIPS32 and seems to use a chip equipped with FPU.

There is information that a chip called JZ4760 is used when googled with the product name of the Chinese retro game machine that I actually purchased Ingenic Semiconductor --Wikipedia If you look at, you can get information with FPU with MIPS32 rev1.

You can also see that the binaries are built to use uClibc.

U-Boot version

If you look at the U-Boot binaries with a hex editor, you can see that the versions used are as follows.

text:RetroGame_v1.0_S_B.uBoot.bin


U-Boot 1.1.6 (Jul 26 2018 - 14:28:08)

When it comes to 1.1.6, I think it's from around 2006, and I'm using a very old one. Well, since it is a boot loader, can it be old?

The current release version seems to be v2019.07, but I can't find an implementation for jz4760 [Implementation of jz4780](https://gitlab.denx.de/u-boot/u-boot/tree/master/arch/ mips / mach-jz47xx) It seems that there is only one.

text:RetroGame_v1.0_S_B.uBoot.bin


Board: Ingenic LEPUS (CPU Speed %d MHz)

I also found the character string. LEPUS seems to be the name of the reference design board, so if you search for this as a keyword, you may find an implementation for jz4760.

For the time being, I found something like u-boot-1.1.6-jz-20120904-r1819.patch.gz. It includes a solid implementation for the jz4760. The implementation of the part that enables RAM may include parameters derived from board constants. The key is whether the board is designed according to the reference design.

Linux version

Similarly, if you look at the uImage binary with a binary editor, you can see that the version used is as follows.

text:RetroGame_v1.0_S_B.uImage.bin


Linux-2.6.31.3

Maybe it's around 2009, I'm using an old one.

The current stable version seems to be 5.2.14, but I can't find any implementation for jz4760 again [Implementation of jz4740, jz4770, jz4780](https://git.kernel.org/pub/scm/linux/kernel/ There seems to be git / stable / linux.git / tree / arch / mips / jz4740? H = v5.2.14). It was written that the difference from jz4740 is the clock frequency, the presence or absence of FPU, and the presence or absence of GPU, so it may work with the build for jz4740 (really?).

This is also GPL, so if you look for it, you may find an implementation for jz4760.

For the time being, I found something like linux-2.6.31.3-jz-20120904-r1448.patch.gz. It includes a solid implementation for the jz4760.

Standard development environment

When I searched for the standard development environment for RetroFW, I found it quickly.

[Tutorial](https: // redirect.) Written in Topic: RetroFW --Developer Support Thread. viglink.com/?format=go&jsonp=vglnk_156881591905714&key=5150cb74f98acee214af0b53b6829d41&libId=k0pcfxgc0102ljt2000DAwfv3cbhjannx&loc=https%3A%2F%2Fboards.dingoonity.com=https%3A%2F%2Fboards.dingoonity. % 2Fdocs.google.com% 2Fdocument% 2Fd% 2F19kJXO3EZ8XCoeporuUUgV_S93AaPbSagza3sAgBILu8%2Fedit%3Fusp%3Dsharing&title=RetroFW%20-%20Developer%20Support%20Thread%20%20Developer%20Support%20Thread%20%20Support%20Thread%20%7C % 20Configuring% 20a% 20Toolchain% 20for% 20RetroFW% 20Development), it was easy to make a sample IPK.

Key points of building a development environment

The main points written in the tutorial are as follows.

For the tutorial, I used VirtualBox to prepare the operating environment of Ubuntu, but I tried Windows Subsystem for Linux. When I tried using Ubuntu (/ wiki / Windows_Subsystem_for_Linux), I was able to build it without any problems except for some parts. The explanation of the introduction of WSL is omitted.

Next, install the package for building BuildRoot on Ubuntu of WSL. As follows.

$ sudo apt install build-essential libncurses5 libncurses5-dev git python unzip bc

Next, get BuildRoot and build.

The BuildRoot version is specified to use 2018.02.9, and it is stated not to use it even if you notice that a new version has been released (because it is sensitive to the toolchain version).

$ wget https://buildroot.org/downloads/buildroot-2018.02.9.tar.gz
$ tar -xzvf buildroot-2018.02.9.tar.gz
$ cd buildroot-2018.02.9
$ make menuconfig

The buildroot configuration is described as follows.

Target Options
    Target Architecture - MIPS (little endian)
    Disable soft-float

Toolchain
    C library (uClibc-ng)
    Enable WCHAR support
    Enable C++ support

Target Packages
    Graphic libraries and applications (graphic/text)
        SDL
        SDL_gfx
        SDL_image
        SDL_mixer
        SDL_net
        SDL_sound
        SDL_TTF

All you have to do is build. It takes a lot of time to build the entire tool chain (cross compiler).

$ export FORCE_UNSAFE_CONFIGURE=1
$ make

I make rootfs during make, but in WSL this seems to be an error (what fakeroot is, what faked is). This time, it is only necessary to build a development environment, and rootfs is not used, so it is ignored. When I tried the same procedure with VirtualBox + Ubuntu 18.04.3 LTS, the build was completed without any error.

After this, depending on your preference, it is written to copy the toolchain to the specified directory and put it in your PATH.

$ mkdir /opt/rs97tools
$ cp -R output/host/* /opt/rs97tools/
$ export PATH=/opt/rs97tools/mipsel-buildroot-linux-uclibc/sysroot/usr/bin:$PATH
$ export PATH=/opt/rs97tools/bin:$PATH

It would be nice if mipsel-linux-gcc and sdl-config --libs could be executed with this. You can also try building the sample project if you like.

$ mkdir /opt/rs97apps
$ cd /opt/rs97apps
$ git clone https://github.com/jbanes/rs97-commander
$ cd rs97-commander
$ make

Summary

It turned out that the purchased Chinese retro game machine is running Linux as the OS, and the emulator is running as a Linux application.

U-Boot and Linux are quite old, but tutorials are available, so it is easy to build a development environment.

Also, by writing an application that conforms to SDL (1.2), it was found that it could be operated on a Chinese retro game machine.

RetroFW v2.0 is under development that supports SDL2. I'm looking forward to it. I tried porting a longterm Linux kernel to improve the application development environment, but it was interrupted.

bonus

Try build

Now that the toolchain is ready, try compiling U-Boot.

U-Boot

$ wget ftp://ftp.denx.de/pub/u-boot/u-boot-1.1.6.tar.bz2
$ tar jxvf u-boot-1.1.6.tar.bz2
$ cd u-boot-1.1.6
$ gzip -cd ../u-boot-1.1.6-jz-20120904-r1819.patch.gz | patch -p1

Refer to this example and change the contents of the header file under ʻinclude / asm-mips. Roughly speaking, ʻextern is corrected to static even though ʻinline and inline` are added.

$ make lepus_msc_config
$ vi Makefile
(Removed examples from SUBDIRS)
$ make

This will create ʻu-boot-msc.bin`. I haven't tried it to work.

It turns out that the toolchain created by BuildRoot doesn't seem to match the version to build U-Boot. This is because the description of Page, which was used as a reference for modifying the header file, states that this error will occur if the toolchain is new.

In fact, gcc-mipsel-linux-gnu (gcc-7.4.0), which can be installed on WSL Ubuntu, also works.

$ apt install gcc-mipsel-linux-gnu

After that

Makefile


ifeq ($(ARCH),mips)
CROSS_COMPILE = mipsel-linux-gnu-
endif

You can change it to make.

When I asked to see .config, I was [told] that I didn't have the source code (https://boards.dingoonity.org/ingenic-jz4760-devices/retrofw-developer-support-thread/msg190970/ # msg190970).

Moreover, it is better to make it from scratch. Well, this one has compiled and linked, so I think it's okay if you check and move. I'm addicted to it if I don't check it after investigating the debugging method when it doesn't work (I wonder if it's LED debugging).

Linux

$ wget https://mirrors.edge.kernel.org/pub/linux/kernel/v2.6/linux-2.6.31.3.tar.gz
$ tar zxvf linux-2.6.31.3.tar.gz
$ cd linux-2.6.31.3
$ gzip -cd ../linux-2.6.31.3-jz-20120904-r1448.patch.gz | patch -p1
$ make lepus_defconfig
$ make
  CHK     include/linux/version.h
  CHK     include/linux/utsrelease.h
  SYMLINK include/asm -> include/asm-mips
  HOSTCC  scripts/basic/fixdep
  HOSTCC  scripts/basic/docproc
  HOSTCC  scripts/basic/hash
  CC      kernel/bounds.s
In file included from include/linux/compiler.h:40:0,
                 from include/linux/stddef.h:4,
                 from include/linux/posix_types.h:4,
                 from include/linux/types.h:14,
                 from include/linux/page-flags.h:8,
                 from kernel/bounds.c:9:
include/linux/compiler-gcc.h:86:30: fatal error: linux/compiler-gcc6.h: No such file or directory
 #include gcc_header(__GNUC__)
                              ^
compilation terminated.
/home/yochy/linux-2.6.31.3/./Kbuild:35: recipe for target 'kernel/bounds.s' failed
make[1]: *** [kernel/bounds.s] Error 1
Makefile:977: recipe for target 'prepare0' failed
make: *** [prepare0] Error 2

Therefore, compilation does not pass as it is. The compiler I'm using is gcc-6.4.0, but the Linux kernel source says it doesn't know gcc6. Looking at the document issued by Ingenic (Ingenic Linux Development Guide), the gcc used is 4.1.2.

To continue working on WSL, you need to create a 64-bit binary of this. For Ubuntu with VirtualBox, all you need to do is install the 32-bit line time library and the pre-built gcc-4.1.2.

Once you change your work to Ubuntu on VIrtualBox and do the following ...

$ cd /opt
$ sudo tar jxvf ~/mipseltools-gcc412-glibc261.tar.bz2
$ export PATH=/opt/mipseltools-gcc412-glibc261/bin:$PATH
$ sudo dpkg --add-architecture i386
$ sudo apt install libc6:i386

Now you can use it.

$ make lepus_defconfig
$ make
    :
  TIMEC   kernel/timeconst.h
Can't use 'defined(@array)' (Maybe you should just omit the defined()?) at kernel/timeconst.pl line 373.

This time perl complains. You can rewrite kernel / timeconst.pl by referring to here.

Next is like this.

$ make
    :
drivers/video/jz4760_lcd.c:141: error: 'LCD_CTRL_BST_64' undeclared here (not in a function)

This is related to the LCD declaration defined in .config.

.config


# CONFIG_JZ4760_LCD_TOPPOLY_TD025THEA7_RGB_DELTA is not set
CONFIG_JZ4760_LCD_TOPPOLY_TD043MGEB1=y
# CONFIG_JZ4760_LCD_TRULY_TFTG320240DTSW_18BIT is not set

I don't know which one should be set to y, so that's it for now. The LCD_CTRL_BST_64 itself is located in ʻarch / mips / include / asm / mach-jz4760b / jz4760blcdc.h, so if you #include` it will pass.

When I asked for .config, I got the kernel source published. At this point, porting to the current kernel version is not impossible, but I don't know if the application will work properly, so I think it's on hold.

Compare v1.2.1 and v1.2

I noticed that RetroFW v1.2.1 has appeared, so the difference from v1.2 used at the time of analysis is not good. I examined it from various directions.

To conclude, the contents are the same. Looking at the release comments and the commit message, it doesn't say that the contents have changed.

The name of the batch file that rewrites the contents of RetroFW.bin for each game machine has become kind.

To be honest, I wanted to write that if you cut out 0x0000_0000 to 0x087f_ffff (end of Linux area) of RetroFW.bin and write it to microSD, you can update the FAT32 area as it is without initializing it, so you do not need to re-install the software. It was. Sorry.

U-Boot/Linux kernel

U-Boot and Linux kernel were the same (sha256 value is the same). There is no point in updating for this purpose.

RetroFW.img

The contents of RegroFW.img were different (sha256 values are different).

We know that U-Boot and the Linux kernel are the same, so if there is a difference in the Linux area obtained from the MBR, there may be some benefit.

That's why I compared RetroFW.img after offset 0x80_0000 (0x4000 sectors in LBA), but unfortunately the contents were the same.

Compare v2.0 and v1.2

I noticed that RetroFW v2.0 was released, so I checked the difference from v1.2 used at the time of analysis. ..

From the conclusion, it seems worth trying because the contents of BuildRoot have changed. For details, refer to CHANGELOG.md.

The U-Boot and Linux kernels are (probably) the same.

Partition configuration

Looking at the MBR, there is a slight difference in the partition configuration (size), so I will show it here.

Partition No. First sector(LBA) Number of sectors Partition type
#1 0x0000_4000 0x0004_f800 Linux
#2 0x0005_3800 0x0007_0800 Linux swap
#3 0x000c_4000 0x0013_c000 FAT32
#4 - - Free

The Linux area has been slightly expanded, and the swap area has been reduced accordingly. The first sector of the FAT32 area has not changed.

Therefore, by replacing only the MBR and Linux areas, it seems possible to update without initializing the FAT32 area.

BuildRoot version

Checking the version of BuildRoot released together, it seems that 2018.02.11 is used (from CHANGES). The tutorial shown above says to use 2018.02.9, but it seems that the version has been updated.

Compare v2.1 and v2.0

RetroFW v2.1 was released.

Since v2.0 and later, the update information is kindly written, so you should read the linked release notes and decide whether it is necessary or not.

By the way, when I tried to replace and update the FAT32 area and earlier, reinitialization ran. Don't forget to back it up as it will be lost.

Compare V2.2 and v2.1

RetroFW v2.2 was released.

For some reason, the update information is not written properly this time. There is something like that in the Readme.md, but what is the Q.O.L. change in system recovery (Is Q.O.L. in this case the Quarity of Life?).

The image file this time is a little strange, it contains only one partition.

Partition No. First sector(LBA) Number of sectors Partition type
#1 0x0000_4000 0x0004_f800 Linux
#2 - - Free
#3 - - Free
#4 - - Free

When I wrote it to the microSD and turned on the power, the initialization process ran as a matter of course. It feels like it finished earlier than the previous processing. The partition structure after the initialization process was as follows.

Partition No. First sector(LBA) Number of sectors Partition type
#1 0x0000_4000 0x0004_f800 Linux
#2 0x0005_3800 0x0007_0800 Linux swap
#3 0x000c_4000 0x01c6_a000 FAT32
#4 - - Free

The FAT32 area has become wider than before.

By the way, the size of the image file is 175,112,704 bytes, but this is the 342,017 (0x53801) sector, and it just cuts into the partition of the swap file. Is there a reason why I didn't make it smaller by one sector?

By the way, if you read Readme.md a little more, it says that libopk and opkrun are imported and PyMenu and SimpleMenu can be used. Um ... PyMenu, SimpleMenu.

Let's see a little more later.

References

Recommended Posts

Let's analyze a Chinese retro game machine
Let's make a rock-paper-scissors game
Inversely analyze a machine learning model
Let's make a shiritori game with Python
[Python3] Let's analyze data using machine learning! (Regression)
Let's make a simple game with Python 3 and iPhone
Let's feel like a material researcher with machine learning
Make a squash game
〇✕ I made a game
Make a Tetris-style game!
Nostalgic, let's reproduce a character game like CBM-3032 with ncursesw.
Let's make a number guessing game in your own language!