nrf_modem_init() returns -22 (EINVAL) on bare-metal nRF9160 app using nrf_modem from nrfxlib v2.9.0

Hello Nordic Support,

I am developing a bare-metal, non-secure firmware for nRF9160 using nrf_modem from SDK nrfxlib v2.9.0, without Zephyr or RTOS.

I followed the porting guide here:
https://docs.nordicsemi.com/bundle/ncs-latest/page/nrfxlib/nrf_modem/doc/ug_nrf_modem_porting_os.html

However, calling nrf_modem_init() consistently fails with:

Modem init failed: -22

which maps to -EINVAL.

/*********************************************************************
*                    SEGGER Microcontroller GmbH                     *
*                        The Embedded Experts                        *
**********************************************************************

-------------------------- END-OF-HEADER -----------------------------

File    : main_ns.c
Purpose : Generic application start

*/


#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <nrfx.h>
#include <nrf_gpio.h>
#include <nrf_errno.h>
#include <nrf.h>

#include <pins_def.h>

#include <nrfx_ipc.h>
#include <nrf_modem.h>
#include <nrf_modem_os.h>

#include "NSCFunctions.h"


#define SHMEM_TX_SIZE     ( 0x3000 )   // 12 KB
#define SHMEM_RX_SIZE     ( 0x3000 )
#define SHMEM_TRACE_SIZE  ( 0x3000 )
#define SHMEM_SIZE        (NRF_MODEM_CELLULAR_SHMEM_CTRL_SIZE + SHMEM_TX_SIZE + SHMEM_RX_SIZE + SHMEM_TRACE_SIZE)

// Create one contiguous memory space for the buffers required by the modem driver
typedef struct {
    uint8_t nrf_modem_ctrl[NRF_MODEM_CELLULAR_SHMEM_CTRL_SIZE];
    uint8_t nrf_modem_tx[SHMEM_TX_SIZE];
    uint8_t nrf_modem_rx[SHMEM_RX_SIZE];
    uint8_t nrf_modem_trace[SHMEM_TRACE_SIZE];
} nrf_modem_bufs_t;

static nrf_modem_bufs_t modem_shm __attribute__(( section(".modem_shm"), aligned(4)));

const struct nrf_modem_shmem_cfg modem_shmem_cfg = {
    .ctrl = {
        .base = (uint32_t)&modem_shm.nrf_modem_ctrl,
        .size = NRF_MODEM_CELLULAR_SHMEM_CTRL_SIZE,
    },
    .tx = {
        .base = (uint32_t)&modem_shm.nrf_modem_tx,
        .size = SHMEM_TX_SIZE,
    },
    .rx = {
        .base = (uint32_t)&modem_shm.nrf_modem_rx,
        .size = SHMEM_RX_SIZE,
    },
    .trace = {
        .base = (uint32_t)&modem_shm.nrf_modem_trace,
        .size = SHMEM_TRACE_SIZE,
    }
  };


//***********************************
// modem_fault_handler
//***********************************
void modem_fault_handler(struct nrf_modem_fault_info *info) 
{
    printf("MODEM FAULT! PC=0x%08X Reason=%u\n", info->program_counter, info->reason);
}

void nrf_modem_os_init(void)
{
    /* Initialize the glue layer and required peripherals. */
}

void nrf_modem_os_shutdown(void)
{
    /* Deinitialize the glue layer.
       When shutdown is called, all pending calls to nrf_modem_os_timedwait
       shall exit and return -NRF_ESHUTDOWN. */
}

void *nrf_modem_os_shm_tx_alloc(size_t bytes)
{
    /* Allocate a buffer on the TX area of shared memory. */
}

void nrf_modem_os_shm_tx_free(void *mem)
{
    /* Free a shared memory buffer in the TX area. */
}

void *nrf_modem_os_alloc(size_t bytes)
{
    /* Allocate a buffer on the library heap. */
}

void nrf_modem_os_free(void *mem)
{
    /* Free a memory buffer in the library heap. */
}

void nrf_modem_os_busywait(int32_t usec)
{
    /* Busy wait for a given number of microseconds. */
}

int32_t nrf_modem_os_timedwait(uint32_t context, int32_t *timeout)
{
    if (!nrf_modem_is_initialized())
    {
        return -NRF_ESHUTDOWN;
    }

    /* Put a thread to sleep for a specific time or until an event occurs.
       Wait for the timeout.
       All waiting threads shall be woken by nrf_modem_event_notify.
       A blind return value of zero will cause a blocking wait. */

    if (!nrf_modem_is_initialized())
    {
        return -NRF_ESHUTDOWN;
    }

    return 0;
}

void nrf_modem_os_event_notify(uint32_t context)
{
    // Notify the application that an event has occurred.
    //   This shall wake all threads sleeping in nrf_modem_os_timedwait.
}

void nrf_modem_os_errno_set(int errno_val)
{
    /* Set OS errno. */
}

bool nrf_modem_os_is_in_isr(void)
{
    // Check if executing in interrupt context.
    return false;
}

int nrf_modem_os_sem_init(void **sem, unsigned int initial_count, unsigned int limit)
{
    /* If multithreaded access to modem functionalities is needed, the function must allocate
     * and initialize a semaphore and return its address through the `sem` parameter. If the
     * address of an already allocated semaphore is provided as an input, the allocation part is
     * skipped and the semaphore is only reinitialized.
     */
    return 0;
}

void nrf_modem_os_sem_give(void *sem)
{
    /* Give a semaphore. */
}

int nrf_modem_os_sem_take(void *sem, int timeout)
{
    /* Try to take a semaphore with the given timeout. */
    return 0;
}

unsigned int nrf_modem_os_sem_count_get(void *sem)
{
    /* Get a semaphore's count. */
    return 0;
}

 int nrf_modem_os_mutex_init(void **mutex)
{
    /* If multithreaded access to modem functionalities is needed, the function must allocate
     * and initialize a reentrant mutex and return its address through the `mutex` parameter.
     * If the address of an already allocated mutex is provided as an input, the allocation part
     * is skipped and the mutex is only reinitialized.
     * Mutexes are not required if multithreaded access to modem functionalities is not needed.
     * In this case, the function must blindly return ``0``.
     */
    return 0;
}
/*
void nrf_modem_os_mutex_unlock(void *sem)
{
    // Unlock a mutex.
}
*/
int nrf_modem_os_mutex_lock(void *sem, int timeout)
{
    /* Try to lock a reentrant mutex with the given timeout. */
    return 0;
}

void nrf_modem_os_log(int level, const char *fmt, ...)
{
    /* Generic logging procedure. */
  //  va_list ap;
  //  va_start(ap, fmt);
  //  vprintf(fmt, ap);
  //  printf("\n");
  //  va_end(ap);
}

void nrf_modem_os_logdump(int level, const char *str, const void *data, size_t len)
{
    /* Log hex representation of object. */
}



// Configure non-secure peripherals.

void nrf_spu_periph_set(uint16_t id, uint32_t flags);
void nrf_spu_periph_clear(uint16_t id, uint32_t flags);
void nrf_spu_gpio_set(uint16_t id);
void nrf_spu_gpio_clear(uint16_t id);

// Non-secure callable function
extern uint32_t pxNSCFunctionHandler( uint32_t value );

//***********************************
// Print modem SHMEM CFG
//***********************************
void print_modem_shmem_cfg(void)
{
  printf("======== Modem SHMEM Config ========\n");

  printf("CTRL  : base = 0x%08X, size = %u bytes\n",
        (unsigned int)(uintptr_t)modem_shmem_cfg.ctrl.base,
        (unsigned int)modem_shmem_cfg.ctrl.size);

  printf("TX    : base = 0x%08X, size = %u bytes\n",
        (unsigned int)(uintptr_t)modem_shmem_cfg.tx.base,
        (unsigned int)modem_shmem_cfg.tx.size);

  printf("RX    : base = 0x%08X, size = %u bytes\n",
        (unsigned int)(uintptr_t)modem_shmem_cfg.rx.base,
        (unsigned int)modem_shmem_cfg.rx.size);

  printf("TRACE : base = 0x%08X, size = %u bytes\n",
        (unsigned int)(uintptr_t)modem_shmem_cfg.trace.base,
        (unsigned int)modem_shmem_cfg.trace.size);

  printf("====================================\n");
}

//*********************************************************************
//       main()
//*********************************************************************
int main(void) 
{
 int i, j, k = 0;

 printf("Hello World!\n");
 printf(nrf_modem_build_version());
 printf("\n");

 print_modem_shmem_cfg();

 struct nrf_modem_init_params init_params = {
        .shmem = modem_shmem_cfg,
        .ipc_irq_prio = 0,               // 0 ... 7
        .fault_handler = modem_fault_handler,
        .dfu_handler = NULL,             // optional
  };

  int err = nrf_modem_init(&init_params);
  if (err < 0) 
     {
        printf("Modem init failed: %d\n", err);
     }
 
  nrf_gpio_cfg_output(2);
  nrf_gpio_pin_set(2);
  nrf_gpio_cfg_output(3);
  nrf_gpio_pin_set(3);
  nrf_gpio_cfg_output(4);
  nrf_gpio_pin_set(4);
  nrf_gpio_cfg_output(5);
  nrf_gpio_pin_set(5);

  printf("NSC function 1 call result: %d\n", entry1(2));
  printf("NSC function 2 call result: %d\n", entry2(2));

  j = 0;

  do 
    {
      for (i = 0; i < 10000; i++) 
          {
            printf("Hello World %d!\n", i);;
          }

      nrf_gpio_pin_toggle( j + 2 );
      j = ( j + 1 ) % 4;
    } while (1);
}

/*************************** End of file ****************************/

Given that:

  • SHMEM areas are correctly aligned

  • Memory is below 0x20020000

  • Sizes are valid (4K + 3×12K = 40K)

  • Fault handler is defined correctly

  • nrf_modem_os_* stubs are implemented

I cannot identify why nrf_modem_init() returns -EINVAL. Could you help confirm whether this is a modem validation bug, a missing requirement, or a configuration issue?

Best regards,

Neculai

Related