Home
Developer Resources
Technical Articles

QNX Technical Articles

QNX® SDP 7.0 Core OS: Release Notes

Date of this edition: June 7, 2021


Note: Changes to these notes since March 6, 2018 are highlighted below with this icon: New:

Target OS: This product is compatible with targets that are running QNX® Neutrino® 7.0.

Host OS: In order to install this product, you must have installed the QNX Software Development Platform 7.0 on one of the following development hosts:

  • Microsoft Windows 10 Pro 64-bit, Windows 8.1 Pro 64-bit, or Windows 7 Professional 64-bit
  • macOS version 10.10, 10.11, 10.12
  • Linux Red Hat Enterprise Linux 7 64-bit, Ubuntu Desktop 18.04 LTS 64-bit, or Ubuntu Desktop 16.04 LTS 64-bit, on x86_64 processors (QNX SDP 7 isn't supported on Linux on ARM processors)

Note:
  • To access the most up-to-date version of these release notes, go to the QNX Software Center, right-click on the package, choose Properties, and use the link provided.
  • Packages in the QNX Software Center include debugging information, unless the debugging information is very large, in which case it's in a separate package.

Contents...

Throughout this document, you may see reference numbers associated with particular issues, changes, etc. When corresponding with our Technical Support staff about a given issue, please quote the relevant reference number. You might also find the reference numbers useful for tracking issues as they become fixed.

All references to POSIX standards in this release note are to The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004 Edition.

See also the release notes for the following:

Fixed issues and new features

Kernel

General

  • New: The version number of the kernel is now 7.0.1.
  • New: On x86_64 targets, you can now read the CR0 and CR4 control registers. On x86 and x86_64 targets, you can now use the RDMSR and WRMSR commands to read and write model-specific registers. You need to successfully call ThreadCtl( _NTO_TCTL_IO_PRIV, 0 ) first. These operations cause an exception that's handled by the kernel. (Ref# J2145363, J2396359)
  • We've corrected an issue that could cause a kernel crash when cleaning up terminated processes. (Ref# J2456722)
  • We've corrected a race condition that could have delayed exiting interrupt handlers. (Ref# J2345073)
  • We've corrected a race condition that could cause the kernel to crash if the number of interrupts exceeded the maximum. (Ref# J2252738)
  • The kernel no longer crashes in a rare case when io-pkt is terminated. (Ref# J1483470)
  • We've fixed some edge cases where the loader could truncate the name of the executable. (Ref# J2180469)
  • The FPU control registers are now saved and restored properly on AArch64 targets. (Ref# J2190376)
  • Attempts to use the DCMD_PROC_THREADCTL to rename the idle thread now fail with an error of EPERM. (Ref# J1638671)
  • The kernel no longer crashes when you're using tickless mode and the time to remain tickless is very small. (Ref# J2231789)
  • We've improved the speed for mmap() and spawn() from SD cards. (Ref# J2172387)
  • We've addressed some security issues. (Ref# J384633, J2163410, J2163529)
  • We've corrected a race condition that could occur in the following scenarios if you were running procnto with the -v option:
    • A SIGKILL signal is sent to a process that's exiting.
    • A fork() fails, and a SIGKILL signal is sent to the process while it's being terminated.
    • An exec() fails, and a SIGKILL signal is sent to the child process while it's being terminated.

    (Ref# J2218077)

  • We've corrected an SMP race condition that could occur when one CPU sends a signal and another CPU calls TimerTimeout(). (Ref# J2144931)
  • New processes now start with the correct locale when you specify the locale via an environment variable. (Ref# J2141710)
  • The root user now has the PROCMGR_AID_CHANNEL_CONNECT ability enabled by default. (Ref# J2201448)
  • Note that the cleanup of a terminated process occurs at the priority of the thread that sent the signal. (Ref# J2146111)
  • We've corrected an issue on OMAP5 where a multi-IOV message wasn't transferred correctly if the IOV array was on the last page of a 1 MB-aligned virtual region. (Ref# J2166386)
  • Periodic timers that don't get handled because of the limit on the number of timers that the kernel services in a clock tick are no longer rearmed to fire in the past. (Ref# J835502)

Configuring procnto

  • There's a new -s option that enables safe shared mutexes; the kernel rejects any attempts to lock PTHREAD_PRIO_INHERIT mutexes whose owner isn't known to the kernel and that the locking thread claims is from a different process. For more information, see "Safely sharing mutexes, barriers, and reader/writer locks between processes," below. (Ref# J1614329, J2250677)
  • You can now specify a tilde (~) to clear some boolean options. The -fe, -h, -p, and -v options now support this feature. (Ref# J1681788, J2212190)
  • There's a new /proc/config virtual file that summarizes the configuration settings for procnto*. (Ref# J2179468, J2220486)

Scheduling

  • We've improved the algorithm that the adaptive partitioning scheduler uses to divide time among partitions. (Ref# J1691063)
  • Threads using sporadic scheduling no longer yield the CPU when they haven't exhausted their budget and didn't finish their work. (Ref# J740870)
  • We've improved the calculation of execution times for threads. One result of this is improved accuracy in round-robin scheduling. (Ref# J2179406)
  • Note that if priority inheritance occurs, whether due to message passing or an attempt to lock a mutex, if a thread that doesn't have the PROCMGR_AID_PRIORITY ability enabled is supposed to be boosted to a privileged priority, it's actually boosted to the highest unprivileged priority. Threads whose priority was boosted like this now correctly return to their real priorities when they relinquish the mutex. (Ref# J2456489, J2500233)
  • We've corrected an issue where a high-priority kernel call on one processor would be delayed by a long low-priority kernel call on a different processor. (Ref# J2370931)

Memory management

  • After you fork a process, memory protection is now set correctly in the child process. (Ref# J2375851)
  • If you add or remove PROT_NOCACHE to or from a memory mapping, any other changes to the protection flags that you make at the same time are now processed correctly. (Ref# J2370544)
  • The memory manager now correctly marks PROT_NONE mappings as wire-on-fault, and then wires them if and when the memory is backed, which happens only if the mapping is changed with mprotect(). (Ref# J2398300)
  • The virtual memory manager now leaves MAP_ELF | MAP_SHARED mappings as shared; if you set a breakpoint in the code, it's then marked as private. In a superlocked system, leaving these mappings as shared greatly reduces the amount of memory reserved for shared libraries. (Ref# J2224405)
  • For a superlocked process, memory is now correctly reserved when you map a PROT_NONE region and then use mprotect() to switch it to PROT_READ. (Ref# J2374654)
  • We've corrected an error that made the system report that memory was exhausted when there was really one page left. (Ref# J2318451)
  • We've corrected an issue that sometimes caused a kernel crash when memory was exhausted. (Ref# J2318424)
  • When memory is superlocked, the kernel now takes page faults only for MAP_LAZY mappings. (Ref# J2285493)
  • We now require that the hardware underlying ClockCycles() be synchronized across all processors on an SMP system. If it isn't, you might encounter some unexpected behavior, such as drifting times and timers. (Ref# J2164513, J2244360)
  • We’ve corrected a race condition that could cause a freeze when a CPU performed a page-table walk. (Ref# J2184555)
  • The kernel no longer crashes when you use a memory-mapped file with a size that's greater than the amount of available memory. (Ref# J1695667)
  • We've corrected an issue with caching that sometimes caused a kernel crash when you opened a memory-mapped file. (Ref# J2277361)
  • We've corrected an issue with caching on ARMv7 and AARCH64 that caused applications to randomly crash with a SIGBUS error when the system was booting. (Ref# J2371514)
  • The memory manager doesn't support 2 MB pages in the x86 PAE variant. (Ref# J2178056)

High-resolution timers

A high-resolution timer is a timer whose tolerance is between 0 and the clock period. High-resolution timers were introduced as an experimental feature in QNX SDP 7.0, but are no longer considered to be experimental.

You should make sure that your hardware is suitable for using high-resolution timers:

  • On Intel systems, the LAPIC on CPU0 is the recommended hardware timer; the HPET has consistently shown significant jitter issues. (Ref# J1701255)
  • You should characterize the hardware timer jitter. The greater the jitter, the less acceptable the accuracy in timer expiry times and the greater the chances for clock drift. Less than 1 μs is ideal, but less than 2 μs is acceptable.
  • You should use the -f option on your board's startup command to specify the ClockCycles() hardware timer frequency, especially on Intel systems.
  • The frequency of the hardware timer beneath ClockCycles() must be constant.
  • The hardware timer beneath ClockCycles() must be synchronized across all CPUs.

    When procnto starts, it samples ClockCycles() "simultaneously" across all CPUs. If the time between all CPUs is < 1 μs, the kernel assumes this time variance is due to the cache coherency protocol overhead, and therefore all the timers are synchronized. If you specify the -v option for procnto, the kernel reports "All ClockCycles offsets within tolerance". If you specify -vv, the kernel also displays the offset on each CPU. For example:

    ClockCycles offsets:
     0 280
     1 0
     2 290
     3 314
    All ClockCycles offsets within tolerance
      

In order to use high-resolution timers (i.e., use timer_settime(), timer_timeout(), TimerSettime(), or TimerTimeout() to set the timer tolerance to a value between 0 and the clock period), you now need to have the PROCMGR_AID_HIGH_RESOLUTION_TIMER ability enabled.


Note: Using high-resolution timers causes extra timer interrupts; as you use more high-resolution timers, the impact on system performance increases. The total impact will vary with interrupt frequency and the hardware characteristics of the system.

Utilities

The utilities on QNX Neutrino targets include the following updates:

confstr, setconf
These utilities now give you an error message if you can't set the value of a configuration string. (Ref# J2212105)
dumper
We now correctly remove notifications when a connection is closed. (Ref# J2191556)
elfnote
Setting a stack size that's insufficient for the startup of the process, including space for arguments and environment variables (limited by _SC_ARG_MAX or ARG_MAX), may cause unexpected early failures of the process execution. (Ref# J780825)
mq
You can once again list the contents of the /dev/mq directory. (Ref# J492768)
on
This utility no longer supports the -l ("el") option. (Ref# J1664773, J1556563)
passwd
This utility no longer has a memory fault if you use the -s option to specify the hashing algorithm. (Ref# J2234518)
pidin
  • This utility can now get IRQ information from remote notes that are running QNX Neutrino 6.6. (Ref# J1677656)
  • The pidin net command no longer crashes with a segmentation fault when you get information from 32-bit remote hosts that are running QNX Neutrino 6.6 or 6.5. (Ref# J1681367)
  • This utility now displays more information without requiring extra process-manager abilities. (Ref# J2235089)
python
You no longer get a "Bad file descriptor" message when you call getpass.getpass(). (Ref# J1702552)
slogger2
New: This daemon now retains the PROCMGR_AID_PRIORITY ability before dropping root. (Ref# J1675308)
traceprinter
This utility once again displays slogger2 log messages. (Ref# J1699540)
uname
If you're running a safety version of the kernel, the release level (the output for the -r option) now includes an uppercase "S". (Ref# J2504408, J2504421)

C library

The updated C library in this product includes the following new functions:

procmgr_session_ext()
Provide process manager session support (an extended version of procmgr_session())

The library also includes the following fixes and updates:

alloca.h
This header file now defines NULL if it isn't already defined. (Ref# J2254429)
clock_gettime()
In order to get the time for a CPU-time clock associated with another process, your process must now have the PROCMGR_AID_XPROCESS_QUERY ability enabled. (Ref# J2161913, J2198684)
clock_settime()
You now get an error of EPERM instead of EINVAL if you try to set the time for a process or thread CPU-time clock. (Ref# J2179406, J2179415, J2212198, J2212200)
ClockCycles()
We now require that the hardware underlying ClockCycles() be synchronized across all processors on an SMP system, so you no longer have to use a runmask for threads to prevent them from migrating to other processors between calls to ClockCycles(). (Ref# J2164513, J2244360)
ClockTime()
  • In order to get the time for a CPU-time clock associated with another process, your process must now have the PROCMGR_AID_XPROCESS_QUERY ability enabled. (Ref# J2161913, J2198684)
  • You now get an error of EPERM instead of EINVAL if you try to set the time for a process or thread CPU-time clock. (Ref# J2179406, J2179415, J2212198, J2212200)
confstr()
There's a new _CS_CONFIG_PATH configuration string. Its value is a colon-separated list of directories to search for configuration files. (Ref# J2184056, J2220477)
ConnectFlags()
This function gives an error of EBADF if the connection ID is invalid. (Ref# J2208048)
creat()
For conformance with POSIX, this function now sets errno to ETXTBSY if the file you're trying to create is a pure procedure (shared text) file that's currently being executed. (Ref# J1643259, J2379084)
dlopen()
If an error occurs, this function now consistently sets the error information that dlerror() reports on. (Ref# J186128)
exec*()
  • The new process image now inherits any signals that were pending for the calling process, as required by POSIX. (Ref# J163197)
  • The kernel now correctly handles signals that are sent to a process while it's in the process of an exec*() operation. (Ref# J1469296)
  • As required by POSIX, these functions now fail with an error of E2BIG if the length of the argument list and the environment is greater than ARG_MAX. (Ref# J2157462)
fcntl()
This function now correctly locks and unlocks regions of files. (Ref# J1673786)
fork()
This function now correctly sets errno to EPERM if the priority is too high. (Ref# J1601362)
fslog()
Calling procmgr_daemon() after calling this function no longer closes the file descriptor for the system logger, so log messages now go to the right place (Ref# J183838)
fstat()
Changes to a FIFO made directly on the underlying file system (chmod(), chown(), unlink()) are now reflected in the information returned by a call to fstat() that goes directly to the pipe resource manager. An fstat() call on a pipe now correctly returns 0 in the st_nlink field. (Ref# J2230953, J2227283)
fwrite()
This function now correctly returns the number of complete elements successfully written, even if an error occurred. (Ref# J2163162)
getc_unlocked(), getchar_unlocked()
These functions no longer block when you call them after locking the file by calling flockfile(). (Ref# J2213786)
getgrgid_r(), getgrnam_r(), getpwnam_r(), getpwuid_r()
If you call these functions with a buffer size of 0, the result pointer is now set to NULL as required by POSIX. (Ref# J2208378)
gulliver.h, aarch64/inout.h
We've fixed some compilation problems that occurred with these header files. (Ref# J1634908)
InterruptAttachEvent()
The ability to attach an interrupt event is now governed by PROCMGR_AID_INTERRUPTEVENT or PROCMGR_AID_INTERRUPT. (Ref# J2221557, J2243993)
lio_listio()
This function now correctly handles a NULL sig argument when the LIO_NOWAIT flag is set. (Ref# J1689215)
malloc()
This function now returns 16-byte-aligned pointers on 64-bit systems. (Ref# J2194461)
mktime()
We've corrected an overflow that occurred when you passed INT_MAX as the year. (Ref# J1692237)
mmap()
The virtual memory manager now correctly determines the size when you mmap() a block device. (Ref# J2162973)
Msg*()
The kernel calls that take a rcvid as an argument are supposed to be called from a server thread; you can no longer successfully call them from a client thread. (Ref# J554759)
MsgDeliverEvent(), MsgSendPulse()
These functions now give an error of EINVAL for priority values of NUM_PRI or higher. (Ref# J1682707)
MsgError()
If a server received an unblock pulse, it can now call MsgError() with an error argument of -1. In this case, MsgError() sets the client's errno to whatever error the kernel logged when the unblock pulse was sent. (Ref# J164943, J2142425)
MsgSendPulse(), MsgSendPulsePtr()
In order to send a pulse to a process, the sending process's effective user ID must match the real, effective, and saved user IDs of the receiving process, or the sending process must have the PROCMGR_AID_CONNECTION ability enabled. (Ref# J2202766)
MsgWrite()
This function no longer crashes when you use it with destination I/O vectors of more than two elements on a system with more than 21 cores. (Ref# J2178038)
nanospin()
This function now correctly handles times between 40 ms and 500 ms. Note that these times are far longer than you should use this function for; use the POSIX timer_*() functions for times of more than a few milliseconds. (Ref# J175389)
open()
For conformance with POSIX, this function now sets errno to ETXTBSY if the file you're trying to open is a pure procedure (shared text) file that's currently being executed and oflag is O_WRONLY or O_RDWR. (Ref# J1643259, J2379084)
posix_spawn(), posix_spawnp()
  • These functions now return ENAMETOOLONG if either of the following occurs:
    • The length of the path argument is greater than PATH_MAX.
    • An action set with posix_spawn_file_actions_addopen() has a path that's longer than PATH_MAX.

    (Ref# J2141833, J2141843)

  • You no longer get spurious messages in message queues when you use these functions to spawn a process that has an mq connection. (Ref# J2205519)
  • If you use posix_spawnattr_settypeid() to set the type ID to SECPOL_INVALID_TYPE, posix_spawn() now correctly fails. (Ref# J2205583)
posix_typed_mem_get_info()
  • Calling this function for a typed memory object that contains a page within the last 16 MB of system RAM within its [start, end] range no longer causes the kernel to crash. (Ref# J2203456, J2207294)
  • This function now gives correct values for the total and length. (Ref# J2203447)
procmgr_ability()
  • Note that if priority inheritance occurs, whether due to message passing or an attempt to lock a mutex, if a thread that doesn't have the PROCMGR_AID_PRIORITY ability enabled is supposed to be boosted to a privileged priority, it's actually boosted to the highest unprivileged priority. (Ref# J2500233)
  • The PROCMGR_AID_IO ability now has a level for its subrange. A level of 0 lets you call ThreadCtl(_NTO_TCTL_IO, 0); a level of 1 lets you call ThreadCtl(_NTO_TCTL_IO_PRIV, 0). (Ref# J2251203)
  • The subrange for the PROCMGR_AID_XPROCESS_QUERY ability is now effective user IDs instead of process IDs. Your process now needs this ability enabled in order to call clock_gettime() or ClockTime() to get the time for a process or thread CPU-time clock that belongs to a different user, aside from the kernel's idle thread. (Ref# J2161913, J2198684)
  • In order to create a timer that sends a pulse to a process belonging to a different user, your process must now have the PROCMGR_AID_CONNECTION ability enabled. (Ref# J2260109)
  • New: The following abilities are new:
    Identifier Privileged? Controls the process's ability to: Subrange (optional)
    PROCMGR_AID_ABLE_PRIV Yes Enable a currently denied privileged ability, add subranges to such an ability, or inherit such an ability
    PROCMGR_AID_HIGH_RESOLUTION_TIMER Yes Set the timer tolerance to a value between 0 and the clock period, by calling timer_settime(), timer_timeout(), TimerSettime(), or TimerTimeout()
    PROCMGR_AID_INTERRUPTEVENT Yes Attach interrupt events by calling InterruptAttachEvent(). You also need I/O privileges; see PROCMGR_AID_IO. Interrupt sources
    PROCMGR_AID_XPROCESS_ABLE Yes Change the abilities of another process.
    PROCMGR_AID_XPROCESS_DEBUG Yes Open for writing the /proc/pid/as or /proc/pid/ctl files of another process that's running as a different user ID than the requesting process. Doing this is required to debug a process. User IDs that can be accessed
    PROCMGR_AID_XPROCESS_MEM_READ Yes Open for reading the /proc/pid/as file of another process that's running as a different user ID than the requesting process. This ability is required to create core files, for full pidin functionality, and for debugging another process. User IDs that can be accessed
pthread.h
PTHREAD_MUTEX_DEFAULT, PTHREAD_MUTEX_ERRORCHECK, PTHREAD_MUTEX_RECURSIVE, and PTHREAD_MUTEX_NORMAL are now included when you compile for POSIX 1003.1-2001 compliance. (Ref# J2211714)
pthread_attr_setstack()
If you use this function to provide a stack for a thread, the kernel no longer reduces the size to take a guard page into account. (Ref# J385192)
pthread_barrierattr_setpshared(), pthread_condattr_setpshared(), pthread_mutexattr_setpshared(), pthread_rwlockattr_setpshared()
If you started procnto with the -s option, you should set the process-shared attribute to PTHREAD_PROCESS_SHARED for any barriers, condition variables, mutexes and read-write locks that are shared between processes. (Ref# J2250677)
pthread_cond_broadcast()
This function now returns EINVAL if you pass it an unitialized condvar. (Ref# J1691149)
pthread_create(), ThreadCreate()
These functions now give an error of EAGAIN if you try to create more than the maximum number of threads in a process. (Ref# J1567443, J2168499)
pthread_mutex_destroy()
Calling this function on an already-destroyed mutex now returns EINVAL. (Ref# J1691484)
pthread_exit()
Any event that you registered for the thread with the _NTO_TCTL_ADD_EXIT_EVENT ThreadCtl() command is delivered when the thread is terminated. (Ref# J2179397, J2179400, J2212195)
pthread_mutex_lock(), pthread_mutex_trylock()
  • These functions now fail with an error of EINVAL if the mutex was created with the a protocol attribute of PTHREAD_PRIO_PROTECT and the calling thread's priority is higher than the mutex's current priority ceiling. (Ref# J1691127)
  • We've corrected a race condition where a fork() could cause pthread_mutex_lock() to fail with an error of EINVAL. (Ref# J1630199, J2182813)
  • This function now gives an error of EINVAL if you're using safe shared mutexes, and you tried to lock a priority-impacting mutex whose owner isn't known to the kernel and that the locking thread claims is from a different process. (Ref# J1614329, J2250677)
pthread_mutex_unlock()
  • We've corrected an issue that sometimes caused this function to fail for a robust mutex. (Ref# J2254363)
  • This function now returns EINVAL if you try to unlock a destroyed mutex. (Ref# J1691472)
regcomp()
This function no longer writes outside of the array it allocates for categories if you use a negative character in a regular expression. This function is in libc.a, but not in libc.so, so you need to recompile the code that uses it. (Ref# J1701452)
resmgr_msgreplyv()
The struct iovec * argument to resmgr_msgreplyv() is now marked as const. (Ref# J2181687)
SchedCtl()
There's a new SCHED_CONFIGURE command that lets you fine-tune the thread scheduler. For more information, see below. (Ref# J1689586)
sem_timedwait()
  • This function now gives an error of EINVAL if you pass it an invalid time specification for a named semaphore. (Ref# J2141105)
  • This function now indicates the correct error if it times out when called for a named semaphore. (Ref# J164943)
setegid(), setgid(), setregid()
A process can set its real or effective group ID to any of its real, effective, or saved set-group IDs or to any ID identified by the PROCMGR_AID_SETGID ability. (Ref# J2275716)
seteuid(), setuid(), setreuid()
A process can set its real or effective user ID to any of its real, effective, or saved set-user IDs or to any ID identified by the PROCMGR_AID_SETUID ability. (Ref# J2275716)
setvbuf()
Input is now correctly unbuffered when you specify _IONBUF. (Ref# J2215438)
SignalWaitinfo(), sigwaitinfo()
These functions now initialize the si_error member of the siginfo_t structure to 0. (Ref# J183358)
slog2f()
New: This function now inteprets %F as the specifier for a floating-point number. (Ref# J1699770)
slogf()
If you call slogf(), and you then call procmgr_daemon() without the PROCMGR_DAEMON_NOCLOSE flag, all file descriptors other than stdin, stdout, and stderr are closed. We now invalidate the global logger file descriptor, so that if you open a new logger file, then log messages are sent to the correct file. (Ref# J167521)
snprintf()
As required by POSIX, this function now gives an error of EOVERFLOW if the given size of the buffer is greater than INT_MAX (Ref# J2190684)
spawn()
We've corrected a race that could cause spawn() to erroneously fail internally when called with invalid arguments. (Ref# J2343305)
SyncMutexEvent()
In order to use this function with a SIGEV_PULSE sigevent that's going to a channel that was created by a different process whose real, effective, or saved user ID doesn't match your process's effective user ID, your process must have the PROCMGR_AID_CONNECTION ability enabled. (Ref# J2202766)
SyncMutexLock()
This function now gives an error of EINVAL if you're using safe shared mutexes, and you tried to lock a priority-impacting mutex whose owner isn't known to the kernel and that the locking thread claims is from a different process. (Ref# J1614329, 2250677)
sysconf()
This function now supports the set of system variables required by POSIX. (Ref# J1467232)
syslog()
  • You can now use this function to create symbolic links that are more than 512 bytes long. (Ref# J2238382)
  • This function now checks the return code from write(). If the call failed, syslog() now reopens the file descriptor for the system log and tries to write the data again. It does this up to three times; if all the attempts fail, errno is set. (Ref# J1529157)
tc*()
These functions now send SIGTTOU signals under the correct circumstances. (Ref# J1664787)
ThreadCtl()
  • Attempts to use the _NTO_TCTL_ONE_THREAD_HOLD, _NTO_TCTL_THREADS_HOLD, _NTO_TCTL_ONE_THREAD_CONT, or _NTO_TCTL_THREADS_CONT command on procnto's threads now result in an error of EPERM. (Ref# J2208110)
  • The following commands are new:
    • _NTO_TCTL_ADD_EXIT_EVENT (Ref# J2179397, J2179400, J2212195)
    • _NTO_TCTL_DEL_EXIT_EVENT (Ref# J2179397, J2179400, J2212195)
  • In order for you to use the _NTO_TCTL_IO command, you must have the PROCMGR_AID_IO ability enabled with a subrange of 0; in order to use _NTO_TCTL_IO_PRIV, you must have the PROCMGR_AID_IO ability enabled with a subrange of 1. (Ref# J2251203)
ThreadDestroy()
Any event that you registered for the thread with the _NTO_TCTL_ADD_EXIT_EVENT ThreadCtl() command is delivered when the thread is terminated. (Ref# J2179397, J2179400, J2212195)
timer_create(), TimerCreate()
  • You can now create a timer for a thread CPU-time clock ID. The timeout that you set for this type of timer represents the thread's execution budget. (Ref# J2179406, J2179415, J2212198, J2212200)
  • In order to create a timer that sends a pulse to a process belonging to a different user, your process must now have the PROCMGR_AID_CONNECTION ability enabled. (Ref# J2260109)
timer_settime(), timer_timeout(), TimerSettime(), TimerTimeout()
In order to set the timer tolerance to a value between 0 and the clock period, you need to have the PROCMGR_AID_HIGH_RESOLUTION_TIMER ability enabled. (Ref# J2395491, J2453450)
TimerInfo()
If the timer's tolerance is greater than 0 and less than one tick, the flags member of the _timer_info structure now includes the _NTO_TI_HIGH_RESOLUTION bit. (Ref# J2394940, J2459259)
trace_logf(), trace_nlogf(), trace_vnlogf()
New: You can't use any floating-point formatting codes or parameters with these functions; use a function such as sprintf() to format floating-point data in a temporary buffer, and then pass it to trace_logf(), trace_nlogf(), or trace_vnlogf() using a %s format. (Ref# J2233903)
UNALIGNED_RET32()
This macro now correctly gives an unsigned value. (Ref# J2163746)
uname()
If you're running a safety version of the kernel, the release member of the utsname structure now includes an uppercase "S". (Ref# J2504408, J2504421)
wcsftime()
This function now calls tzset() to set the time zone. (Ref# J2142145)
wcstombs()
This function now returns the correct value when you pass it a NULL output pointer. (Ref# J2141541)
wordexp(), wordfree()
Because of security concerns, the versions of wordexp() and wordfree() in libc.a are stubs that do nothing, and libc.so doesn't include them at all. We recommend that you not use them. (Ref# J1468916, J2144181)

Experimental items


Caution: Experimental software is primarily provided for customers and the community to try out, and perhaps to get a glimpse of what might be in store for the future. For information about the use of experimental software, see the Commercial Software License Agreement (CSLA) or Partner Software License Agreement (PSLA) in the Licensing area of our website, http://www.qnx.com/legal/licensing/.

The experimental items in this release are:

  • QNX framework for integrity measurement (QFIM)
  • New: Trusted Platform Module (TPM)

    Note: The version of TPM that shipped with SDP 7.0 is deprecated and will be replaced with a different TPM service in a future release.

Known issues

  • New: The L memory configuration option for procnto is a superset of the l option. You can combine them, but they're processed in the order that you specify them, resulting in different combinations of locking and superlocking. The L~l combination is invalid because memory is superlocked but not locked; don't use it. (Ref# J2746113)
  • New: Specifying the ARM_SHMCTL_SO bit in the special argument to shm_ctl_special() should result in strongly ordered mappings but currently doesn't. (Ref# J2740401)

    Workaround: Also set ARM_SHMCTL_SH. Adding this bit results in strongly ordered mappings and doesn't remove functionality because non-cacheable mappings are always outer-shareable (i.e., you can't get a non-shareable non-cacheable mapping anyway).

  • If you've enabled tickless mode, and an interrupt arrives on a processor other than processor 0 while the OS is in tickless mode, the kernel might not return to normal mode. (Ref# J2504279)

    Workaround: Do one of the following:

    • Use a hardware timer that can be accessed from any processor.
    • If the clock tick interrupt must happen on processor 0, bind all interrupts to that processor.
    • Don't enable tickless mode; that is, don't use the startup-* -Z option.
  • When a client thread makes multiple notification requests on the same device, if one of the file descriptors is closed while the client is blocked in select() or poll(), then the client might not be unblocked. For select(), it's unblocked only if the highest-numbered file descriptor is closed. For poll(), it may depend on the order the file descriptors are listed in the pollfd array. (Ref# J2483089)

    Workaround: This is an unusual thing to do, and it's best to avoid it. Don't use select() or poll() on multiple fds to the same device, or if you do, don't close() one of those fds while blocked on it.

  • If any process in your system uses high-resolution timers, the expiry of normal-resolution timers may be delayed. This is most pronounced if the high-resolution timer repeats and the normal-resolution timer repeats at an interval that's close to the tick size. (Ref# J2394360)
  • After a high-resolution timer expires, if there are no more high-resolution timers in the near future, the kernel returns to normal 1 ms ticking, but it doesn't try to return to the "normal" 1 ms offset, so as to avoid adjusting the hardware unnecessarily. For example, the clock ticks might be as follows:
    1
    2
    3
    4   // Set a high-resolution timer for 500 us from here
    4.5 // Fire the high-resolution timer. Return to normal 1 ms ticks.
    5.5
    6.5
      

    This may cause clock_gettime() to return times that aren't a multiple of the clock resolution returned by clock_getres(). (Ref# J2416004)

  • The documentation for clock_settime() and ClockTime() should mention that if you change the time for CLOCK_REALTIME, the change occurs immediately, and the expiry time for some timers might end up in the past. In this case, the timers will expire on the next clock tick. (Ref# J2415067)

    Workaround: If you need the affected timers to expire before the next clock tick, then before changing the time, set a high-resolution timer to expire just after the new time; after you change the time, the high-resolution timer will expire and so will all the affected timers.

  • The kernel doesn't recognize orphaned process groups (process groups in which the parent of every member is either itself a member of the group or isn't a member of the group's session) as required by POSIX. (Ref# J165028)

  • When you build an ARMv7 (32-bit) application, in some cases, the compiler generates ARM Thumb instructions as a size optimization. Attempts to call memcpy_isr() or memset_isr() from Thumb build code could fail or not return properly. A failed function call is indicated by an application crash. (Ref# J2520502)

    Workaround: This issue only occurs when you link against the static variant of libcS.a (or libc.a). Use one of the following methods to avoid it:

    • Make sure the calling code is of the same type (non-Thumb) as the destination function by building the object with -marm specified to the compiler.
    • Link against the dynamic version of libc (libc.so).
  • If an application tags functions and causes their contents to move to a non-standard section of an ELF binary using the GCC pragma option for the __attribute__ keyword (in the .preinit_array initialization section) and is linked against the static libc.a, then those pre-initialization functions do not execute (normal constructors and destructors execute as usual). In most cases, .preinit_array is used only for system or OS implementations for bootstrapping and not for general application development. (Ref# J2520529)

    Workaround: If you need to use the .preinit_array section, link to the dynamic libc.so to ensure that the pre-constructor calls are executed.

  • If thread cancelation is attempted during an I/O routine (like fprintf), it's possible that not all the locks associated with that file I/O stream will be released before the thread termination is complete. While ultimately these resources are cleaned up when the process is terminated, there is a period of time between when a thread is being canceled and the process is being terminated when the locks that protect resources related to the I/O routine are held inside of libc. (Ref# J2520540)

    Workaround: If an application must use cancelation APIs (such as pthread_cancel()), make sure that the state of the cancelation is tightly controlled using pthread_setcancelstate() and only allow cancelation at specific control points in the application's state machine.

  • New: In <time.h>, there's a macro named timespec2nsec(), which gets called instead of the C Library function with the same name and method signature. In this release, the macro implementation has unexpected behavior sometimes because it evaluates its argument twice. So, code that would be valid for calling the C function is not valid for calling the macro. (Ref# J2642295)

    Workaround: For the argument, avoid using code that may have a different meaning or become undefined when evaluated for the second time. An example would be using increment or decrement operators in an array index that references a timespec structure, as in the following statement:

    timespec2nsec((struct timespec*)attribList[i++]);
    
  • New: Some tc*() (terminal interface) functions are implemented as having cancellation points when according to POSIX, they must not or should not have them. This issue is being fixed in a newer version of QNX SDP.

    The terminal interface functions that incorrectly do have cancellation points include:

    • tcdrain()
    • tcgetsid()
    • tcsetsize()
    • tcgetsize()
    • tcinject()
    • tcischars()

    In the documentation, the safety table for each listed function has a No value in the Cancellation point column. This should be used as a guideline in writing programs that use these functions; they should not be used as cancellation points. (Ref# J2711220)

  • New: If you don't specify a leading slash in the name and you aren't in the root directory, posix_typed_mem_open() fails to open the typed memory object. (Ref# J1035253)

    Workaround: Start the name with a slash.

  • New: The definition of USHRT_MAX in <limits.h> is incorrect; it should be as follows:
    #if __INT_BITS__-0 <= 16
    #define USHRT_MAX   65535U       /*  maximum value of an unsigned short  */
    #else
    #define USHRT_MAX   65535      /*  maximum value of an unsigned short  */
    #endif
      

    (Ref# J2753844, J2753845)

See also the release notes for the QNX Software Development Platform 7.0.

Verification issues

These are the known verification issues that you may encounter when verifying your installation with this update:

  • New: Some files in Connectivity - PCI Utils (Build 7.0.21) are incompatible with the QNX SDP 7.0 OS services - OS services base (7.0.629.S201802061654) package from this update because of a packaging error. (Ref# J2534523, J2789982, J2897868, J2807479)

    The version of each of the following files:
    • rsrcdb_query
    • rsrcdb_query.sym
    will result in verification issues if you have QNX SDP 7.0Connectivity - PCI Utils (7.0 BuildID 21) already in your installation.

    We recommend that you install an updated version of QNX SDP 7.0 OS services - OS services base to a BuildID that's 7.0.991.S201809180259 or higher:

    1. From QNX Software Center, select your Updates or Available tab.
    2. Navigate to the QNX SDP 7.0 OS services - OS services base package (e.g., by typing "os services base" in the Search field).

      If you're unable to locate QNX SDP 7.0 OS services - OS services base from specified tabs, select the All tab and look for the package under the Updates: or Available: categories.

    3. Right-click and select Install on the QNX SDP 7.0 OS services - OS services base (7.0.991.S201809180259 or higher) package.

      If you perform subsequent Verify Installation actions on your installation, you will no longer see any verification issues related the following files for the QNX SDP 7.0 Connectivity - PCI Utils (7.0 BuildID 21) package.


      Note: For any other verification issues that QNX Software Center reports, you need to investigate.

    See the myQNX License Manager and QNX Software Center User's Guide for more information on how to install a package.

Additional documentation

Here's some information that will be added to future versions of the product documentation.

Secure events

In a deeply embedded system that consists entirely of trusted programs, no safeguards are needed on events, but this isn't the case in systems that are more open. One problem with sigevents is that a server can deliver any event to a client, even if the client has never expressed an interest in receiving events. In QNX Neutrino 7.0.1 or later, the client can register events to make them more secure, as follows:

  1. The client sets up the sigevent to indicate how it wants to be notified.
  2. If the client wants to allow the server to update the value associated with the event, it sets the SIGEV_FLAG_UPDATEABLE flag in the event.
  3. The client registers an event by calling MsgRegisterEvent(). This function registers the given sigevent with the kernel, which stores the event internally and provides a handle for the event.
  4. To prevent the delivery of unregistered events, the client sets the _NTO_COF_REG_EVENTS flag when it calls ConnectAttach() to connect to the server.
  5. The client gives the handle instead of the actual event to the server.
  6. If the client set the SIGEV_FLAG_UPDATEABLE flag on the event, then the server is allowed to update the value included in the registered event.
  7. When the server calls MsgDeliverEvent() with a handle, the kernel looks up the handle. If the handle exists and the client has allowed the server to deliver it, the kernel delivers the corresponding registered event to the client.
  8. When the client no longer needs the secure event, it can call MsgUnregisterEvent() to remove it and unregister the handle.

The client can thus be sure that it gets only the events that it wants, and that no one has tampered with them.


Note: We plan to phase out the use of unregistered events.

MsgRegisterEvent()

Register a secure event

Synopsis:

#include <sys/neutrino.h>

int MsgRegisterEvent( struct sigevent *event,
                      int coid );

int MsgRegisterEvent_r( struct sigevent *event,
                        int coid );

Arguments:

event
A pointer to the sigevent that you want to register as a secure event.
coid
The connection ID to the only server permitted to send the event, or -1 to allow all servers to send it.

Library:

libc

Use the -l c option to qcc to link against this library. This library is usually included automatically.

Description:

The MsgRegisterEvent() and MsgRegisterEvent_r() kernel calls register the given sigevent as a secure event. These functions are identical except in the way they indicate errors. See the Returns section for details.

MsgRegisterEvent() registers the given sigevent with the kernel and converts it into a handle that you can use in any API that passes an event. When the server calls MsgDeliverEvent() with a handle event, the kernel looks up the handle. If the handle exists and the server is allowed to deliver it, the kernel delivers the registered event.


Note: A server is allowed to provide its own value to the registered event if and only if the SIGEV_FLAG_UPDATEABLE flag is set on the event.

You can use MsgUnregisterEvent() to unregister the secure event.

Blocking states

None.

Returns:

0 on success. If an error occurs:

  • MsgRegisterEvent() returns -1 and sets errno.
  • MsgRegisterEvent_r() returns a value from the Errors section and doesn't set errno.

Errors:

EAGAIN
No more events can be registered for the process. See the RLIMIT_SIGEVENT_NP resource for setrlimit().
EINVAL
One of the following occurred:
  • The size of the event doesn't match the size of a sigevent for the current architecture.
  • The connection ID is invalid.
  • The event is already registered as a secure event.
ENOMEM
There wasn't enoungh memory available to register the event.

Examples:

In this example, the parent process acts as the server, and the child acts as the client.

#include <stdio.h>
#include <stdint.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <sys/neutrino.h>
#include <sys/wait.h>

struct message_s
{
    uint16_t        type;
    uint16_t        subtype;
    struct sigevent event;
    struct sigevent updateable;
    struct sigevent forbidden;
};

static int
child(int chid)
{
    // Mask SIGUSR1 and SIGUSR2.
    sigset_t    sigset;
    siginfo_t   siginfo;
    sigemptyset(&sigset);
    sigaddset(&sigset, SIGUSR1);
    sigaddset(&sigset, SIGUSR2);

    if (sigprocmask(SIG_BLOCK, &sigset, NULL) < 0) {
        perror("sigprocmask");
        return 1;
    }

    // Attach to the server, indicating that we don't want unregistered events.
    int coid = ConnectAttach(0, getppid(), chid, _NTO_SIDE_CHANNEL,
                             _NTO_COF_REG_EVENTS);
    if (coid < 0) {
        perror("ConnectAttach");
        return 1;
    }

    // Register three events:
    // 1. Non-updateable, on a connection to the parent server
    // 2. Updateable, on a connection to the parent server
    // 3. On a connection to the memory manager
    struct message_s    msg = { .type = 1 };

    // First event.
    SIGEV_SIGNAL_VALUE_INIT(&msg.event, SIGUSR1, 13);

    if (MsgRegisterEvent(&msg.event, coid) < 0) {
        perror("MsgRegisterEvent(&msg.event)");
        return 1;
    }

    // Second event.
    SIGEV_SIGNAL_VALUE_INIT(&msg.updateable, SIGUSR2, 34);
    SIGEV_MAKE_UPDATEABLE(&msg.updateable);

    if (MsgRegisterEvent(&msg.updateable, coid) < 0) {
        perror("MsgRegisterEvent(&msg.updateable)");
        return 1;
    }

    // Third event.
    SIGEV_SIGNAL_VALUE_INIT(&msg.forbidden, SIGUSR2, 34);

    if (MsgRegisterEvent(&msg.forbidden, SYSMGR_COID) < 0) {
        perror("MsgRegisterEvent(&msg.forbidden)");
        return 1;
    }

    // Send the server a message that includes the sigevents.
    if (MsgSend(coid, &msg, sizeof(msg), NULL, 0) < 0) {
        perror("MsgSend");
        return 1;
    }

    // Wait for event delivery.
    int i = 0;
    for (i = 0; i < 2; i++) {
        printf("Waiting for SIGUSR1/SIGUSR2n");
        if (sigwaitinfo(&sigset, &siginfo) < 0) {
            perror("sigwaitinfo");
            return 1;
        }

        switch (siginfo.si_signo) {
        case SIGUSR1:
            printf("Received SIGUSR1 with value %dn", siginfo.si_value.sival_int);
            if (siginfo.si_value.sival_int != 13) {
                fprintf(stderr, "SIGUSR1: Expected value 13, got %dn",
                        siginfo.si_value.sival_int);
                return 1;
            }
            break;

        case SIGUSR2:
            printf("Received SIGUSR2 with value %dn", siginfo.si_value.sival_int);
            if (siginfo.si_value.sival_int != 57) {
                fprintf(stderr, "SIGUSR1: Expected value 57, got %dn",
                        siginfo.si_value.sival_int);
                return 1;
            }
            break;

        default:
            fprintf(stderr, "Unexpected signal %dn", siginfo.si_signo);
            return 1;
        }
    }

    return 0;
}

int
main(int argc, char **argv)
{
    // Create a channel.
    int chid = ChannelCreate(0);
    if (chid < 0) {
        perror("ChannelCreate");
        return 1;
    }

    // Create a child process.
    pid_t   pid = fork();
    if (pid < 0) {
        perror("fork");
        return 1;
    }

    if (pid == 0) {
        return child(chid);
    }

    // Handle messages from the child.
    int kill_child = 1;
    for (;;) {
        struct message_s    msg;
        int                 rcvid;

        rcvid = MsgReceive(chid, &msg, sizeof(msg), NULL);
        if (rcvid < 0) {
            perror("MsgReceive");
            break;
        }

        if (rcvid == 0) {
            continue;
        }

        printf("Received message type %dn", msg.type);
        MsgReply(rcvid, 0, NULL, 0);
        kill_child = 0;

        // Try a handle for an event that this server isn't allowed to send.
        if ((MsgDeliverEvent(rcvid, &msg.forbidden) != -1) || (errno != EACCES)) {
            perror("MsgDeliverEvent(forbidden)");
            break;
        }

        // Try an unregistered event. It shouldn't be delivered.
        struct sigevent ev;
        SIGEV_SIGNAL_INIT(&ev, SIGUSR1);
        if ((MsgDeliverEvent(rcvid, &ev) != -1) || (errno != EACCES)) {
            perror("MsgDeliverEvent(unregistered)");
            break;
        }

        // Try a legitimate handle that doesn't have an updateable value.
        // The client should get the original value that it registered.
        msg.event.sigev_value.sival_int = 57;
        if (MsgDeliverEvent(rcvid, &msg.event) < 0) {
            perror("MsgDeliverEvent");
            break;
        }

        // Try a legitimate handle that has an updateable value.
        msg.updateable.sigev_value.sival_int = 57;
        if (MsgDeliverEvent(rcvid, &msg.updateable) < 0) {
            perror("MsgDeliverEvent");
            break;
        }

        break;
    }

    if (kill_child) {
        kill(pid, SIGKILL);
    }

    int     status;
    pid_t   wpid = waitpid(pid, &status, 0);
    if (wpid != pid) {
        perror("waitpid");
        return 1;
    }

    printf("Child exited with status %dn", WEXITSTATUS(status));
    return 0;
}

Classification:

QNX Neutrino

Safety:
Cancellation pointNo
Interrupt handlerNo
Signal handlerYes
ThreadYes

MsgUnregisterEvent()

Unregister a secure event

Synopsis:

#include <sys/neutrino.h>

int MsgUnregisterEvent( const struct sigevent *event );

int MsgUnregisterEvent_r( const struct sigevent *event );

Arguments:

event
A pointer to the sigevent that contains the handle of the secure event that you want to unregister.

Library:

libc

Use the -l c option to qcc to link against this library. This library is usually included automatically.

Description:

The MsgUnregisterEvent() and MsgUnregisterEvent_r() kernel calls unregister the secure event associated with the given handle. These functions are identical except in the way they indicate errors. See the Returns section for details.

To register an event, call MsgRegisterEvent().

Blocking states

None.

Returns:

0 on success. If an error occurs:

  • MsgUnregisterEvent() returns -1 and sets errno.
  • MsgUnregisterEvent_r() returns a value from the Errors section and doesn't set errno.

Errors:

ESRCH
The event associated with the handle couldn't be found.

Classification:

QNX Neutrino

Safety:
Cancellation pointNo
Interrupt handlerNo
Signal handlerYes
ThreadYes

Safely sharing mutexes, barriers, and reader/writer locks between processes

You can share most synchronization objects between processes, but security can be a concern.

Most of this discussion involves mutexes, but barriers and reader/writer locks are built from mutexes, so it applies to them too.


Note: In order for processes to share a synchronization object, they must also share the memory in which it resides.

The problem with shared mutexes is that a thread in any process can claim that another thread (in any process) owns the mutex; when priority inheritance is applied, the latter's priority is boosted to that of the former. If an application needs to share a mutex between separate processes, then it must decide whether to expose itself to potential interference from unrelated processes, disable priority inheritance on the shared mutex, or force all operations on the shared mutex to enter the kernel—which they don't normally do—resulting in a performance penalty.

You can now configure the kernel to reject attempts to lock shared mutexes that would cause priority inheritance unless all mutex locking operations enter the kernel. This has a significant impact on the performance of shared mutexes that use the priority-inheritence protocol, but guarantees that noncooperating threads can't interfere with each other. In order to use a shared mutex in a safe manner, you must do the following:

  • Specify the -s option for procnto to force all mutex operations on shared mutexes that support priority inheritance to enter the kernel.
  • Either disable priority inheritance for the mutex by using pthread_mutexattr_setprotocol() to set the PTHREAD_PRIO_NONE flag, or explicitly identify the mutex as shared by using pthread_mutexattr_setpshared() to set the process-shared attribute to PTHREAD_PROCESS_SHARED.

If you specify the -s option, the kernel rejects any attempts to lock a PTHREAD_PRIO_INHERIT mutex that doesn't have PTHREAD_PROCESS_SHARED set. In this case, pthread_mutex_lock() and SyncMutexLock() give an error of EINVAL.

Similarly, if you use procnto's -s option, and you share a barrier or reader/writer lock between processes, you should use pthread_barrierattr_setpshared() or pthread_rwlockattr_setpshared() to set its process-shared attribute to PTHREAD_PROCESS_SHARED.

SCHED_CONFIGURE command for SchedCtl()

As a trade-off between latency and throughput, the scheduling algorithm makes decisions that people expecting strict priority scheduling occasionally find surprising:

  • A high-priority thread T that becomes ready on CPU A doesn't always displace a lower-priority thread running on that CPU. Under some circumstances, an interprocess interrupt (IPI) is sent to CPU B to force it to perform a new scheduling decision, which is expected (but not guaranteed) to cause thread T to run on CPU B.
  • If a thread T running on CPU A is displaced by a higher-priority thread, and a thread with a priority lower than T's is running on CPU B, CPU B isn't signalled to reevaluate its scheduling decision.

The end result is that only the highest-priority thread in the system is guaranteed to run on some CPU, while the other CPUs may be running threads with lower priority than those ready to run at any given point in time. The SCHED_CONFIGURE command provides some parameters that you can use to tune the scheduler.


Note: Your process must have the PROCMGR_AID_SCHEDULE ability enabled (see procmgr_ability()).

The data argument for this command must be a pointer to a struct sched_config structure. It includes the following members:

int32_t low_latency_priority
The priority threshold above which a thread always displaces a lower-priority thread running on the CPU on which it becomes ready. Effectively, this parameter allows for a priority class to be defined as low latency, avoiding the overhead of an IPI and a new scheduling decision by another CPU.
int32_t migrate_priority
The priority threshold above which a preempted thread will be rescheduled on another CPU (the one running the lowest priority thread).

Keeping both parameters at their default values (INT_MAX) maintains the scheduling behavior described above. Setting both to 0 results in a strict-priority scheduling system, where the N highest-priority threads run on the N available CPUs. Intermediate values create a system with classes of priorities that behave differently, as it's expected that low-priority threads are used for higher throughput, and high-priority threads for low latency.

Errors:

SchedCtl() and SchedCtl_r() indicate the following errors for the SCHED_CONFIGURE command (see the Returns section for details):

EOK
Success.
EINVAL
The size of the parameter block doesn't match the size of the expected structure.
EPERM
You don't have the PROCMGR_AID_SCHEDULE ability enabled.

Technical support

To obtain technical support for any QNX product, visit the Support area on our website (www.qnx.com). You'll find a wide range of support options, including community forums.

For questions about installing and using QNX SDP, see the Getting Started forum on our Foundry27 site, http://community.qnx.com. There are other forums for specific topics, including the QNX Neutrino RTOS, development tools, networking, Board Support Packages, and so on.