void aml_append(Aml *parent_ctx, Aml *child) { GArray *buf = build_alloc_array(); build_append_array(buf, child->buf); switch (child->block_flags) { case AML_OPCODE: build_append_byte(parent_ctx->buf, child->op); break; case AML_EXT_PACKAGE: build_extop_package(buf, child->op); break; case AML_PACKAGE: build_package(buf, child->op); break; case AML_RES_TEMPLATE: build_append_byte(buf, 0x79); /* EndTag */ /* * checksum operations are treated as succeeded if checksum * field is zero. [ACPI Spec 1.0b, 6.4.2.8 End Tag] */ build_append_byte(buf, 0); /* fall through, to pack resources in buffer */ case AML_BUFFER: build_buffer(buf, child->op); break; case AML_NO_OPCODE: break; default: assert(0); break; } build_append_array(parent_ctx->buf, buf); build_free_array(buf); }
/* ACPI 1.0b: 16.2.6.3 Debug Objects Encoding: DebugObj */ Aml *aml_debug(void) { Aml *var = aml_alloc(); build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ build_append_byte(var->buf, 0x31); /* DebugOp */ return var; }
static void build_append_value(GArray *table, uint32_t value, int size) { uint8_t prefix; int i; switch (size) { case 1: prefix = 0x0A; /* BytePrefix */ break; case 2: prefix = 0x0B; /* WordPrefix */ break; case 4: prefix = 0x0C; /* DWordPrefix */ break; default: assert(0); return; } build_append_byte(table, prefix); for (i = 0; i < size; ++i) { build_append_byte(table, value & 0xFF); value = value >> 8; } }
/* * ACPI 5.0: 6.4.3.6 Extended Interrupt Descriptor * Type 1, Large Item Name 0x9 */ Aml *aml_interrupt(AmlConsumerAndProducer con_and_pro, AmlLevelAndEdge level_and_edge, AmlActiveHighAndLow high_and_low, AmlShared shared, uint32_t *irq_list, uint8_t irq_count) { int i; Aml *var = aml_alloc(); uint8_t irq_flags = con_and_pro | (level_and_edge << 1) | (high_and_low << 2) | (shared << 3); const int header_bytes_in_len = 2; uint16_t len = header_bytes_in_len + irq_count * sizeof(uint32_t); assert(irq_count > 0); build_append_byte(var->buf, 0x89); /* Extended irq descriptor */ build_append_byte(var->buf, len & 0xFF); /* Length, bits[7:0] */ build_append_byte(var->buf, len >> 8); /* Length, bits[15:8] */ build_append_byte(var->buf, irq_flags); /* Interrupt Vector Information. */ build_append_byte(var->buf, irq_count); /* Interrupt table length */ /* Interrupt Number List */ for (i = 0; i < irq_count; i++) { build_append_int_noprefix(var->buf, irq_list[i], 4); } return var; }
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefSleep */ Aml *aml_sleep(uint64_t msec) { Aml *var = aml_alloc(); build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ build_append_byte(var->buf, 0x22); /* SleepOp */ aml_append(var, aml_int(msec)); return var; }
/* ACPI 1.0b: 16.2.5.3 Type 1 Opcodes Encoding: DefRelease */ Aml *aml_release(Aml *mutex) { Aml *var = aml_alloc(); build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ build_append_byte(var->buf, 0x27); /* ReleaseOp */ aml_append(var, mutex); return var; }
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAcquire */ Aml *aml_acquire(Aml *mutex, uint16_t timeout) { Aml *var = aml_alloc(); build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ build_append_byte(var->buf, 0x23); /* AcquireOp */ aml_append(var, mutex); build_append_int_noprefix(var->buf, timeout, sizeof(timeout)); return var; }
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMutex */ Aml *aml_mutex(const char *name, uint8_t sync_level) { Aml *var = aml_alloc(); build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ build_append_byte(var->buf, 0x01); /* MutexOp */ build_append_namestring(var->buf, "%s", name); assert(!(sync_level & 0xF0)); build_append_byte(var->buf, sync_level); return var; }
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefCreateField */ Aml *aml_create_field(Aml *srcbuf, Aml *bit_index, Aml *num_bits, const char *name) { Aml *var = aml_alloc(); build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ build_append_byte(var->buf, 0x13); /* CreateFieldOp */ aml_append(var, srcbuf); aml_append(var, bit_index); aml_append(var, num_bits); build_append_namestring(var->buf, "%s", name); return var; }
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefOpRegion */ Aml *aml_operation_region(const char *name, AmlRegionSpace rs, Aml *offset, uint32_t len) { Aml *var = aml_alloc(); build_append_byte(var->buf, 0x5B); /* ExtOpPrefix */ build_append_byte(var->buf, 0x80); /* OpRegionOp */ build_append_namestring(var->buf, "%s", name); build_append_byte(var->buf, rs); aml_append(var, offset); build_append_int(var->buf, len); return var; }
/* ACPI 1.0b: 6.4.2.2 DMA Format/6.4.2.2.1 ASL Macro for DMA Descriptor */ Aml *aml_dma(AmlDmaType typ, AmlDmaBusMaster bm, AmlTransferSize sz, uint8_t channel) { Aml *var = aml_alloc(); uint8_t flags = sz | bm << 2 | typ << 5; assert(channel < 8); build_append_byte(var->buf, 0x2A); /* Byte 0: DMA Descriptor */ build_append_byte(var->buf, 1U << channel); /* Byte 1: _DMA - DmaChannel */ build_append_byte(var->buf, flags); /* Byte 2 */ return var; }
/* ACPI 1.0b: 6.4.3.5.5 Word Address Space Descriptor: bytes 3-5 */ static Aml *aml_as_desc_header(AmlResourceType type, AmlMinFixed min_fixed, AmlMaxFixed max_fixed, AmlDecode dec, uint8_t type_flags) { uint8_t flags = max_fixed | min_fixed | dec; Aml *var = aml_alloc(); build_append_byte(var->buf, type); build_append_byte(var->buf, flags); build_append_byte(var->buf, type_flags); /* Type Specific Flags */ return var; }
/* * ACPI 1.0b: 6.4.2.1.1 ASL Macro for IRQ Descriptor * * More verbose description at: * ACPI 5.0: 19.5.64 IRQNoFlags (Interrupt Resource Descriptor Macro) * 6.4.2.1 IRQ Descriptor */ Aml *aml_irq_no_flags(uint8_t irq) { uint16_t irq_mask; Aml *var = aml_alloc(); assert(irq < 16); build_append_byte(var->buf, 0x22); /* IRQ descriptor 2 byte form */ irq_mask = 1U << irq; build_append_byte(var->buf, irq_mask & 0xFF); /* IRQ mask bits[7:0] */ build_append_byte(var->buf, irq_mask >> 8); /* IRQ mask bits[15:8] */ return var; }
/* * ACPI 2.0b: 16.2.3.6.4.3 Unicode Macro (Convert Ascii String To Unicode) */ Aml *aml_unicode(const char *str) { int i = 0; Aml *var = aml_bundle(0x11 /* BufferOp */, AML_BUFFER); do { build_append_byte(var->buf, str[i]); build_append_byte(var->buf, 0); i++; } while (i <= strlen(str)); return var; }
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefProcessor */ Aml *aml_processor(uint8_t proc_id, uint32_t pblk_addr, uint8_t pblk_len, const char *name_format, ...) { va_list ap; Aml *var = aml_bundle(0x83 /* ProcessorOp */, AML_EXT_PACKAGE); va_start(ap, name_format); build_append_namestringv(var->buf, name_format, ap); va_end(ap); build_append_byte(var->buf, proc_id); /* ProcID */ build_append_int_noprefix(var->buf, pblk_addr, sizeof(pblk_addr)); build_append_byte(var->buf, pblk_len); /* PblkLen */ return var; }
static void build_append_int(GArray *table, uint32_t value) { if (value == 0x00) { build_append_byte(table, 0x00); /* ZeroOp */ } else if (value == 0x01) { build_append_byte(table, 0x01); /* OneOp */ } else if (value <= 0xFF) { build_append_value(table, value, 1); } else if (value <= 0xFFFFF) { build_append_value(table, value, 2); } else { build_append_value(table, value, 4); } }
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefBuffer * Pass byte_list as NULL to request uninitialized buffer to reserve space. */ Aml *aml_buffer(int buffer_size, uint8_t *byte_list) { int i; Aml *var = aml_bundle(0x11 /* BufferOp */, AML_BUFFER); for (i = 0; i < buffer_size; i++) { if (byte_list == NULL) { build_append_byte(var->buf, 0x0); } else { build_append_byte(var->buf, byte_list[i]); } } return var; }
/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToInteger */ Aml *aml_to_integer(Aml *arg) { Aml *var = aml_opcode(0x99 /* ToIntegerOp */); aml_append(var, arg); build_append_byte(var->buf, 0x00 /* NullNameOp */); return var; }
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefField */ Aml *aml_field(const char *name, AmlFieldFlags flags) { Aml *var = aml_bundle(0x81 /* FieldOp */, AML_EXT_PACKAGE); build_append_namestring(var->buf, "%s", name); build_append_byte(var->buf, flags); return var; }
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefMethod */ Aml *aml_method(const char *name, int arg_count) { Aml *var = aml_bundle(0x14 /* MethodOp */, AML_PACKAGE); build_append_namestring(var->buf, "%s", name); build_append_byte(var->buf, arg_count); /* MethodFlags: ArgCount */ return var; }
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: ReservedField */ Aml *aml_reserved_field(unsigned length) { Aml *var = aml_alloc(); /* ReservedField := 0x00 PkgLength */ build_append_byte(var->buf, 0x00); build_append_pkg_length(var->buf, length, false); return var; }
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefAnd */ Aml *aml_and(Aml *arg1, Aml *arg2) { Aml *var = aml_opcode(0x7B /* AndOp */); aml_append(var, arg1); aml_append(var, arg2); build_append_byte(var->buf, 0x00 /* NullNameOp */); return var; }
static GArray *build_alloc_method(const char *name, uint8_t arg_count) { GArray *method = build_alloc_array(); build_append_nameseg(method, "%s", name); build_append_byte(method, arg_count); /* MethodFlags: ArgCount */ return method; }
/* ACPI 1.0b: 16.2.5.4 Type 2 Opcodes Encoding: DefLGreaterEqual */ Aml *aml_lgreater_equal(Aml *arg1, Aml *arg2) { /* LGreaterEqualOp := LNotOp LLessOp */ Aml *var = aml_opcode(0x92 /* LNotOp */); build_append_byte(var->buf, 0x95 /* LLessOp */); aml_append(var, arg1); aml_append(var, arg2); return var; }
void build_append_int_noprefix(GArray *table, uint64_t value, int size) { int i; for (i = 0; i < size; ++i) { build_append_byte(table, value & 0xFF); value = value >> 8; } }
/* * Build NAME(XXXX, 0x00000000) where 0x00000000 is encoded as a dword, * and return the offset to 0x00000000 for runtime patching. * * Warning: runtime patching is best avoided. Only use this as * a replacement for DataTableRegion (for guests that don't * support it). */ int build_append_named_dword(GArray *array, const char *name_format, ...) { int offset; va_list ap; build_append_byte(array, 0x08); /* NameOp */ va_start(ap, name_format); build_append_namestringv(array, name_format, ap); va_end(ap); build_append_byte(array, 0x0C); /* DWordPrefix */ offset = array->len; build_append_int_noprefix(array, 0x00000000, 4); assert(array->len == offset + 4); return offset; }
/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToBuffer */ Aml *aml_to_buffer(Aml *src, Aml *dst) { Aml *var = aml_opcode(0x96 /* ToBufferOp */); aml_append(var, src); if (dst) { aml_append(var, dst); } else { build_append_byte(var->buf, 0x00 /* NullNameOp */); } return var; }
/* ACPI 2.0a: 17.2.4.4 Type 2 Opcodes Encoding: DefToHexString */ Aml *aml_to_hexstring(Aml *src, Aml *dst) { Aml *var = aml_opcode(0x98 /* ToHexStringOp */); aml_append(var, src); if (dst) { aml_append(var, dst); } else { build_append_byte(var->buf, 0x00 /* NullNameOp */); } return var; }
/* ACPI 1.0b: 16.2.5.2 Named Objects Encoding: DefField */ Aml *aml_field(const char *name, AmlAccessType type, AmlLockRule lock, AmlUpdateRule rule) { Aml *var = aml_bundle(0x81 /* FieldOp */, AML_EXT_PACKAGE); uint8_t flags = rule << 5 | type; flags |= lock << 4; /* LockRule at 4 bit offset */ build_append_namestring(var->buf, "%s", name); build_append_byte(var->buf, flags); return var; }
static void build_append_notify_target_ifequal(GArray *method, GArray *target_name, uint32_t value, int size) { GArray *notify = build_alloc_array(); uint8_t op = 0xA0; /* IfOp */ build_append_byte(notify, 0x93); /* LEqualOp */ build_append_byte(notify, 0x68); /* Arg0Op */ build_append_value(notify, value, size); build_append_byte(notify, 0x86); /* NotifyOp */ build_append_array(notify, target_name); build_append_byte(notify, 0x69); /* Arg1Op */ /* Pack it up */ build_package(notify, op, 1); build_append_array(method, notify); build_free_array(notify); }