1/*
2 * Copyright (c) 2008-2016 Apple Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28
29#ifndef _LIBKERN_OSKEXT_H
30#define _LIBKERN_OSKEXT_H
31
32extern "C" {
33#include <kern/thread_call.h>
34#include <libkern/OSKextLibPrivate.h>
35#include <libkern/kernel_mach_header.h>
36#include <libkern/kxld.h>
37#include <mach/kmod.h>
38
39#ifdef XNU_KERNEL_PRIVATE
40#include <kern/thread_call.h>
41#endif /* XNU_KERNEL_PRIVATE */
42}
43
44#include <libkern/OSKextLib.h>
45#include <libkern/OSKextLibPrivate.h>
46#include <libkern/c++/OSObject.h>
47#include <libkern/c++/OSContainers.h>
48#include <IOKit/IOLocks.h>
49
50/*********************************************************************
51* C functions used for callbacks.
52*********************************************************************/
53#ifdef XNU_KERNEL_PRIVATE
54extern "C" {
55void osdata_kmem_free(void * ptr, unsigned int length);
56void osdata_phys_free(void * ptr, unsigned int length);
57void osdata_vm_deallocate(void * ptr, unsigned int length);
58void osdata_kext_free(void * ptr, unsigned int length);
59void kxld_log_callback(
60 KXLDLogSubsystem subsystem,
61 KXLDLogLevel level,
62 const char * format,
63 va_list argList,
64 void * user_data);
65};
66#endif /* XNU_KERNEL_PRIVATE */
67
68/*********************************************************************
69* C Function Prototypes for Friend Declarations.
70*********************************************************************/
71class OSKext;
72
73extern "C" {
74
75void OSKextLog(
76 OSKext * aKext,
77 OSKextLogSpec msgLogSpec,
78 const char * format, ...)
79 __attribute__((format(printf, 3, 4)));
80
81void OSKextVLog(
82 OSKext * aKext,
83 OSKextLogSpec msgLogSpec,
84 const char * format,
85 va_list srcArgList);
86
87#ifdef XNU_KERNEL_PRIVATE
88void OSKextRemoveKextBootstrap(void);
89
90kern_return_t OSRuntimeInitializeCPP(
91 OSKext * kext);
92kern_return_t OSRuntimeFinalizeCPP(
93 OSKext * kext);
94void OSRuntimeUnloadCPPForSegment(
95 kernel_segment_command_t * segment);
96
97kern_return_t is_io_catalog_send_data(
98 mach_port_t masterPort,
99 uint32_t flag,
100 io_buf_ptr_t inData,
101 mach_msg_type_number_t inDataCount,
102 kern_return_t * result);
103
104void kmod_dump_log(vm_offset_t*, unsigned int, boolean_t);
105void *OSKextKextForAddress(const void *addr);
106
107#endif /* XNU_KERNEL_PRIVATE */
108};
109
110/********************************************************************/
111#if PRAGMA_MARK
112#pragma mark -
113#endif
114
115struct list_head {
116 struct list_head *prev;
117 struct list_head *next;
118};
119
120struct OSKextGrabPgoStruct {
121 bool metadata;
122 uint64_t *pSize;
123 char *pBuffer;
124 uint64_t bufferSize;
125 int err;
126 struct list_head list_head;
127};
128
129#ifndef container_of
130#define container_of(ptr,type,member) ((type*)(((uintptr_t)ptr) - offsetof(type, member)))
131#endif
132/********************************************************************/
133
134#if XNU_KERNEL_PRIVATE
135
136struct OSKextAccount
137{
138 vm_allocation_site_t site;
139 uint32_t loadTag;
140 OSKext * kext;
141};
142
143struct OSKextActiveAccount
144{
145 uintptr_t address;
146 uintptr_t address_end;
147 OSKextAccount * account;
148};
149typedef struct OSKextActiveAccount OSKextActiveAccount;
150
151#endif /* XNU_KERNEL_PRIVATE */
152
153/*
154 * @class OSKext
155 */
156/********************************************************************/
157class OSKext : public OSObject
158{
159 OSDeclareDefaultStructors(OSKext)
160
161#if PRAGMA_MARK
162/**************************************/
163#pragma mark Friend Declarations
164/**************************************/
165#endif
166 friend class IOCatalogue;
167 friend class KLDBootstrap;
168 friend class OSMetaClass;
169
170 friend int OSKextGrabPgoData(uuid_t uuid,
171 uint64_t *pSize,
172 char *pBuffer,
173 uint64_t bufferSize,
174 int wait_for_unload,
175 int metadata);
176
177#ifdef XNU_KERNEL_PRIVATE
178 friend void OSKextVLog(
179 OSKext * aKext,
180 OSKextLogSpec msgLogSpec,
181 const char * format,
182 va_list srcArgList);
183
184 friend void OSKextRemoveKextBootstrap(void);
185 friend OSReturn OSKextUnloadKextWithLoadTag(uint32_t);
186
187 friend kern_return_t kext_request(
188 host_priv_t hostPriv,
189 /* in only */ uint32_t clientLogSpec,
190 /* in only */ vm_offset_t requestIn,
191 /* in only */ mach_msg_type_number_t requestLengthIn,
192 /* out only */ vm_offset_t * responseOut,
193 /* out only */ mach_msg_type_number_t * responseLengthOut,
194 /* out only */ vm_offset_t * logDataOut,
195 /* out only */ mach_msg_type_number_t * logDataLengthOut,
196 /* out only */ kern_return_t * op_result);
197
198 friend kxld_addr_t kern_allocate(
199 u_long size,
200 KXLDAllocateFlags * flags,
201 void * user_data);
202
203 friend void kxld_log_shim(
204 KXLDLogSubsystem subsystem,
205 KXLDLogLevel level,
206 const char * format,
207 va_list argList,
208 void * user_data);
209
210 friend void _OSKextConsiderUnloads(
211 __unused thread_call_param_t p0,
212 __unused thread_call_param_t p1);
213
214 friend kern_return_t OSRuntimeInitializeCPP(
215 OSKext * kext);
216 friend kern_return_t OSRuntimeFinalizeCPP(
217 OSKext * kext);
218 friend void OSRuntimeUnloadCPPForSegment(
219 kernel_segment_command_t * segment);
220
221 friend kern_return_t is_io_catalog_send_data(
222 mach_port_t masterPort,
223 uint32_t flag,
224 io_buf_ptr_t inData,
225 mach_msg_type_number_t inDataCount,
226 kern_return_t * result);
227
228 friend void kmod_panic_dump(vm_offset_t*, unsigned int);
229 friend void kmod_dump_log(vm_offset_t*, unsigned int, boolean_t);
230 friend void kext_dump_panic_lists(int (*printf_func)(const char * fmt, ...));
231 friend void *OSKextKextForAddress(const void *addr);
232
233#endif /* XNU_KERNEL_PRIVATE */
234
235private:
236
237 /*************************
238 * Instance variables
239 *************************/
240 OSDictionary * infoDict;
241
242 const OSSymbol * bundleID;
243 OSString * path; // not necessarily correct :-/
244 OSString * executableRelPath; // relative to bundle
245
246 OSKextVersion version; // parsed
247 OSKextVersion compatibleVersion; // parsed
248
249 /* These fields are required for tracking loaded kexts and
250 * will always have values for a loaded kext.
251 */
252 OSKextLoadTag loadTag; // 'id' from old kmod_info;
253 // kOSKextInvalidLoadTag invalid
254 kmod_info_t * kmod_info; // address into linkedExec./alloced for interface
255
256 OSArray * dependencies; // kernel resource does not have any;
257 // links directly to kernel
258
259 /* Only real kexts have these; interface kexts do not.
260 */
261 OSData * linkedExecutable;
262 OSSet * metaClasses; // for C++/OSMetaClass kexts
263
264 /* Only interface kexts have these; non-interface kexts can get at them
265 * in the linked Executable.
266 */
267 OSData * interfaceUUID;
268
269 struct {
270 unsigned int loggingEnabled:1;
271
272 unsigned int hasAllDependencies:1;
273 unsigned int hasBleedthrough:1;
274
275 unsigned int interface:1;
276 unsigned int kernelComponent:1;
277 unsigned int prelinked:1;
278 unsigned int builtin:1;
279 unsigned int loaded:1;
280 unsigned int dtraceInitialized:1;
281 unsigned int starting:1;
282 unsigned int started:1;
283 unsigned int stopping:1;
284 unsigned int unloading:1;
285
286 unsigned int autounloadEnabled:1;
287 unsigned int delayAutounload:1; // for development
288
289 unsigned int CPPInitialized:1;
290 unsigned int jettisonLinkeditSeg:1;
291 } flags;
292
293 struct list_head pendingPgoHead;
294 uuid_t instance_uuid;
295 OSKextAccount * account;
296 uint32_t builtinKmodIdx;
297
298#if PRAGMA_MARK
299/**************************************/
300#pragma mark Private Functions
301/**************************************/
302#endif
303
304#ifdef XNU_KERNEL_PRIVATE
305 /* Startup/shutdown phases.
306 */
307public:
308 static void initialize(void);
309 static OSDictionary * copyKexts(void);
310 static OSReturn removeKextBootstrap(void);
311 static void willShutdown(void); // called by IOPMrootDomain on shutdown
312 static void reportOSMetaClassInstances(
313 const char * kextIdentifier,
314 OSKextLogSpec msgLogSpec);
315
316#endif /* XNU_KERNEL_PRIVATE */
317
318private:
319 /* Called by power management at sleep/shutdown.
320 */
321 static bool setLoadEnabled(bool flag);
322 static bool setUnloadEnabled(bool flag);
323 static bool setAutounloadsEnabled(bool flag);
324 static bool setKernelRequestsEnabled(bool flag);
325
326 // all getters subject to race condition, caller beware
327 static bool getLoadEnabled(void);
328 static bool getUnloadEnabled(void);
329 static bool getAutounloadEnabled(void);
330 static bool getKernelRequestsEnabled(void);
331
332 /* Instance life cycle.
333 */
334 static OSKext * withBooterData(
335 OSString * deviceTreeName,
336 OSData * booterData);
337 virtual bool initWithBooterData(
338 OSString * deviceTreeName,
339 OSData * booterData);
340
341 static OSKext * withPrelinkedInfoDict(
342 OSDictionary * infoDict,
343 bool doCoalesedSlides);
344 virtual bool initWithPrelinkedInfoDict(
345 OSDictionary * infoDict,
346 bool doCoalesedSlides);
347
348 static void setAllVMAttributes(void);
349
350 static OSKext * withMkext2Info(
351 OSDictionary * anInfoDict,
352 OSData * mkextData);
353 virtual bool initWithMkext2Info(
354 OSDictionary * anInfoDict,
355 OSData * mkextData);
356
357 virtual bool setInfoDictionaryAndPath(
358 OSDictionary * aDictionary,
359 OSString * aPath);
360 virtual bool setExecutable(
361 OSData * anExecutable,
362 OSData * externalData = NULL,
363 bool externalDataIsMkext = false);
364 virtual bool registerIdentifier(void);
365
366 virtual void free(void) APPLE_KEXT_OVERRIDE;
367
368 static OSReturn removeKext(
369 OSKext * aKext,
370 bool terminateServicesAndRemovePersonalitiesFlag = false);
371
372 virtual bool isInExcludeList(void);
373
374 /* Mkexts.
375 */
376 static OSReturn readMkextArchive(
377 OSData * mkextData,
378 uint32_t * checksumPtr = NULL);
379 static OSReturn readMkext2Archive(
380 OSData * mkextData,
381 OSDictionary ** mkextPlistOut,
382 uint32_t * checksumPtr = NULL);
383 virtual OSData * createMkext2FileEntry(
384 OSData * mkextData,
385 OSNumber * offsetNum,
386 const char * entryName);
387 virtual OSData * extractMkext2FileData(
388 UInt8 * data,
389 const char * name,
390 uint32_t compressedSize,
391 uint32_t fullSize);
392
393 /* Dependencies.
394 */
395 virtual bool resolveDependencies(
396 OSArray * loopStack = NULL); // priv/prot
397 virtual bool addBleedthroughDependencies(OSArray * anArray);
398 virtual bool flushDependencies(bool forceFlag = false); // priv/prot
399 virtual uint32_t getNumDependencies(void);
400 virtual OSArray * getDependencies(void);
401
402 /* User-space requests (load/generic).
403 */
404 static OSReturn loadFromMkext(
405 OSKextLogSpec clientLogSpec,
406 char * mkextBuffer,
407 uint32_t mkextBufferLength,
408 char ** logInfoOut,
409 uint32_t * logInfoLengthOut);
410 static OSReturn handleRequest(
411 host_priv_t hostPriv,
412 OSKextLogSpec clientLogSpec,
413 char * requestBuffer,
414 uint32_t requestLength,
415 char ** responseOut,
416 uint32_t * responseLengthOut,
417 char ** logInfoOut,
418 uint32_t * logInfoLengthOut);
419 static OSReturn serializeLogInfo(
420 OSArray * logInfoArray,
421 char ** logInfoOut,
422 uint32_t * logInfoLengthOut);
423
424 /* Loading.
425 */
426 virtual OSReturn load(
427 OSKextExcludeLevel startOpt = kOSKextExcludeNone,
428 OSKextExcludeLevel startMatchingOpt = kOSKextExcludeAll,
429 OSArray * personalityNames = NULL); // priv/prot
430 virtual OSReturn unload(void);
431 virtual OSReturn queueKextNotification(
432 const char * notificationName,
433 OSString * kextIdentifier);
434
435 static void recordIdentifierRequest(
436 OSString * kextIdentifier);
437
438 virtual OSReturn slidePrelinkedExecutable(bool doCoalesedSlides);
439 virtual OSReturn loadExecutable(void);
440 virtual void jettisonLinkeditSegment(void);
441 virtual void jettisonDATASegmentPadding(void);
442 static void considerDestroyingLinkContext(void);
443 virtual OSData * getExecutable(void);
444 virtual void setLinkedExecutable(OSData * anExecutable);
445
446#if CONFIG_DTRACE
447 friend void OSKextRegisterKextsWithDTrace(void);
448 static void registerKextsWithDTrace(void);
449 virtual void registerWithDTrace(void);
450 virtual void unregisterWithDTrace(void);
451#endif /* CONFIG_DTRACE */
452
453 virtual OSReturn start(bool startDependenciesFlag = true);
454 virtual OSReturn stop(void);
455 virtual OSReturn setVMAttributes(bool protect, bool wire);
456 virtual boolean_t segmentShouldBeWired(kernel_segment_command_t *seg);
457 virtual OSReturn validateKextMapping(bool startFlag);
458 virtual boolean_t verifySegmentMapping(kernel_segment_command_t *seg);
459
460 static OSArray * copyAllKextPersonalities(
461 bool filterSafeBootFlag = false);
462
463 static void setPrelinkedPersonalities(OSArray * personalitiesArray);
464
465 static void sendAllKextPersonalitiesToCatalog(
466 bool startMatching = false);
467 virtual OSReturn sendPersonalitiesToCatalog(
468 bool startMatching = false,
469 OSArray * personalityNames = NULL);
470
471 static bool canUnloadKextWithIdentifier(
472 OSString * kextIdentifier,
473 bool checkClassesFlag = true);
474
475 static OSReturn autounloadKext(OSKext * aKext);
476
477 /* Sync with user space.
478 */
479 static OSReturn pingKextd(void);
480
481 /* Getting info about loaded kexts (kextstat).
482 */
483 static OSDictionary * copyLoadedKextInfo(
484 OSArray * kextIdentifiers = NULL,
485 OSArray * keys = NULL);
486 static OSDictionary * copyLoadedKextInfoByUUID(
487 OSArray * kextIdentifiers = NULL,
488 OSArray * keys = NULL);
489 static OSData * copyKextUUIDForAddress(OSNumber *address = NULL);
490 virtual OSDictionary * copyInfo(OSArray * keys = NULL);
491
492 /* Logging to user space.
493 */
494 static OSKextLogSpec setUserSpaceLogFilter(
495 OSKextLogSpec userLogSpec,
496 bool captureFlag = false);
497 static OSArray * clearUserSpaceLogFilter(void);
498 static OSKextLogSpec getUserSpaceLogFilter(void);
499
500 /* OSMetaClasses defined by kext.
501 */
502 virtual OSReturn addClass(
503 OSMetaClass * aClass,
504 uint32_t numClasses);
505 virtual OSReturn removeClass(
506 OSMetaClass * aClass);
507 virtual bool hasOSMetaClassInstances(void);
508 virtual OSSet * getMetaClasses(void);
509
510 virtual void reportOSMetaClassInstances(
511 OSKextLogSpec msgLogSpec);
512
513 /* Resource requests and other callback stuff.
514 */
515 static OSReturn dispatchResource(OSDictionary * requestDict);
516
517 static OSReturn dequeueCallbackForRequestTag(
518 OSKextRequestTag requestTag,
519 OSDictionary ** callbackRecordOut);
520 static OSReturn dequeueCallbackForRequestTag(
521 OSNumber * requestTagNum,
522 OSDictionary ** callbackRecordOut);
523 static void invokeRequestCallback(
524 OSDictionary * callbackRecord,
525 OSReturn requestResult);
526 virtual void invokeOrCancelRequestCallbacks(
527 OSReturn callbackResult,
528 bool invokeFlag = true);
529 virtual uint32_t countRequestCallbacks(void);
530
531 /* panic() support.
532 */
533public:
534 enum {
535 kPrintKextsLock = 0x01,
536 kPrintKextsUnslide = 0x02,
537 kPrintKextsTerse = 0x04
538 };
539 static void printKextsInBacktrace(
540 vm_offset_t * addr,
541 unsigned int cnt,
542 int (* printf_func)(const char *fmt, ...),
543 uint32_t flags);
544private:
545 static OSKextLoadedKextSummary *summaryForAddress(const uintptr_t addr);
546 static void *kextForAddress(const void *addr);
547 static boolean_t summaryIsInBacktrace(
548 OSKextLoadedKextSummary * summary,
549 vm_offset_t * addr,
550 unsigned int cnt);
551 static void printSummary(
552 OSKextLoadedKextSummary * summary,
553 int (* printf_func)(const char *fmt, ...),
554 uint32_t flags);
555
556 static int saveLoadedKextPanicListTyped(
557 const char * prefix,
558 int invertFlag,
559 int libsFlag,
560 char * paniclist,
561 uint32_t list_size);
562 static void saveLoadedKextPanicList(void);
563 void savePanicString(bool isLoading);
564 static void printKextPanicLists(int (*printf_func)(const char *fmt, ...));
565
566 /* Kext summary support.
567 */
568 static void updateLoadedKextSummaries(void);
569 void updateLoadedKextSummary(OSKextLoadedKextSummary *summary);
570 void updateActiveAccount(OSKextActiveAccount *accountp);
571
572#ifdef XNU_KERNEL_PRIVATE
573public:
574#endif /* XNU_KERNEL_PRIVATE */
575
576 /* C++ Initialization.
577 */
578 virtual void setCPPInitialized(bool initialized=true);
579
580#if PRAGMA_MARK
581/**************************************/
582#pragma mark Public Functions
583/**************************************/
584#endif
585public:
586 // caller must release
587 static OSKext * lookupKextWithIdentifier(const char * kextIdentifier);
588 static OSKext * lookupKextWithIdentifier(OSString * kextIdentifier);
589 static OSKext * lookupKextWithLoadTag(OSKextLoadTag aTag);
590 static OSKext * lookupKextWithAddress(vm_address_t address);
591 static OSKext * lookupKextWithUUID(uuid_t uuid);
592
593 kernel_section_t *lookupSection(const char *segname, const char*secname);
594
595 static bool isKextWithIdentifierLoaded(const char * kextIdentifier);
596
597 static OSReturn loadKextWithIdentifier(
598 const char * kextIdentifier,
599 Boolean allowDeferFlag = true,
600 Boolean delayAutounloadFlag = false,
601 OSKextExcludeLevel startOpt = kOSKextExcludeNone,
602 OSKextExcludeLevel startMatchingOpt = kOSKextExcludeAll,
603 OSArray * personalityNames = NULL);
604 static OSReturn loadKextWithIdentifier(
605 OSString * kextIdentifier,
606 Boolean allowDeferFlag = true,
607 Boolean delayAutounloadFlag = false,
608 OSKextExcludeLevel startOpt = kOSKextExcludeNone,
609 OSKextExcludeLevel startMatchingOpt = kOSKextExcludeAll,
610 OSArray * personalityNames = NULL);
611 static OSReturn removeKextWithIdentifier(
612 const char * kextIdentifier,
613 bool terminateServicesAndRemovePersonalitiesFlag = false);
614 static OSReturn removeKextWithLoadTag(
615 OSKextLoadTag loadTag,
616 bool terminateServicesAndRemovePersonalitiesFlag = false);
617
618 static OSReturn requestResource(
619 const char * kextIdentifier,
620 const char * resourceName,
621 OSKextRequestResourceCallback callback,
622 void * context,
623 OSKextRequestTag * requestTagOut);
624 static OSReturn cancelRequest(
625 OSKextRequestTag requestTag,
626 void ** contextOut);
627
628 static void considerUnloads(Boolean rescheduleOnlyFlag = false);
629 static void flushNonloadedKexts(Boolean flushPrelinkedKexts);
630 static void setKextdActive(Boolean active = true);
631 static void setDeferredLoadSucceeded(Boolean succeeded = true);
632 static void considerRebuildOfPrelinkedKernel(void);
633 static void createExcludeListFromBooterData(
634 OSDictionary * theDictionary,
635 OSCollectionIterator * theIterator);
636 static void createExcludeListFromPrelinkInfo(OSArray * theInfoArray);
637 static boolean_t updateExcludeList(OSDictionary * infoDict);
638
639 static bool isWaitingKextd(void);
640
641 virtual bool setAutounloadEnabled(bool flag);
642
643 virtual const OSSymbol * getIdentifier(void);
644 virtual const char * getIdentifierCString(void);
645 virtual OSKextVersion getVersion(void);
646 virtual OSKextVersion getCompatibleVersion(void);
647 virtual bool isLibrary(void);
648 virtual bool isCompatibleWithVersion(OSKextVersion aVersion);
649 virtual OSObject * getPropertyForHostArch(const char * key);
650
651 virtual OSKextLoadTag getLoadTag(void);
652 virtual void getSizeInfo(uint32_t *loadSize, uint32_t *wiredSize);
653 virtual OSData * copyUUID(void);
654 OSData * copyTextUUID(void);
655 OSData * copyMachoUUID(const kernel_mach_header_t * header);
656 virtual OSArray * copyPersonalitiesArray(void);
657
658 /* This removes personalities naming the kext (by CFBundleIdentifier),
659 * not all personalities defined by the kext (IOPersonalityPublisher or CFBundleIdentifier).
660 */
661 virtual void removePersonalitiesFromCatalog(void);
662
663 /* Converts common string-valued properties to OSSymbols for lower memory consumption.
664 */
665 static void uniquePersonalityProperties(OSDictionary * personalityDict);
666
667 virtual bool declaresExecutable(void); // might be missing
668 virtual bool isInterface(void);
669 virtual bool isKernel(void);
670 virtual bool isKernelComponent(void);
671 virtual bool isExecutable(void);
672 virtual bool isLoadableInSafeBoot(void);
673 virtual bool isPrelinked(void);
674 virtual bool isLoaded(void);
675 virtual bool isStarted(void);
676 virtual bool isCPPInitialized(void);
677};
678
679
680#endif /* !_LIBKERN_OSKEXT_H */
681