123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595 |
- Introduction
- ============
- The Shared Memory (SMEM) protocol allows multiple processors in Qualcomm
- Technologies, Inc. MSM System on Chips to communicate at a low level using a
- segment of shared system memory that is accessible by any of the processors.
- This is accomplished by an operating-system independent mechanism that allows a
- client on any processor to dynamically allocate a block of memory from shared
- system memory which is then visible and accessible to clients on other
- processors for the purpose of storing and exchanging data.
- +-------------+
- SMEM | Processor 1 |
- +---------+ +------------+ +-------------+
- | |+--------------->| Item 1 |<----------------+ ^
- | Linux | | | |
- | |<---------------+| |+-------------------+
- +---------+ +------------+
- ^ + | Item 2 | +-------------+
- | | | |<--------------+| Processor 2 |
- | | | |+-------------->| |
- | | | | +-------------+
- | | | |
- | | | | +-------------+
- | | +------------+ | Processor 3 |
- | | . +-------------+
- | | . + ^ .
- | | +------------+ | | .
- | +----------------->| Item N |<-----------------+ | .
- +---------------------+| |+--------------------+ .
- | | +-------------+
- | |+-------------->| |
- | | | Processor N |
- | |<--------------+| |
- +------------+ +-------------+
- The SMEM driver supports all known versions of the SMEM protocol.
- Hardware description
- ====================
- The SMEM protocol requires a contiguous segment of system memory that is
- accessible by both the local processor and one or more remote processors.
- Each processor supporting the SMEM protocol must configure their MMUs and other
- applicable hardware such that accesses to shared memory are non-cacheable.
- Optionally, additional segments of system memory may be provided to act as
- auxiliary memory areas for the SMEM protocol. Such segments may provide
- performance benefits to certain processors by optimizing access latency. Such
- auxiliary memory areas must be a slave to the single main SMEM area.
- While the SMEM protocol has provisions for software-based remote spinlocks to
- manage synchronization between processors, this functionality may be
- substituted with dedicated hardware. Such hardware is expected to be managed
- by another driver providing a standardized API.
- Software description
- ====================
- At its core, the SMEM protocol is a heap memory management system. The core
- functionality consists of allocating segments of memory, and lookup operations
- to find the address of existing segments of memory. There is no provision to
- free memory that is allocated.
- Allocated segments of memory are called SMEM items. Each SMEM item has a unique
- 32-bit identifier which maps each specific SMEM item to a slot in the table of
- contents that lives at the start of the SMEM region.
- A SMEM client that wishes to allocate a SMEM item will provide the item
- identifier and a desired size in bytes. Assuming there is enough free space in
- the SMEM region to accommodate the request, the amount of desired bytes will be
- carved out, and the base address and size for the item will be stored in the
- table of contents. The base address will be returned as a pointer to the
- client, so that the client may use the SMEM item as if it were normal memory
- allocated through "malloc".
- A SMEM client that wishes to find an already allocated SMEM item will provide
- the item identifier and the size in bytes that the client expects for the item.
- A lookup in the table of contents for the specified item identifier will be
- performed. Assuming a matching SMEM item is found, the size of the item that
- is stored in the table of contents will be compared to the size specified by the
- client. This sanity check of the expected vs actual size is done to ensure that
- all users of a particular SMEM item agree on the size of the data to be
- exchanged under the assumption that if the users do not agree on the item size,
- then they will not be able to successfully communicate as one or more sides may
- view a corruption of the data stored in the SMEM item. Assuming the sizes
- match, the virtual address corresponding to the base address stored in the table
- of contents for the item will be returned to the client.
- +------+ Request +-----------+ Memory
- |Client|+----------------->|SMEM Driver| +---------------+
- +------+ Item X of size Y +-----------+ | |
- ^ + | |
- | | Lookup/Alloc +---------------+ Find X
- | +--------------->| TOC[X] |+--------+
- | +---------------+ |
- | | | |
- | | | |
- | | | |
- | | | |
- | | | |
- | | | |
- | Return pointer for client +---------------+ |
- +---------------------------------------------+| Item X |<--------+
- +---------------+
- | |
- | |
- | |
- +---------------+
- The SMEM driver depends on the kernel memory management subsystem for managing
- the system memory that SMEM uses. The main SMEM memory region is statically
- mapped at boot, and the virtual address for the base of the region is stored
- in MSM_SHARED_RAM_BASE. Auxiliary memory regions are ioremap'd at driver init.
- All SMEM regions are mapped as non-cacheable.
- Although the SMEM driver is aware of auxiliary memory regions, and capable of
- understanding SMEM items that exist in auxiliary memory regions, the SMEM
- driver does not allocate from the auxiliary memory regions. A detailed
- description of the purpose and use of auxiliary memory regions is outside the
- scope of this document.
- Design
- ======
- The SMEM protocol requires that the system bootloader initialize (zero out) and
- bootstrap the main SMEM region before any processor in the system has booted to
- avoid an initialization race condition.
- SMEM regions are configured as non-cachable memory. While this results in a
- small performance hit, it significantly reduces the complexity for the SMEM
- driver and clients in terms of cache management and memory barriers. Clients
- are generally able to treat their SMEM items like regular local memory, which
- eases the requirements to write correct code.
- The unsigned data type is assumed to be an unsigned 32-bit integer value.
- The root structure at the base of the main SMEM region is:
- #define SMD_HEAP_SIZE 512
- struct smem_shared {
- struct smem_proc_comm proc_comm[4];
- unsigned version[32];
- struct smem_heap_info heap_info;
- struct smem_heap_entry heap_toc[SMD_HEAP_SIZE];
- };
- This structure and its fields are initialized by the bootloader.
- The proc_comm field is reserved as the first part of the SMEM region to maintain
- compatibility with legacy systems, but is otherwise deprecated. While the
- proc comm driver is beyond the scope of this document, the remaining structure
- definition to fully define smem_shared is:
- struct smem_proc_comm {
- unsigned command;
- unsigned status;
- unsigned data1;
- unsigned data2;
- };
- The version field of the smem_shared struct is an array of version entries
- specifying the SMEM protocol version of every supporting processor active in the
- system. Each unsigned value in the array corresponds to one entry. This
- provides a mechanism for ensuring protocol version compatibility between
- processors. While the full table of assigned and reserved entries in the array
- is beyond the scope of this document, index 8 (smem_shared.version[8]) is
- reserved for any future use by Linux. The bootloader always initializes it's
- entry (index 7, or smem_shared.version[7]) to the SMEM protocol version
- supported by the bootloader. Checking the value of the bootloader's entry can
- be used as a sanity check to determine if the SMEM region was successfully
- initialized.
- The heap_info field of smem_shared contains basic information of the SMEM heap.
- The bootloader fills in values corresponding to the main SMEM region when it
- initializes the heap. It is defined as:
- struct smem_heap_info {
- unsigned initialized;
- unsigned free_offset;
- unsigned heap_remaining;
- unsigned reserved;
- };
- The initialized field is set to 1 by the bootloader when it initializes the
- heap. The free_offset field contains the offset from the base of the SMEM
- region for the first free byte in the heap. When a new SMEM item is allocated,
- free_offset is incremented by the size of the allocated item. SMEM item sizes
- are 8-byte aligned. The heap_remaining field contains the number of free bytes
- remaining in the heap. When a new SMEM item is allocated, heap_remaining is
- decremented by the size of the item. The reserved field is defined to be 0.
- The heap_toc field of smem_shared is the heap table of contents. It is an array
- containing a slot for every defined SMEM item. SMEM item identifiers index into
- this array. The structures definition is:
- struct smem_heap_entry {
- unsigned allocated;
- unsigned offset;
- unsigned size;
- unsigned reserved; /* bits 1:0 reserved, bits 31:2 aux smem base addr */
- };
- If an SMEM item is allocated, the allocated field is 1. The offset field is
- either the offset from the main SMEM region base where this SMEM item exists, or
- the offset from the auxiliary SMEM region base specified in the reserved field.
- The size field contains the size of the SMEM item in bytes. The size is defined
- to be 8-byte aligned. The reserved field is 0 if the SMEM item is located in
- the main SMEM region, or bits 31(MSB) to 2 specify the physical address of the
- auxiliary SMEM region where the SMEM item resides. If reserved is used as a
- physical address, then the address must be 4-byte aligned per ARM architectural
- requirements.
- The bootloader allocates and intializes the following SMEM items:
- Name ID Size (bytes)
- ----------------------------------------------------
- SMEM_PROC_COMM 0 64
- SMEM_HEAP_INFO 1 16
- SMEM_ALLOCATION_TABLE 2 8192
- SMEM_VERSION_INFO 3 128
- SMEM_HW_RESET_DETECT 4 8
- SMEM_AARM_WARM_BOOT 5 4
- SMEM_DIAG_ERR_MESSAGE 6 200
- SMEM_SPINLOCK_ARRAY 7 32
- SMEM_MEMORY_BARRIER_LOCATION 8 4
- All other SMEM items are dynamically allocated by processors in the system.
- Although the SMEM protocol requires the bootloader to initialize the SMEM region
- before any processor in the system is active, early development of new systems
- do not always have a fully functional bootloader. To determine if the
- bootloader initialized the main SMEM region properly, the SMEM driver will check
- the expected values of smem_shared.heap_info.initialized,
- smem_shared.heap_info.reserved, and the bootloader entry of the
- SMEM_VERSION_INFO SMEM item. If this check fails, the SMEM driver will print
- an error message to the kernel log, and enter a disabled state.
- Security Feature
- ----------------
- The SMEM protocol supports an optional security feature that segments the main
- SMEM region into multiple partitions. Each partition becomes a unique item
- namespace. Access to partitions is restricted to a maximum of two processors
- and enforced by Memory Protection Units (MPUs). The exceptions to this are the
- Partition Table of Contents partition, which is read-only accessible by all
- processors, and the Legacy/Default partition, which is freely accessible by all
- processors.
- +-------------------------+ SMEM Base address
- |Legacy/Default |
- |SMEM Partition |
- +-------------------------+
- |SMEM Partition 0 |
- |Processor 1 - Processor 2|
- +-------------------------+
- |SMEM Partition 1 |
- |Processor 1 - Processor 3|
- +-------------------------+
- |SMEM Partition 2 |
- |Processor 4 - Processor 5|
- +-------------------------+
- .
- .
- .
- +-------------------------+
- |SMEM Partition N |
- |Processor N - Processor M|
- +-------------------------+ SMEM Base address + SMEM size - 4k
- |Table of Contents |
- | |
- +-------------------------+ SMEM Base address + SMEM size
- SMEM items which are point-to-point in nature and accessed by two or fewer
- processors may be allocated from a partition that is restricted to those
- processors. SMEM items which are non-sensitive, accessed by 3 or more
- processors, and/or do not correspond to a secured partition are allocated from
- the Legacy/Default partition.
- During the firmware boot process, the Table of Contents is initialized with a
- description of all the secured partitions. Each secured partition is also
- initialized. The required MPU settings to protect the Table of Contents and the
- secured partitions are also established. The Table of Contents is located 4k
- bytes prior to the end of the main SMEM region so that it is in a known position
- for all processors to find and do local configuration.
- The Table of Contents is defined as:
- struct smem_toc {
- /*
- * Identifier is a constant for use in debugging and identifying this
- * struct in a binary capture. Set to 0x434f5424 ("$TOC").
- */
- uint32_t identifier;
- /* Version number */
- uint32_t version;
- /* Number of entries in the table */
- uint32_t num_entries;
- uint32_t reserved[5];
- /* Zero or more entries follow */
- struct smem_toc_entry entry[];
- };
- Each entry in the Table of Contents is defined as:
- struct smem_toc_entry {
- /* Offset in bytes from SMEM base of the region */
- uint32_t offset;
- /* Size in bytes of the region */
- uint32_t size;
- /* Flags for this region */
- uint32_t flags;
- /*
- * IDs for the 2 subsystems which have access to this partition.
- * Order does not matter.
- * For the entry which describes the TOC itself, these are both set to
- * SMEM_INVALID_HOST.
- * Use uint16_t, rather than enum type, to ensure size.
- */
- uint16_t host0;
- uint16_t host1;
- /*
- * Lowest common multiple of cacheline sizes for both endpoints. For
- * example, if host0 has cacheline size of 32 and host1 has cacheline
- * size of 64, this value is set to 64.
- */
- uint32_t size_cacheline;
- uint32_t reserved[3];
- /*
- * Sizes of sub ranges that are part of the region, but are excluded
- * from the SMEM heap. These are allocated from the end of the region
- * starting with sizes[0]. Set to 0 when not used.
- */
- uint32_t exclusion_sizes[SMEM_TOC_MAX_EXCLUSIONS];
- };
- While the Legacy/Default partition maintains the structure and format of the
- main SMEM region with the security feature disabled, the secured partitions have
- a different format and structure:
- +--------------+ +--------------------------+ Partition Base Address
- | | | Partition Header |
- | | | | +
- | Uncached Page| +--------------------------+ |
- | | | Item A Header | |
- | | +--------------------------+ |
- | | | Item A Data | |
- +--------------+ | | |
- | | | | |
- | | +--------------------------+ |
- | Uncached Page| | Item B Header | |Direction of heap growth
- | | +--------------------------+ |
- | | | Item B Data | |
- | | | | |
- +--------------+ | | |
- | | +--------------------------+ |
- | | | | |
- | Uncached Page| | Unused Heap | |
- | | | space to | v
- | | | page boundary |
- | | | |
- +--------------+ +--------------------------+<----------+ End of heap
- . . . . . .
- Free Space
- Can be used for
- either heap.
- . . . . . .
- +--------------+ +--------------------------+<----------+ End of heap
- | | | |
- | | | Unused Heap |
- | Cached Page | | space to | ^
- | | | page boundary | |
- | | +--------------------------+ |
- | | | Item Y Data | |
- +--------------+ | | |
- | | +--------------------------+ |
- | | | Item Y Header | |
- | Cached Page | +--------------------------+ |
- | | | Item Y Header Padding | |Direction of heap growth
- | | +--------------------------+ |
- | | | Item Z Data | |
- +--------------+ | | |
- | | | | |
- | | | | |
- | Cached Page | +--------------------------+ |
- | | | Item Z Header | |
- | | +--------------------------+ + Padding is here to ensure
- | | | Item Z Header Padding | the the data buffer start
- +--------------+ +--------------------------+ and end addresses are
- | | aligned to cachelines for
- | | Exclusion Range both endpoints.
- | Uncached Page|. . . Free Space . . .
- | | +--------------------------+
- | | | Exclusion Ranges 0..N |
- | | | |
- +--------------+ +--------------------------+ Partition Base Address + size
- The design of the secured partitions has two advantages over the Legacy/Default
- Partition
- 1. Using a linked list instead of a static array to track allocated SMEM
- items maximizes space utilization
- 2. Creating two heaps allows one to be cacheline aligned, thus providing
- an option for a higher level of performance to clients (requires
- client to specify they want their SMEM item allocated in the
- cached area)
- The partition header struct is defined as:
- struct smem_partition_header {
- /* Identifier magic number - 0x54525024 ("$PRT") */
- uint32_t identifier;
- /*
- * IDs for the 2 subsystems which have access to this partition.
- * Order does not matter.
- * Use uint16_t, rather than enum type, to ensure size.
- */
- uint16_t host0;
- uint16_t host1;
- /* Partition size, in bytes, not including the exclusion ranges */
- uint32_t size;
- /* Offset of the byte following the last allocation in uncached heap */
- uint32_t offset_free_uncached;
- /* Offset of the byte following the last allocation in cached heap */
- uint32_t offset_free_cached;
- uint32_t reserved[3];
- };
- The allocated SMEM item header struct is defined as:
- struct smem_partition_allocation_header {
- /* 0xa5a5 canary value to detect overrun problems */
- uint16_t canary;
- /* SMEM item ID. Use uint16_t here, rather than enum, to ensure size. */
- uint16_t smem_id;
- /* Size of the allocated item, includes any necessary padding. */
- uint32_t size;
- /* Size of the data padding for cacheline alignment, if applicable */
- uint16_t data_padding;
- /* Size of the header padding for cacheline alignment, if applicable */
- uint16_t header_padding;
- uint32_t reserved[1];
- };
- SMP/multi-core
- ==============
- The SMEM driver expects a remote spinlock driver to provide inter-processor
- synchronization primitives which not only provide locking between multiple cores
- but locking between multiple processors to protect the state of structures
- stored in SMEM regions during allocation and lookup. Once a pointer to a SMEM
- item is returned to a client, that client is expected to provide all the
- necessary locking and other synchronization as required.
- The remote spinlocks may make use of the SMEM_SPINLOCK_ARRAY SMEM item (typical
- of legacy systems).
- SMEM regions are non-cachable to maintain a consistent state of the data
- throughout all operations. This simplifies cache management and memory barrier
- requirements to a few key points in the SMEM item allocation process, and allows
- clients to treat SMEM items like local memory once allocated.
- Security
- ========
- SMEM by default provides no security of SMEM items. If a SMEM item is intended
- to only be used between clients on processors A and B, malicious clients on
- processor C are free to sniff or inject data into the SMEM item.
- An optional security feature may be enabled that makes use of Memory Protection
- Units (MPUs) to limit access of special segments of the main SMEM region.
- Access to these partitions is limited to two processors, so only point-to-point
- traffic (such as SMD or SMP2P) is able to be protected. Auxiliary SMEM regions
- are not protected under this feature. Support for this feature is activated by
- a Device Tree property.
- Performance
- ===========
- Some client use cases such as SMD may benefit from caching, but that places an
- additional burden of cache maintenance and protocol design onto the clients.
- Interface
- =========
- Kernel-space APIs:
- /**
- * smem_alloc() - Find an existing item, otherwise allocate it with security
- * support
- *
- * @id: ID of SMEM item
- * @size_in: Size of the SMEM item
- * @to_proc: SMEM host that shares the item with apps
- * @flags: Item attribute flags
- * @returns: Pointer to SMEM item, NULL if it couldn't be found/allocated, or
- * -EPROBE_DEFER if the driver is not ready
- */
- void *smem_alloc(unsigned id, unsigned size_in, unsigned to_proc,
- unsigned flags);
- /**
- * smem_get_entry() - Get existing item with security support
- *
- * @id: ID of SMEM item
- * @size: Pointer to size variable for storing the result
- * @to_proc: SMEM host that shares the item with apps
- * @flags: Item attribute flags
- * @returns: Pointer to SMEM item, NULL if it doesn't exist, or -EPROBE_DEFER
- * if the driver isn't ready
- */
- void *smem_get_entry(unsigned id, unsigned *size, unsigned to_proc,
- unsigned flags);
- /**
- * smem_get_entry_no_rlock() - Get existing item without using remote spinlock.
- *
- * @id: ID of SMEM item
- * @size_out: Pointer to size variable for storing the result
- * @to_proc: SMEM host that shares the item with apps
- * @flags: Item attribute flags
- * @returns: Pointer to SMEM item, NULL if it doesn't exist, or -EPROBE_DEFER
- * if the driver isn't ready
- *
- * This function does not lock the remote spinlock and should only be used in
- * failure-recover cases such as retrieving the subsystem failure reason during
- * subsystem restart.
- */
- void *smem_get_entry_no_rlock(unsigned id, unsigned *size_out, unsigned to_proc,
- unsigned flags);
- /**
- * smem_find() - Find existing item with security support
- *
- * @id: ID of SMEM item
- * @size_in: Size of the SMEM item
- * @to_proc: SMEM host that shares the item with apps
- * @flags: Item attribute flags
- * @returns: Pointer to SMEM item, NULL if it doesn't exist, or -EPROBE_DEFER
- * if the driver is not ready
- */
- void *smem_find(unsigned id, unsigned size);
- /**
- * smem_virt_to_phys() - Convert SMEM address to physical address.
- *
- * @smem_address: Address of SMEM item (returned by smem_alloc(), etc)
- * @returns: Physical address (or NULL if there is a failure)
- *
- * This function should only be used if an SMEM item needs to be handed
- * off to a DMA engine. This function will not return a version of EPROBE_DEFER
- * if the driver is not ready since the caller should obtain @smem_address from
- * one of the other public APIs and get EPROBE_DEFER at that time, if
- * applicable.
- */
- phys_addr_t smem_virt_to_phys(void *smem_address);
- Driver parameters
- =================
- Module parameters:
- debug_mask - 0 for off (default), 1 for on.
- Enables or disables printing debug messages to the kernel log
- Config options
- ==============
- Configuration of SMEM regions is done via Device Tree per the format in
- Documentation/devicetree/bindings/arm/msm/smem.txt.
- Dependencies
- ============
- Drivers needed:
- Remote spinlocks
- Depends on the system bootloader to initialize the main SMEM region.
- Known issues
- ============
- None.
- To do
- =====
- Convert use of the unsigned data type to well defined value such as uint32_t for
- better portability.
|