#include <fcntl.h>
#include <sys/mman.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/neutrino.h>

#define MAP_FAILED  ((void *)-1)

// This is the size of the map we need to allocate
static const int mapSize = 28 * 1024 * 1024; // Crashes kernel with SEGV

// This is the smallest size that crashes the kernel
/*static const int mapSize = (1 * 1024 * 1024) - (4 * 1024) + \
 *                         512 - 256 - 128 - 64 - 32 - 16 - \
 *                         8 - 4 - 2 - 1; // Crashes kernel ... 0xFF001
 */

// This is the largest size that doesn't crash the kernel; anything smaller
// works
//static const int mapSize = (1 * 1024 * 1024) - (4 * 1024); // Works ... 0xFF000


static const char * testPath = "/dev/shmem/test";

int main( int argc, char *argv[] )
{
   int fd;
   unsigned int i;
   unsigned char* addr = MAP_FAILED;

   fprintf( stderr, "Attaching to shared map \"%s\" of size %d (0x%x) bytes.\n",
            testPath, mapSize, mapSize );

   // Failing to do this will cause an immediate Memory Fault
   if (ThreadCtl(_NTO_TCTL_IO, 0) != EOK)
   {
      perror( "ThreadCtl" );
      return EXIT_FAILURE;
   }
   fprintf( stderr, "ThreadCtl  ok\n" );

   if( unlink( testPath ))
   {
      // It's fine if the file doesn't exist
      if( errno != ENOENT )
      {
         perror( "unlink" );
      }
   }
   fprintf( stderr, "unlink     ok\n" );

   fd = shm_open( testPath, O_RDWR | O_CREAT, 0777);
   if( fd == -1 )
   {
      perror( "shm_open" );
      return EXIT_FAILURE;
   }
   fprintf( stderr, "shm_open   ok\n" );

   if( -1 == shm_ctl( fd,
                      SHMCTL_ANON | SHMCTL_PRIV | SHMCTL_GLOBAL,
                      (_uint64) 0,
                      (_uint64) mapSize ))
   {
      if( ENOSYS == errno )
      {
         // ENOSYS is telling us that we already did a shm_ctl, and it can only
         // be done once.
         fprintf( stderr, "shm_ctl  already run (ok if current size fits)\n" );
      }
      else
      {
         perror( "shm_ctl" );
         close(fd);
         return EXIT_FAILURE;
      }
   }
   fprintf( stderr, "shm_ctl    ok\n" );

   if( MAP_FAILED == (addr = mmap( NULL,
                                   mapSize,
                                   PROT_READ | PROT_WRITE | PROT_NOCACHE,
                                   MAP_SHARED,
                                   fd,
                                   0 )))
   {
      perror( "mmap" );
      return EXIT_FAILURE;
   }
   fprintf( stderr, "mmap       ok\n" );

   close(fd);

   if( munmap( addr, mapSize ))
   {
      perror( "munmap" );
   }
   else
   {
      fprintf( stderr, "munmap     ok\n" );
   }

   if( shm_unlink( testPath ))
   {
      perror( "shm_unlink" );
   }
   else
   {
      fprintf( stderr, "shm_unlink ok\n" );
   }

   return 0;
}
