/*
 * Copyright (c) 2008 Apple Inc. All rights reserved.
 *
 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
 * 
 * This file contains Original Code and/or Modifications of Original Code
 * as defined in and that are subject to the Apple Public Source License
 * Version 2.0 (the 'License'). You may not use this file except in
 * compliance with the License. The rights granted to you under the License
 * may not be used to create, or enable the creation or redistribution of,
 * unlawful or unlicensed copies of an Apple operating system, or to
 * circumvent, violate, or enable the circumvention or violation of, any
 * terms of an Apple operating system software license agreement.
 * 
 * Please obtain a copy of the License at
 * http://www.opensource.apple.com/apsl/ and read it before using this file.
 * 
 * The Original Code and all software distributed under the License are
 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
 * Please see the License for the specific language governing rights and
 * limitations under the License.
 * 
 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
 */

#ifndef _MKEXT_H_
#define _MKEXT_H_ 1

#include <sys/cdefs.h>
#include <sys/types.h>

#include <mach/machine.h>

/*********************************************************************
* Mkext File Format
*
* ALL BINARY VALUES ARE BIG-ENDIAN.
*********************************************************************/
#if PRAGMA_MARK
#pragma mark Constants
#endif

#define MKEXT_MAGIC 0x4D4B5854 /* 'MKXT' */
#define MKEXT_SIGN  0x4D4F5358 /* 'MOSX' */

#define MKEXT_EXTN  ".mkext"

#define MKEXT_VERS_1  (0x01008000)

// Used during development/bringup: v 2.0d1
#define MKEXT_VERS_2  (0x02002001)
// xxx - Will use this when format is final
// #define MKEXT_VERS_2  (0x02008000)

#if PRAGMA_MARK
#pragma mark Core Header
#endif
/*********************************************************************
* Core Header
*
* All versions of mkext files have this basic header:
*
* - magic & signature - always 'MKXT' and 'MOSX' as defined above.
* - length - the length of the whole file
* - adler32 - checksum from &version to end of file
* - version - a 'vers' style value
* - numkexts - how many kexts are in the archive (only needed in v.1)
* - cputype & cpusubtype - in version 1 could be CPU_TYPE_ANY
*     and CPU_SUBTYPE_MULTIPLE if the archive contained fat kexts;
*     version 2 does not allow this and all kexts must be of a single
*     arch. For either version, mkexts of specific arches can be
*     embedded in a fat Mach-O file to combine them.
*********************************************************************/

#define MKEXT_HEADER_CORE      \
    uint32_t      magic;       \
    uint32_t      signature;   \
    uint32_t      length;      \
    uint32_t      adler32;     \
    uint32_t      version;     \
    uint32_t      numkexts;    \
    cpu_type_t    cputype;     \
    cpu_subtype_t cpusubtype;

typedef struct mkext_basic_header {
    MKEXT_HEADER_CORE
} mkext_basic_header;

#define MKEXT_HDR_CAST(hdr)        ((mkext_basic_header *)(hdr))

#define MKEXT_SWAP(num)            OSSwapBigToHostInt32((uint32_t)(num))

#define MKEXT_GET_MAGIC(hdr)       (MKEXT_SWAP(MKEXT_HDR_CAST(hdr)->magic))
#define MKEXT_GET_SIGNATURE(hdr)   (MKEXT_SWAP(MKEXT_HDR_CAST(hdr)->signature))
#define MKEXT_GET_LENGTH(hdr)      (MKEXT_SWAP(MKEXT_HDR_CAST(hdr)->length))
#define MKEXT_GET_CHECKSUM(hdr)    (MKEXT_SWAP(MKEXT_HDR_CAST(hdr)->adler32))
#define MKEXT_GET_VERSION(hdr)     (MKEXT_SWAP(MKEXT_HDR_CAST(hdr)->version))
#define MKEXT_GET_COUNT(hdr)       (MKEXT_SWAP(MKEXT_HDR_CAST(hdr)->numkexts))
#define MKEXT_GET_CPUTYPE(hdr)     (MKEXT_SWAP(MKEXT_HDR_CAST(hdr)->cputype))
#define MKEXT_GET_CPUSUBTYPE(hdr)  (MKEXT_SWAP(MKEXT_HDR_CAST(hdr)->cpusubtype))

#if PRAGMA_MARK
#pragma mark Mkext Version 2 Format Definitions
#endif
/*********************************************************************
* Mkext Version 2 Format Definitions
*
* The version field here will be some variant of 0x0200....; that is
* the  major version byte will be 0x02.
*
* Version 2 uses zlib for compression, not the lzss compressor used
* by version 1.
*
* In version 2, all executable & resource files are stored in sequence
* followed by the combined info dictionaries of all kexts at the end.
* This XML plist should be nul-terminated and stored at a page-aligned
* offset in the file so that kernel code can unmap it as soon as it's
* parsed.
*
* The info dict for each kext will have inserted into it these
* additional properties:
*
* - _MKEXTBundlePath (string) - full path to the original bundle,
*   relative to volume.
* - _MKEXTExecutable (integer) - offset to the executable entry.
* - _MKEXTResources (dict) - keyed by filename, values integer offsets
*   to file entries.
*
* Mkext2 buffers are used to send load requests to the kernel. When
* this is done, the plist will contain an _MKEXTLoadRequest dictionary
* whose keys are the bundle IDs to load, and whose values are
* dictionaries of flags:
*
* - "Load Kext" - boolean, whether to load the kext or not (default true).
*   May be used to send just personalities for already-loaded kexts,
*   but we do have a mechanism to do that from userland already.
* - "Start Kext" - boolean, whether to start the kext or not
*   (used when debugging). Defaults to true.
* - "Add Personalities" - boolean, whether to send personalities to
*   the IOCatalogue (used when debugging). Defaults to true.
* - "Disable Autounload" - boolean, whether to prevent the reaper
*   thread from unloading the kext, so the dev. has time to set up
*   the debug session. (Predefined window, or maybe this will be a
*   number of seconds to wait.) Defaults to false.
*********************************************************************/
#define kMKEXTInfoDictionariesKey             "_MKEXTInfoDictionaries"

#define kMKEXTBundlePathKey                   "_MKEXTBundlePath"
#define kMKEXTExecutableRelativePathKey       "_MKEXTExecutableRelativePath"
#define kMKEXTExecutableKey                   "_MKEXTExecutable"

#define kMKEXTLoadRequestKey                  "_MKEXTLoadRequest"
#define kMKEXTLoadRequestLoadKey              "Load Kext"
#define kMKEXTLoadRequestStartKey             "Start Kext"
#define kMKEXTLoadRequestAddPersonalitiesKey  "Add Personalities"
#define kMKEXTLoadRequestDisableAutounloadKey "Disable Autounload"

typedef struct mkext2_file_entry {
    uint32_t  compressed_size;  // if zero, file is not compressed
    uint32_t  full_size;        // full size of data w/o this struct
    uint8_t   data[0];          // data is inline to this struct
} mkext2_file_entry;

typedef struct mkext2_header {
    MKEXT_HEADER_CORE
    uint32_t plist_offset;
    uint32_t plist_compressed_size;
    uint32_t plist_full_size;
} mkext2_header;

#define MKEXT2_GET_ENTRY_COMPSIZE(ptr)   MKEXT_SWAP((ptr)->compressed_size)
#define MKEXT2_GET_ENTRY_FULLSIZE(ptr)   MKEXT_SWAP((ptr)->full_size)
#define MKEXT2_GET_ENTRY_DATA(ptr)       ((ptr)->data)

#define MKEXT2_HDR_CAST(hdr)             ((mkext2_header *)(hdr))
#define MKEXT2_GET_PLIST(hdr)            MKEXT_SWAP(MKEXT2_HDR_CAST(hdr)->plist_offset)
#define MKEXT2_GET_PLIST_COMPSIZE(hdr)   MKEXT_SWAP(MKEXT2_HDR_CAST(hdr)->plist_compressed_size)
#define MKEXT2_GET_PLIST_FULLSIZE(hdr)   MKEXT_SWAP(MKEXT2_HDR_CAST(hdr)->plist_full_size)

#if PRAGMA_MARK
#pragma mark Mkext Version 1 Format Definitions
#endif
/*********************************************************************
* Mkext Version 1 Format Definitions
*
* The original mkext format has version 0x01008000 ("1.0").
*
* In version 1, plists were not nul-terminated, so it's up to the
* reader to add that '\0' on the end if it's needed.
*
* Original bad names preserved for source compatibility.
*********************************************************************/

// If all fields are 0 then this file slot is empty
// If compsize is zero then the file isn't compressed.
typedef struct mkext_file {
    uint32_t offset;         // 4 bytes
    uint32_t compsize;       // 4 bytes
    uint32_t realsize;       // 4 bytes
    uint32_t modifiedsecs;   // 4 bytes; cast to time_t to use
} mkext_file;

// The plist file entry is mandatory, but module may be empty
typedef struct mkext_kext {
    mkext_file plist;        // 16 bytes
    mkext_file module;       // 16 bytes
} mkext_kext;

typedef struct mkext_header {
    MKEXT_HEADER_CORE
    mkext_kext kext[1];    // 32 bytes/entry
} mkext_header;

typedef mkext_header mkext1_header;

#define MKEXT1_ENTRY_CAST(ptr)           ((mkext_file *)(ptr))
#define MKEXT1_GET_ENTRY_OFFSET(ptr)     (MKEXT_SWAP(MKEXT1_ENTRY_CAST(ptr)->offset))
#define MKEXT1_GET_ENTRY_COMPSIZE(ptr)   (MKEXT_SWAP(MKEXT1_ENTRY_CAST(ptr)->compsize))
#define MKEXT1_GET_ENTRY_FULLSIZE(ptr)   (MKEXT_SWAP(MKEXT1_ENTRY_CAST(ptr)->realsize))
#define MKEXT1_GET_ENTRY_MODTIME(ptr)    ((time_t)MKEXT_SWAP(MKEXT1_ENTRY_CAST(ptr)->modifiedsecs))
#define MKEXT1_ENTRY_EXISTS(ptr)         (MKEXT1_GET_ENTRY_OFFSET(ptr)   ||  \
                                          MKEXT1_GET_ENTRY_FULLSIZE(ptr) ||  \
                                          MKEXT_GET_ENTRY_COMPSIZE(ptr)  ||  \
                                          MKEXT_GET_ENTRY_COMPSIZE(ptr))

#define MKEXT1_GET_KEXT(hdr, i)          ((mkext_kext *)&(MKEXT1_HDR_CAST(hdr)->kext[(i)]))
#define MKEXT1_GET_KEXT_PLIST(hdr, i)    (MKEXT1_ENTRY_CAST(&(MKEXT1_GET_KEXT((hdr), (i))->plist)))
#define MKEXT1_GET_KEXT_EXEC(hdr, i)     (MKEXT1_ENTRY_CAST(&(MKEXT1_GET_KEXT((hdr), (i))->module)

#define MKEXT1_HDR_CAST(hdr)             ((mkext1_header *)(hdr))

/* These functions are only used for version 1 mkexts.
 */
__BEGIN_DECLS
u_int8_t *
compress_lzss(u_int8_t * dst, u_int32_t dstlen,
    u_int8_t * src, u_int32_t srclen);

int
decompress_lzss(u_int8_t * dst, u_int32_t dstlen,
    u_int8_t * src, u_int32_t srclen);

u_int32_t
mkext_adler32(u_int8_t * src, int32_t length);
__END_DECLS

#endif /* _MKEXT_H_ */
