Eglinfo-fb installation on ubuntu container for Torizon OS

Hello,

To make a long story short, our plan is to use Torizoncore OS for different containers. This allows us to use different graphical background programs such as navigations and web-browser in our infotainment system in a car.

So technically speaking, we want to develop a docker container to run UI program. We have used Storyboard UI program which traditionally requires to have “eglinfo-fb” recipe with console-tdx-image in yocto. (How to add framebuffer EGL to console-trdx-image? - Technical Support - Toradex Community)

But, I have no idea to run storyboard UI program, in other words, we need to install eglinfo-fb recipe in a ubuntu base container.

Here is what I did so far.

  1. My first thought was, if we are able to install eglinfo-fb itself (GitHub - dv1/eglinfo: EGL information utility with support for client APIs like OpenGL ES and OpenVG) in our ubuntu base container like we did with yocto, we will be able to run UI program.
    However, when I try to install it “./waf configure --platform=x11 --device=imx6”, there is no error during installation. But, in case of “./waf configure --platform=fb --device=imx6”, it shows below errors

    0;root@2ff06025ed0b: ~/eglinforoot@2ff06025ed0b:~/eglinfo# ./waf configure --platform=fb --device=imx6
    Setting top to : /root/eglinfo
    Setting out to : /root/eglinfo/build
    Checking for ‘gcc’ (c compiler) : /usr/bin/gcc
    Checking for ‘g++’ (c++ compiler) : /usr/bin/g++
    Checking for library [‘EGL’, ‘GAL’] : yes
    Checking for header EGL/eglvivante.h : yes
    Checking for header EGL/egl.h : yes
    Checking for library GLESv2 : yes
    Checking for header GLES2/gl2.h : yes
    Checking for header [‘GLES2/gl2.h’, ‘GLES2/gl2ext.h’] : yes
    Checking for fixed gccso shader binary definition : yes
    Checking for library OpenVG : yes
    Checking for header VG/openvg.h : yes
    Checking for compiler switch -Wextra : yes
    Checking for compiler switch -Wall : yes
    Checking for compiler switch -Wno-unused-parameter : yes
    Checking for compiler switch -std=c99 : yes
    Checking for compiler switch -pedantic : yes
    Checking for compiler switch -Wextra : yes
    Checking for compiler switch -Wall : yes
    Checking for compiler switch -std=c++98 : yes
    Checking for compiler switch -pedantic : yes
    Checking for compiler switch -O0 : yes
    Checking for compiler switch -g3 : yes
    Checking for compiler switch -ggdb : yes
    Checking for compiler switch -O0 : yes
    Checking for compiler switch -g3 : yes
    Checking for compiler switch -ggdb : yes
    Checking for compiler switch -O2 : yes
    Checking for compiler switch -O2 : yes

    Building for platform : fb
    Building for device : imx6

    API support:
    Desktop OpenGL : no
    OpenGL ES 1.x : no
    OpenGL ES 2.x : yes
    OpenVG : yes
    ‘configure’ finished successfully (14.538s)
    0;root@2ff06025ed0b: ~/eglinforoot@2ff06025ed0b:~/eglinfo# ./waf
    Waf: Entering directory /root/eglinfo/build/release' [ 1/13] cxx: src/json_writer.cpp -> build/release/src/json_writer.cpp.1.o [ 2/13] cxx: src/log.cpp -> build/release/src/log.cpp.1.o [ 3/13] cxx: src/main.cpp -> build/release/src/main.cpp.1.o [ 4/13] cxx: src/process_egl.cpp -> build/release/src/process_egl.cpp.1.o [ 5/13] cxx: src/scopes.cpp -> build/release/src/scopes.cpp.1.o [ 6/13] cxx: src/text_writer.cpp -> build/release/src/text_writer.cpp.1.o [ 7/13] c: src/json-sax/json.c -> build/release/src/json-sax/json.c.1.o [ 8/13] cxx: src/platform_fb_imx6.cpp -> build/release/src/platform_fb_imx6.cpp.1.o [ 9/13] cxx: src/glapi_stats.cpp -> build/release/src/glapi_stats.cpp.1.o [10/13] cxx: src/process_glapi.cpp -> build/release/src/process_glapi.cpp.1.o ../../src/platform_fb_imx6.cpp: In constructor 'eglinfo::native_window::native_window(const eglinfo::native_display&, EGLint)': ../../src/platform_fb_imx6.cpp:37:83: warning: unused parameter 'p_visual_id' [-Wunused-parameter] native_window::native_window(native_display const &p_native_display, EGLint const p_visual_id) ^~~~~~~~~~~ [11/13] cxx: src/openvg_stats.cpp -> build/release/src/openvg_stats.cpp.1.o [12/13] cxx: src/process_openvg.cpp -> build/release/src/process_openvg.cpp.1.o [13/13] cxxprogram: build/release/src/json_writer.cpp.1.o build/release/src/log.cpp.1.o build/release/src/main.cpp.1.o build/release/src/process_egl.cpp.1.o build/release/src/scopes.cpp.1.o build/releaseo src/platform_fb_imx6.cpp.1.o: In function eglinfo::native_window::native_window(eglinfo::native_display const&, int)‘:
    platform_fb_imx6.cpp:(.text+0xa2): undefined reference to fbCreateWindow' src/platform_fb_imx6.cpp.1.o: In function eglinfo::native_display::native_display(char const*)’:
    platform_fb_imx6.cpp:(.text+0x278): undefined reference to fbGetDisplayByIndex' collect2: error: ld returned 1 exit status Waf: Leaving directory /root/eglinfo/build/release’
    Build failed
    → task in ‘eglinfo’ failed (exit status 1):
    {task 3061207952L: cxxprogram json_writer.cpp.1.o,log.cpp.1.o,main.cpp.1.o,process_egl.cpp.1.o,scopes.cpp.1.o,text_writer.cpp.1.o,json.c.1.o,platform_fb_imx6.cpp.1.o,glapi_stats.cpp.1.o,process_g}
    [‘/usr/bin/g++’, ‘src/json_writer.cpp.1.o’, ‘src/log.cpp.1.o’, ‘src/main.cpp.1.o’, ‘src/process_egl.cpp.1.o’, ‘src/scopes.cpp.1.o’, ‘src/text_writer.cpp.1.o’, ‘src/json-sax/json.c.1.o’, 'src/platform_fb_]

  2. Similar problem happens when I attempt to run Storyboard UI program as shown in below log.

    root@66e96ff5e542:~# /root/sbengine/bin/sbengine -ogreio,channel=D1 -vvvv /root/OIS1D_D1/OIS1D_D1.gapp
    INFO [0.002]:Detected plugin [/root/sbengine/plugins//libgre-plugin-screen-dump.so]
    INFO [0.008]:Detected plugin [/root/sbengine/plugins//libgre-plugin-effects-3d.so]
    WARN [0.010]:Unable to load plugin: /root/sbengine/plugins//libgre-plugin-mtdev.so Error: libmtdev.so.1: cannot open shared object file: No such file or directory
    WARN [0.012]:Unable to load plugin: /root/sbengine/plugins//libgre-plugin-audio.so Error: libasound.so.2: cannot open shared object file: No such file or directory
    INFO [0.012]:Detected plugin [/root/sbengine/plugins//libgre-plugin-screen-scale.so]
    INFO [0.013]:Detected plugin [/root/sbengine/plugins//libgre-plugin-screen-path.so]
    INFO [0.014]:Detected plugin [/root/sbengine/plugins//libgre-plugin-poly.so]
    INFO [0.016]:Detected plugin [/root/sbengine/plugins//libgre-plugin-rext-external.so]
    WARN [0.017]:Unable to load plugin: /root/sbengine/plugins//libgre-plugin-tslib.so Error: libts-1.0.so.0: cannot open shared object file: No such file or directory
    INFO [0.019]:Detected plugin [/root/sbengine/plugins//libgre-plugin-screen-3d.so]
    INFO [0.020]:Detected plugin [/root/sbengine/plugins//libgre-plugin-event-redirect.so]
    INFO [0.021]:Detected plugin [/root/sbengine/plugins//libgre-plugin-gesture.so]
    INFO [0.022]:Detected plugin [/root/sbengine/plugins//libgre-plugin-media.so]
    INFO [0.025]:Detected plugin [/root/sbengine/plugins//libgre-plugin-luagredom.so]
    INFO [0.027]:Detected plugin [/root/sbengine/plugins//libgre-plugin-circle.so]
    INFO [0.029]:Detected plugin [/root/sbengine/plugins//libgre-plugin-rtext.so]
    INFO [0.030]:Detected plugin [/root/sbengine/plugins//libgre-plugin-system.so]
    INFO [0.031]:Detected plugin [/root/sbengine/plugins//libgre-plugin-animation.so]
    INFO [0.032]:Detected plugin [/root/sbengine/plugins//libgre-plugin-capture-playback.so]
    INFO [0.033]:Detected plugin [/root/sbengine/plugins//libgre-plugin-screen-rotate.so]
    INFO [0.034]:Detected plugin [/root/sbengine/plugins//libgre-plugin-greio.so]
    INFO [0.035]:Detected plugin [/root/sbengine/plugins//libgre-plugin-dev-input.so]
    INFO [0.035]:Detected plugin [/root/sbengine/plugins//libgre-plugin-timer.so]
    INFO [0.036]:Detected plugin [/root/sbengine/plugins//libgre-plugin-canvas.so]

    WARN [0.038]:Unable to load plugin: /root/sbengine/plugins//librender-plugin-opengles_2.0.so
    Error: /root/sbengine/plugins//librender-plugin-opengles_2.0.so: undefined symbol: fbDestroyWindow

    INFO [0.039]:Detected plugin [/root/sbengine/plugins//libgre-plugin-lua.so]
    INFO [0.042]:Detected plugin [/root/sbengine/plugins//libgre-plugin-model3d.so]
    INFO [0.043]:Detected plugin [/root/sbengine/plugins//libgre-plugin-logger.so]
    INFO [0.057]:Detected plugin [/root/sbengine/plugins//libgre-plugin-ffmpeg.so]
    ERROR [0.057]:Unable to load a renderer
    ERROR [0.057]:Unable to Load Manager Plugins: name=/root/OIS1D_D1/OIS1D_D1.gapp errno=0
    INFO [0.060]:All Plugins and Managers Finalized
    Failed to run engine using file: /root/OIS1D_D1/OIS1D_D1.gapp

  3. My last clue is about “libvivante.so” in /usr/lib. Since there is no “libvivante-fb.so”, “libGAL-fb.so”, “libEGL-fb.so”, or “libOpenVG.so”. So, I followed this guide from NXP (https://community.nxp.com/docs/DOC-330147) to install the BSP packages with below commands. After that, I could see all libraries that I want, but still no luck to run the UI program.

    cd …

    mkdir –p gpu_pack

    cd gpu_pack

    wget http://www.nxp.com/lgfiles/NMG/MAD/YOCTO//imx-gpu-viv-5.0.11.p7.4-hfp.bin

    wget http://www.nxp.com/lgfiles/NMG/MAD/YOCTO//xserver-xorg-video-imx-viv-5.0.11.p7.4.tar.gz

    chmod +x *

    ./imx-gpu-viv-5.0.11.p7.4-hfp –-auto-accept -–force

    cd imx-gpu*

    cp g2d/usr/include/* /usr/include/

    cp -d g2d/usr/lib/* /usr/lib/

    cp -Pr gpu-core/usr/* /usr

So, my thinking so far is that it feels like I’m using X11 based OS image like angstrom-lxde-image in yocto project in order to install required libraries for running UI program. So, basically I think I need to set up an ubuntu container just like a console-tdx-image.

Any thoughts, comments or helps to move forward will be so welcome.

Thank you in advance.

Currently, for the i.MX 6 devices we use an upstream kernel along with upstream graphics stack. Upstream graphics stack means the etnaviv DRM kernel driver and its user space implementation in Mesa. Distribution like Debian typically only package upstream/open source packages, like Mesa. By using the etnaviv driver and upstream Mesa allows us to use the graphics stack packages directly from the Debian feeds.

In contrast, our regular BSP uses currently the downstream/official NXP graphics stack from Vivante. However, that graphics stack is not available as a package in Debian, and integrating it is somewhat involved. The Vivante driver allows to obtain a EGL context through the fbdev device. That is also what eglinfo-fb tries to make use of.

From here you could go two directions:

  1. Try using upstream (etnaviv) graphics stack
  2. Create TorizonCore with downstream kernel, then try integrating downstream (vivante) graphics stack into Debian/Ubuntu container.

Both should technically be doable. At Toradex we so far mostly work with option 1 on i.MX 6 (largely because it “just works”).

Make sure you run a container using --privileged to have access to the DRM nodes in /dev/dri:

# docker run -it --privileged debian:buster

When using upstream graphics stack, you can use EGL directly through DRM/KMS (GBM) (as opposed to Vivante’s approach which gets a EGL context through fbdev). You can get an overview of the available “graphic cards” and outputs in sysfs:

root@96140a7b182d:/# ls /sys/class/drm/
card0  card1  card1-DPI-1  card1-HDMI-A-1  card1-VGA-1  renderD128  version
  • card0 represents the Vivante GPU (etnaviv driver)
  • card1 represents the IPU display controller (hence its where all the outputs are listed)

Install some tools to try out EGL using Mesa:

# apt-get install mesa-utils-extra kmscube

Unfortunately it seems that eglinfo can not handle well the two card nodes on i.MX 6 and segfaults. However, it seems that the Etnaviv node is not required, getting rid of it and symlinking card0 to card1 seems to make eglinfo work:

# rm /dev/dri/card0
# ln -s /dev/dri/card1 /dev/dri/card0

With that you get eglinfo:

# eglinfo
EGL client extensions string:
    EGL_EXT_device_base EGL_EXT_device_enumeration EGL_EXT_device_query
    EGL_EXT_platform_base EGL_KHR_client_get_all_proc_addresses
    EGL_EXT_client_extensions EGL_KHR_debug EGL_EXT_platform_wayland
    EGL_EXT_platform_x11 EGL_MESA_platform_gbm
    EGL_MESA_platform_surfaceless

GBM platform:
EGL API version: 1.4
EGL vendor string: Mesa Project
EGL version string: 1.4
EGL client APIs: OpenGL OpenGL_ES
EGL extensions string:
    EGL_ANDROID_native_fence_sync EGL_EXT_buffer_age
    EGL_EXT_image_dma_buf_import EGL_EXT_image_dma_buf_import_modifiers
    EGL_KHR_cl_event2 EGL_KHR_config_attribs EGL_KHR_create_context
    EGL_KHR_create_context_no_error EGL_KHR_fence_sync
    EGL_KHR_get_all_proc_addresses EGL_KHR_gl_colorspace
    EGL_KHR_gl_renderbuffer_image EGL_KHR_gl_texture_2D_image
    EGL_KHR_gl_texture_3D_image EGL_KHR_gl_texture_cubemap_image
    EGL_KHR_image EGL_KHR_image_base EGL_KHR_image_pixmap
    EGL_KHR_no_config_context EGL_KHR_reusable_sync
    EGL_KHR_surfaceless_context EGL_EXT_pixel_format_float
    EGL_KHR_wait_sync EGL_MESA_configless_context EGL_MESA_drm_image
    EGL_MESA_image_dma_buf_export EGL_WL_bind_wayland_display
Configurations:
     bf lv colorbuffer dp st  ms    vis   cav bi  renderable  supported
  id sz  l  r  g  b  a th cl ns b    id   eat nd gl es es2 vg surfaces
---------------------------------------------------------------------
0x01 32  0  8  8  8  8  0  0  0 0 0x34325241--         y  y  y     win
0x02 32  0  8  8  8  8 16  0  0 0 0x34325241--         y  y  y     win
0x03 32  0  8  8  8  8 24  0  0 0 0x34325241--         y  y  y     win
0x04 32  0  8  8  8  8 24  8  0 0 0x34325241--         y  y  y     win
0x05 32  0  8  8  8  8  0  0  2 1 0x34325241--         y  y  y     win
0x06 32  0  8  8  8  8  0  0  4 1 0x34325241--         y  y  y     win
0x07 32  0  8  8  8  8 16  0  2 1 0x34325241--         y  y  y     win
0x08 32  0  8  8  8  8 16  0  4 1 0x34325241--         y  y  y     win
0x09 32  0  8  8  8  8 24  0  2 1 0x34325241--         y  y  y     win
0x0a 32  0  8  8  8  8 24  0  4 1 0x34325241--         y  y  y     win
0x0b 32  0  8  8  8  8 24  8  2 1 0x34325241--         y  y  y     win
0x0c 32  0  8  8  8  8 24  8  4 1 0x34325241--         y  y  y     win
0x0d 24  0  8  8  8  0  0  0  0 0 0x34325258--         y  y  y     win
0x0e 24  0  8  8  8  0 16  0  0 0 0x34325258--         y  y  y     win
0x0f 24  0  8  8  8  0 24  0  0 0 0x34325258--         y  y  y     win
0x10 24  0  8  8  8  0 24  8  0 0 0x34325258--         y  y  y     win
0x11 24  0  8  8  8  0  0  0  2 1 0x34325258--         y  y  y     win
0x12 24  0  8  8  8  0  0  0  4 1 0x34325258--         y  y  y     win
0x13 24  0  8  8  8  0 16  0  2 1 0x34325258--         y  y  y     win
0x14 24  0  8  8  8  0 16  0  4 1 0x34325258--         y  y  y     win
0x15 24  0  8  8  8  0 24  0  2 1 0x34325258--         y  y  y     win
0x16 24  0  8  8  8  0 24  0  4 1 0x34325258--         y  y  y     win
0x17 24  0  8  8  8  0 24  8  2 1 0x34325258--         y  y  y     win
0x18 24  0  8  8  8  0 24  8  4 1 0x34325258--         y  y  y     win
0x19 16  0  5  6  5  0  0  0  0 0 0x36314752--         y  y  y     win
0x1a 16  0  5  6  5  0 16  0  0 0 0x36314752--         y  y  y     win
0x1b 16  0  5  6  5  0 24  0  0 0 0x36314752--         y  y  y     win
0x1c 16  0  5  6  5  0 24  8  0 0 0x36314752--         y  y  y     win
0x1d 16  0  5  6  5  0  0  0  2 1 0x36314752--         y  y  y     win
0x1e 16  0  5  6  5  0  0  0  4 1 0x36314752--         y  y  y     win
0x1f 16  0  5  6  5  0 16  0  2 1 0x36314752--         y  y  y     win
0x20 16  0  5  6  5  0 16  0  4 1 0x36314752--         y  y  y     win
0x21 16  0  5  6  5  0 24  0  2 1 0x36314752--         y  y  y     win
0x22 16  0  5  6  5  0 24  0  4 1 0x36314752--         y  y  y     win
0x23 16  0  5  6  5  0 24  8  2 1 0x36314752--         y  y  y     win
0x24 16  0  5  6  5  0 24  8  4 1 0x36314752--         y  y  y     win

Most tools allow to specify the device, e.g. kmscube:

kmscube  --device /dev/dri/card1

From your output it seems that sbengine is actually linked against Vivante’s EGL library:

Error: /root/sbengine/plugins//librender-plugin-opengles_2.0.so: undefined symbol: fbDestroyWindow

fbDestroyWindow is a Vivante specific symbol. So you probably need to get a sbengine which works on-top of DRM/KMS (GBM) platforms…

Also, since you mentioned “different graphical background programs”, Wayland might be an option too. With EGL only one instance can occupy a screen, and you would need to have some way to switch between the different programs. Using Wayland would allow that: You would have a single compositor running and then multiple clients which use the Wayland backend to talk to the compositor. Weston is the reference compositor, it supports multiple shells like the Desktop shell (default, as we use it in the example containers) or the IVI shell:
https://at.projects.genivi.org/wiki/display/WIE/Quick+start

There are also other alternatives, e.g. Westeros:
https://wiki.rdkcentral.com/display/RDK/Westeros

Hello stefan!

Thank you for your kind comments and interesting on my questions. It helped me a lot to understand my circumstances. I was able to follow your answers and found out there are more things to let you know about my findings so far.

First of all, in terms of fbdev, actually, there are two storyboard UI engines to run in embedded linux, fbdev and openEGL. When I try to run fbdev, simply what I have to do from debian container is,

docker run -it --privileged debian:buster
apt-get update
apt-get libgles2-mesa libegl1-mesa

Then, I was able to run storyboard UI program without any problem. But in this method, we cannot expect graphic accelerations (It only gives ~5 fps framerate). We knew how to run it with this fbdev engine before I asked the questions, but it wasn’t a good idea due to the low framerate.

But, thanks to your kind explanations, I now understood facts that since the debian container is using the upstream graphics stack which directly use the graphics stack packages from the Debian feeds, this fbdev sbengine makes it possible to run it with direct graphics stacks without symlinking card0 to card1.

  1. Secondly, I was able to follow all your works and eglinfo results on above answers. But like I said, we need to choose second option “Create TorizonCore with downstream kernel, then try integrating downstream (vivante) graphics stack into Debian/Ubuntu container”

Before do that, I was able to make “console-tdx-image” from yocto by the below command in torizoncore OS

root@apalis-imx6-10492381:~# xzcat console-tdx-image.rootfs.tar.xz | docker import - {container name}

But, I failed to run UI with those error logs

ACTION [0.066]:Loading Lua script [json.lua]
ACTION [0.071]:Loading Lua script [callbacks.lua]
ACTION [0.076]:Loading Lua script [events.lua]
INFO   [0.077]:Initialize plugin [model3d (5.3.0.28994)] []
INFO   [0.077]:Initialize plugin [ffmpeg (5.3.0.28994)] []
EVENT  [0.090]:IO: Dispatch [gre.internalinit]
INFO   [0.090]:Application does not contain polygonal content, turning off multisampling
INFO   [0.090]:GLES 2.0: initialize native API
INFO   [0.091]:GLES 2.0: Initialize EGL display
INFO   [0.095]:GLES 2.0: EGL initialization
[     1] Failed to open device: No such file or directory, Try again...
[     2] Failed to open device: No such file or directory, Try again...
[     3] Failed to open device: No such file or directory, Try again...
[     4] Failed to open device: No such file or directory, Try again...
[     5] _OpenDevice(1090): FATAL: Failed to open device, errno=No such file or directory.
[     6] Failed to open device: No such file or directory, Try again...
[     7] Failed to open device: No such file or directory, Try again...
[     8] Failed to open device: No such file or directory, Try again...
[     9] Failed to open device: No such file or directory, Try again...
[    10] _OpenDevice(1090): FATAL: Failed to open device, errno=No such file or directory.
sh-4.4# 

I think it’s because, TorizonCore is not built with downstream kernel while console-tdx-image docker container is based on downstream kernel. So my next plan is to

(1) see if TorizonCore kernel can be changed with “run update_kernel” in uboot
(2) see if debian container can be changed with downstream kernel

Since console-tdx-image container is quite heavy (~1.5GB), I also want to spend some time on (2).

I will keep posting here about my findings and questions.

P.S: For those who are interested in this topic, I found good articles explaining a linux graphical stack: June | 2012 | Clean Rinse)

Thank you!

Yes, this is due to missing kernel driver for the Vivante graphics driver. Unfortunately it is not an easy task to integrate that properly into TorizonCore. Just run update_kernel definitly won’t do the trick as TorizonCore has a different boot flow and storage location of the kernel (TorizonCore uses a boot.scr file which then reads the kernel from the OSTree based root file system). Also the kernel configuration from our regular BSP is not fit for Docker…

However, on Crank’s support channels I found some hints that there is a custom OpenGL ES 2.0 render plug-in for RaspberryPi. They seem to have already a custom for Vivante driver as well, maybe they have already one which works with Mesa DRM/GBM? If not, it also should not be very complicated for Crank to support OpenGL ES 2.0 ontop of DRM/GBM, so maybe asking Crank might help?

Hello,

I already asked Cranksoft a long ago. I asked several times about possibilities and methods to run openGL ES 2.0 engine in X11 OS like LXDE, but it seems like they have no plan for supporting OpenGL ES render with Mesa DRM/GBM directly. If you go their wiki, you could easily find their answers that only yocto linux with imx6 can be executable in framebuffer without X11 and wayland distro. In order to use direct Mesa DRM/GBM, they recommand us to use fbdev engine which does not have graphic acceleration.

So, given my circumstances, I am feeling that there’re not much to do regarding docker container development to use both Cranksoft UI in framebuffer and other graphical backends based navigation programs. I think our team have to come up with other ways of infotainment system architecture. Thank you very much!

Just to close up this question, the underlying issue here extends beyond just Torizon. As such we here at Toradex are currently discussing possible solutions in conjunction with Crank.

Just to summarize the general solution would still be similar as Stefan has described above. A single container would run a compositor that other container clients would use. So for this case it’d be a 3 container solution with a compositor and then one container for the Storyboard app and then another container for the navigational app. This would allow for different underlying graphical programs without conflict.

Best Regards,
Jeremias